pacemaker  2.0.1-57cc9c14bf
Scalable High-Availability cluster resource manager
strings.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
3  *
4  * This source code is licensed under the GNU Lesser General Public License
5  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
6  */
7 
8 #include <crm_internal.h>
9 
10 #ifndef _GNU_SOURCE
11 # define _GNU_SOURCE
12 #endif
13 
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <limits.h>
18 #include <bzlib.h>
19 #include <sys/types.h>
20 
21 char *
22 crm_itoa_stack(int an_int, char *buffer, size_t len)
23 {
24  if (buffer != NULL) {
25  snprintf(buffer, len, "%d", an_int);
26  }
27 
28  return buffer;
29 }
30 
31 long long
32 crm_int_helper(const char *text, char **end_text)
33 {
34  long long result = -1;
35  char *local_end_text = NULL;
36  int saved_errno = 0;
37 
38  errno = 0;
39 
40  if (text != NULL) {
41 #ifdef ANSI_ONLY
42  if (end_text != NULL) {
43  result = strtol(text, end_text, 10);
44  } else {
45  result = strtol(text, &local_end_text, 10);
46  }
47 #else
48  if (end_text != NULL) {
49  result = strtoll(text, end_text, 10);
50  } else {
51  result = strtoll(text, &local_end_text, 10);
52  }
53 #endif
54 
55  saved_errno = errno;
56  if (errno == EINVAL) {
57  crm_err("Conversion of %s failed", text);
58  result = -1;
59 
60  } else if (errno == ERANGE) {
61  crm_err("Conversion of %s was clipped: %lld", text, result);
62 
63  } else if (errno != 0) {
64  crm_perror(LOG_ERR, "Conversion of %s failed", text);
65  }
66 
67  if (local_end_text != NULL && local_end_text[0] != '\0') {
68  crm_err("Characters left over after parsing '%s': '%s'", text, local_end_text);
69  }
70 
71  errno = saved_errno;
72  }
73  return result;
74 }
75 
84 long long
85 crm_parse_ll(const char *text, const char *default_text)
86 {
87  if (text == NULL) {
88  text = default_text;
89  if (text == NULL) {
90  crm_err("No default conversion value supplied");
91  errno = EINVAL;
92  return -1;
93  }
94  }
95  return crm_int_helper(text, NULL);
96 }
97 
106 int
107 crm_parse_int(const char *text, const char *default_text)
108 {
109  long long result = crm_parse_ll(text, default_text);
110 
111  if ((result < INT_MIN) || (result > INT_MAX)) {
112  errno = ERANGE;
113  return -1;
114  }
115  return (int) result;
116 }
117 
126 guint
127 crm_parse_ms(const char *text)
128 {
129  if (text) {
130  long long ms = crm_int_helper(text, NULL);
131 
132  if ((ms < 0) || (ms > G_MAXUINT)) {
133  errno = ERANGE;
134  }
135  return errno? 0 : (guint) ms;
136  }
137  return 0;
138 }
139 
140 gboolean
141 safe_str_neq(const char *a, const char *b)
142 {
143  if (a == b) {
144  return FALSE;
145 
146  } else if (a == NULL || b == NULL) {
147  return TRUE;
148 
149  } else if (strcasecmp(a, b) == 0) {
150  return FALSE;
151  }
152  return TRUE;
153 }
154 
155 gboolean
156 crm_is_true(const char *s)
157 {
158  gboolean ret = FALSE;
159 
160  if (s != NULL) {
161  crm_str_to_boolean(s, &ret);
162  }
163  return ret;
164 }
165 
166 int
167 crm_str_to_boolean(const char *s, int *ret)
168 {
169  if (s == NULL) {
170  return -1;
171 
172  } else if (strcasecmp(s, "true") == 0
173  || strcasecmp(s, "on") == 0
174  || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) {
175  *ret = TRUE;
176  return 1;
177 
178  } else if (strcasecmp(s, "false") == 0
179  || strcasecmp(s, "off") == 0
180  || strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0) {
181  *ret = FALSE;
182  return 1;
183  }
184  return -1;
185 }
186 
187 char *
189 {
190  int len;
191 
192  if (str == NULL) {
193  return str;
194  }
195 
196  for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
197  str[len] = '\0';
198  }
199 
200  return str;
201 }
202 
203 gboolean
204 crm_str_eq(const char *a, const char *b, gboolean use_case)
205 {
206  if (use_case) {
207  return g_strcmp0(a, b) == 0;
208 
209  /* TODO - Figure out which calls, if any, really need to be case independent */
210  } else if (a == b) {
211  return TRUE;
212 
213  } else if (a == NULL || b == NULL) {
214  /* shouldn't be comparing NULLs */
215  return FALSE;
216 
217  } else if (strcasecmp(a, b) == 0) {
218  return TRUE;
219  }
220  return FALSE;
221 }
222 
223 static inline const char * null2emptystr(const char *);
224 static inline const char *
225 null2emptystr(const char *input)
226 {
227  return (input == NULL) ? "" : input;
228 }
229 
242 bool
243 crm_starts_with(const char *str, const char *prefix)
244 {
245  const char *s = str;
246  const char *p = prefix;
247 
248  if (!s || !p) {
249  return FALSE;
250  }
251  while (*s && *p) {
252  if (*s++ != *p++) {
253  return FALSE;
254  }
255  }
256  return (*p == 0);
257 }
258 
259 static inline int crm_ends_with_internal(const char *, const char *, gboolean);
260 static inline int
261 crm_ends_with_internal(const char *s, const char *match, gboolean as_extension)
262 {
263  if ((s == NULL) || (match == NULL)) {
264  return 0;
265  } else {
266  size_t slen, mlen;
267 
268  if (match[0] != '\0'
269  && (as_extension /* following commented out for inefficiency:
270  || strchr(&match[1], match[0]) == NULL */))
271  return !strcmp(null2emptystr(strrchr(s, match[0])), match);
272 
273  if ((mlen = strlen(match)) == 0)
274  return 1;
275  slen = strlen(s);
276  return ((slen >= mlen) && !strcmp(s + slen - mlen, match));
277  }
278 }
279 
292 gboolean
293 crm_ends_with(const char *s, const char *match)
294 {
295  return crm_ends_with_internal(s, match, FALSE);
296 }
297 
321 gboolean
322 crm_ends_with_ext(const char *s, const char *match)
323 {
324  return crm_ends_with_internal(s, match, TRUE);
325 }
326 
327 /*
328  * This re-implements g_str_hash as it was prior to glib2-2.28:
329  *
330  * http://git.gnome.org/browse/glib/commit/?id=354d655ba8a54b754cb5a3efb42767327775696c
331  *
332  * Note that the new g_str_hash is presumably a *better* hash (it's actually
333  * a correct implementation of DJB's hash), but we need to preserve existing
334  * behaviour, because the hash key ultimately determines the "sort" order
335  * when iterating through GHashTables, which affects allocation of scores to
336  * clone instances when iterating through rsc->allowed_nodes. It (somehow)
337  * also appears to have some minor impact on the ordering of a few
338  * pseudo_event IDs in the transition graph.
339  */
340 guint
341 g_str_hash_traditional(gconstpointer v)
342 {
343  const signed char *p;
344  guint32 h = 0;
345 
346  for (p = v; *p != '\0'; p++)
347  h = (h << 5) - h + *p;
348 
349  return h;
350 }
351 
352 /* used with hash tables where case does not matter */
353 gboolean
354 crm_strcase_equal(gconstpointer a, gconstpointer b)
355 {
356  return crm_str_eq((const char *) a, (const char *) b, FALSE);
357 }
358 
359 guint
360 crm_strcase_hash(gconstpointer v)
361 {
362  const signed char *p;
363  guint32 h = 0;
364 
365  for (p = v; *p != '\0'; p++)
366  h = (h << 5) - h + g_ascii_tolower(*p);
367 
368  return h;
369 }
370 
371 static void
372 copy_str_table_entry(gpointer key, gpointer value, gpointer user_data)
373 {
374  if (key && value && user_data) {
375  g_hash_table_insert((GHashTable*)user_data, strdup(key), strdup(value));
376  }
377 }
378 
379 GHashTable *
380 crm_str_table_dup(GHashTable *old_table)
381 {
382  GHashTable *new_table = NULL;
383 
384  if (old_table) {
385  new_table = crm_str_table_new();
386  g_hash_table_foreach(old_table, copy_str_table_entry, new_table);
387  }
388  return new_table;
389 }
390 
391 char *
392 add_list_element(char *list, const char *value)
393 {
394  int len = 0;
395  int last = 0;
396 
397  if (value == NULL) {
398  return list;
399  }
400  if (list) {
401  last = strlen(list);
402  }
403  len = last + 2; /* +1 space, +1 EOS */
404  len += strlen(value);
405  list = realloc_safe(list, len);
406  sprintf(list + last, " %s", value);
407  return list;
408 }
409 
410 bool
411 crm_compress_string(const char *data, int length, int max, char **result, unsigned int *result_len)
412 {
413  int rc;
414  char *compressed = NULL;
415  char *uncompressed = strdup(data);
416 #ifdef CLOCK_MONOTONIC
417  struct timespec after_t;
418  struct timespec before_t;
419 #endif
420 
421  if(max == 0) {
422  max = (length * 1.1) + 600; /* recommended size */
423  }
424 
425 #ifdef CLOCK_MONOTONIC
426  clock_gettime(CLOCK_MONOTONIC, &before_t);
427 #endif
428 
429  compressed = calloc(max, sizeof(char));
430  CRM_ASSERT(compressed);
431 
432  *result_len = max;
433  rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length, CRM_BZ2_BLOCKS, 0,
434  CRM_BZ2_WORK);
435 
436  free(uncompressed);
437 
438  if (rc != BZ_OK) {
439  crm_err("Compression of %d bytes failed: %s " CRM_XS " bzerror=%d",
440  length, bz2_strerror(rc), rc);
441  free(compressed);
442  return FALSE;
443  }
444 
445 #ifdef CLOCK_MONOTONIC
446  clock_gettime(CLOCK_MONOTONIC, &after_t);
447 
448  crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms",
449  length, *result_len, length / (*result_len),
450  difftime (after_t.tv_sec, before_t.tv_sec) * 1000 +
451  (after_t.tv_nsec - before_t.tv_nsec) / 1e6);
452 #else
453  crm_trace("Compressed %d bytes into %d (ratio %d:1)",
454  length, *result_len, length / (*result_len));
455 #endif
456 
457  *result = compressed;
458  return TRUE;
459 }
460 
472 gint
473 crm_alpha_sort(gconstpointer a, gconstpointer b)
474 {
475  if (!a && !b) {
476  return 0;
477  } else if (!a) {
478  return -1;
479  } else if (!b) {
480  return 1;
481  }
482  return strcasecmp(a, b);
483 }
484 
485 char *
486 crm_strdup_printf(char const *format, ...)
487 {
488  va_list ap;
489  int len = 0;
490  char *string = NULL;
491 
492  va_start(ap, format);
493  len = vasprintf (&string, format, ap);
494  CRM_ASSERT(len > 0);
495  va_end(ap);
496  return string;
497 }
const char * bz2_strerror(int rc)
Definition: results.c:425
char * crm_strdup_printf(char const *format,...)
Definition: strings.c:486
bool crm_compress_string(const char *data, int length, int max, char **result, unsigned int *result_len)
Definition: strings.c:411
long long crm_int_helper(const char *text, char **end_text)
Definition: strings.c:32
gboolean crm_ends_with(const char *s, const char *match)
Definition: strings.c:293
guint g_str_hash_traditional(gconstpointer v)
Definition: strings.c:341
guint crm_parse_ms(const char *text)
Definition: strings.c:127
#define CRM_BZ2_WORK
Definition: xml.h:43
#define crm_trace(fmt, args...)
Definition: logging.h:255
char * add_list_element(char *list, const char *value)
Definition: strings.c:392
gboolean crm_ends_with_ext(const char *s, const char *match)
Definition: strings.c:322
gint crm_alpha_sort(gconstpointer a, gconstpointer b)
Compare two strings alphabetically (case-insensitive)
Definition: strings.c:473
char * crm_itoa_stack(int an_int, char *buffer, size_t len)
Definition: strings.c:22
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:167
#define CRM_XS
Definition: logging.h:43
char * crm_strip_trailing_newline(char *str)
Definition: strings.c:188
gboolean crm_is_true(const char *s)
Definition: strings.c:156
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:204
int crm_parse_int(const char *text, const char *default_text)
Parse an integer value from a string.
Definition: strings.c:107
long long crm_parse_ll(const char *text, const char *default_text)
Parse a long long integer value from a string.
Definition: strings.c:85
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:227
#define crm_err(fmt, args...)
Definition: logging.h:249
#define CRM_ASSERT(expr)
Definition: results.h:20
bool crm_starts_with(const char *str, const char *prefix)
Check whether a string starts with a certain sequence.
Definition: strings.c:243
char data[0]
Definition: internal.h:90
gboolean crm_strcase_equal(gconstpointer a, gconstpointer b)
Definition: strings.c:354
gboolean safe_str_neq(const char *a, const char *b)
Definition: strings.c:141
guint crm_strcase_hash(gconstpointer v)
Definition: strings.c:360
#define CRM_BZ2_BLOCKS
Definition: xml.h:42
GHashTable * crm_str_table_dup(GHashTable *old_table)
Definition: strings.c:380