pacemaker  2.0.3-4b1f869f0f
Scalable High-Availability cluster resource manager
strings.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2019 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 
12 #ifndef _GNU_SOURCE
13 # define _GNU_SOURCE
14 #endif
15 
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <limits.h>
20 #include <bzlib.h>
21 #include <sys/types.h>
22 
23 char *
24 crm_itoa_stack(int an_int, char *buffer, size_t len)
25 {
26  if (buffer != NULL) {
27  snprintf(buffer, len, "%d", an_int);
28  }
29 
30  return buffer;
31 }
32 
33 long long
34 crm_int_helper(const char *text, char **end_text)
35 {
36  long long result = -1;
37  char *local_end_text = NULL;
38  int saved_errno = 0;
39 
40  errno = 0;
41 
42  if (text != NULL) {
43 #ifdef ANSI_ONLY
44  if (end_text != NULL) {
45  result = strtol(text, end_text, 10);
46  } else {
47  result = strtol(text, &local_end_text, 10);
48  }
49 #else
50  if (end_text != NULL) {
51  result = strtoll(text, end_text, 10);
52  } else {
53  result = strtoll(text, &local_end_text, 10);
54  }
55 #endif
56 
57  saved_errno = errno;
58  if (errno == EINVAL) {
59  crm_err("Conversion of %s failed", text);
60  result = -1;
61 
62  } else if (errno == ERANGE) {
63  crm_err("Conversion of %s was clipped: %lld", text, result);
64 
65  } else if (errno != 0) {
66  crm_perror(LOG_ERR, "Conversion of %s failed", text);
67 
68  } else if (local_end_text == text) {
69  crm_err("Text contained no digits: %s", text);
70  result = -1;
71  }
72 
73  if (local_end_text != NULL && local_end_text[0] != '\0') {
74  crm_err("Characters left over after parsing '%s': '%s'", text, local_end_text);
75  }
76 
77  errno = saved_errno;
78  }
79  return result;
80 }
81 
90 long long
91 crm_parse_ll(const char *text, const char *default_text)
92 {
93  if (text == NULL) {
94  text = default_text;
95  if (text == NULL) {
96  crm_err("No default conversion value supplied");
97  errno = EINVAL;
98  return -1;
99  }
100  }
101  return crm_int_helper(text, NULL);
102 }
103 
113 int
114 crm_parse_int(const char *text, const char *default_text)
115 {
116  long long result = crm_parse_ll(text, default_text);
117 
118  if (result < INT_MIN) {
119  // If errno is ERANGE, crm_parse_ll() has already logged a message
120  if (errno != ERANGE) {
121  crm_err("Conversion of %s was clipped: %lld", text, result);
122  errno = ERANGE;
123  }
124  return INT_MIN;
125 
126  } else if (result > INT_MAX) {
127  // If errno is ERANGE, crm_parse_ll() has already logged a message
128  if (errno != ERANGE) {
129  crm_err("Conversion of %s was clipped: %lld", text, result);
130  errno = ERANGE;
131  }
132  return INT_MAX;
133  }
134 
135  return (int) result;
136 }
137 
146 guint
147 crm_parse_ms(const char *text)
148 {
149  if (text) {
150  long long ms = crm_int_helper(text, NULL);
151 
152  if ((ms < 0) || (ms > G_MAXUINT)) {
153  errno = ERANGE;
154  }
155  return errno? 0 : (guint) ms;
156  }
157  return 0;
158 }
159 
160 gboolean
161 safe_str_neq(const char *a, const char *b)
162 {
163  if (a == b) {
164  return FALSE;
165 
166  } else if (a == NULL || b == NULL) {
167  return TRUE;
168 
169  } else if (strcasecmp(a, b) == 0) {
170  return FALSE;
171  }
172  return TRUE;
173 }
174 
175 gboolean
176 crm_is_true(const char *s)
177 {
178  gboolean ret = FALSE;
179 
180  if (s != NULL) {
181  crm_str_to_boolean(s, &ret);
182  }
183  return ret;
184 }
185 
186 int
187 crm_str_to_boolean(const char *s, int *ret)
188 {
189  if (s == NULL) {
190  return -1;
191 
192  } else if (strcasecmp(s, "true") == 0
193  || strcasecmp(s, "on") == 0
194  || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) {
195  *ret = TRUE;
196  return 1;
197 
198  } else if (strcasecmp(s, "false") == 0
199  || strcasecmp(s, "off") == 0
200  || strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0) {
201  *ret = FALSE;
202  return 1;
203  }
204  return -1;
205 }
206 
207 char *
209 {
210  int len;
211 
212  if (str == NULL) {
213  return str;
214  }
215 
216  for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
217  str[len] = '\0';
218  }
219 
220  return str;
221 }
222 
223 gboolean
224 crm_str_eq(const char *a, const char *b, gboolean use_case)
225 {
226  if (use_case) {
227  return g_strcmp0(a, b) == 0;
228 
229  /* TODO - Figure out which calls, if any, really need to be case independent */
230  } else if (a == b) {
231  return TRUE;
232 
233  } else if (a == NULL || b == NULL) {
234  /* shouldn't be comparing NULLs */
235  return FALSE;
236 
237  } else if (strcasecmp(a, b) == 0) {
238  return TRUE;
239  }
240  return FALSE;
241 }
242 
243 static inline const char * null2emptystr(const char *);
244 static inline const char *
245 null2emptystr(const char *input)
246 {
247  return (input == NULL) ? "" : input;
248 }
249 
262 bool
263 crm_starts_with(const char *str, const char *prefix)
264 {
265  const char *s = str;
266  const char *p = prefix;
267 
268  if (!s || !p) {
269  return FALSE;
270  }
271  while (*s && *p) {
272  if (*s++ != *p++) {
273  return FALSE;
274  }
275  }
276  return (*p == 0);
277 }
278 
279 static inline int crm_ends_with_internal(const char *, const char *, gboolean);
280 static inline int
281 crm_ends_with_internal(const char *s, const char *match, gboolean as_extension)
282 {
283  if ((s == NULL) || (match == NULL)) {
284  return 0;
285  } else {
286  size_t slen, mlen;
287 
288  if (match[0] != '\0'
289  && (as_extension /* following commented out for inefficiency:
290  || strchr(&match[1], match[0]) == NULL */))
291  return !strcmp(null2emptystr(strrchr(s, match[0])), match);
292 
293  if ((mlen = strlen(match)) == 0)
294  return 1;
295  slen = strlen(s);
296  return ((slen >= mlen) && !strcmp(s + slen - mlen, match));
297  }
298 }
299 
312 gboolean
313 crm_ends_with(const char *s, const char *match)
314 {
315  return crm_ends_with_internal(s, match, FALSE);
316 }
317 
341 gboolean
342 crm_ends_with_ext(const char *s, const char *match)
343 {
344  return crm_ends_with_internal(s, match, TRUE);
345 }
346 
347 /*
348  * This re-implements g_str_hash as it was prior to glib2-2.28:
349  *
350  * https://gitlab.gnome.org/GNOME/glib/commit/354d655ba8a54b754cb5a3efb42767327775696c
351  *
352  * Note that the new g_str_hash is presumably a *better* hash (it's actually
353  * a correct implementation of DJB's hash), but we need to preserve existing
354  * behaviour, because the hash key ultimately determines the "sort" order
355  * when iterating through GHashTables, which affects allocation of scores to
356  * clone instances when iterating through rsc->allowed_nodes. It (somehow)
357  * also appears to have some minor impact on the ordering of a few
358  * pseudo_event IDs in the transition graph.
359  */
360 guint
361 g_str_hash_traditional(gconstpointer v)
362 {
363  const signed char *p;
364  guint32 h = 0;
365 
366  for (p = v; *p != '\0'; p++)
367  h = (h << 5) - h + *p;
368 
369  return h;
370 }
371 
372 /* used with hash tables where case does not matter */
373 gboolean
374 crm_strcase_equal(gconstpointer a, gconstpointer b)
375 {
376  return crm_str_eq((const char *) a, (const char *) b, FALSE);
377 }
378 
379 guint
380 crm_strcase_hash(gconstpointer v)
381 {
382  const signed char *p;
383  guint32 h = 0;
384 
385  for (p = v; *p != '\0'; p++)
386  h = (h << 5) - h + g_ascii_tolower(*p);
387 
388  return h;
389 }
390 
391 static void
392 copy_str_table_entry(gpointer key, gpointer value, gpointer user_data)
393 {
394  if (key && value && user_data) {
395  g_hash_table_insert((GHashTable*)user_data, strdup(key), strdup(value));
396  }
397 }
398 
399 GHashTable *
400 crm_str_table_dup(GHashTable *old_table)
401 {
402  GHashTable *new_table = NULL;
403 
404  if (old_table) {
405  new_table = crm_str_table_new();
406  g_hash_table_foreach(old_table, copy_str_table_entry, new_table);
407  }
408  return new_table;
409 }
410 
411 char *
412 add_list_element(char *list, const char *value)
413 {
414  int len = 0;
415  int last = 0;
416 
417  if (value == NULL) {
418  return list;
419  }
420  if (list) {
421  last = strlen(list);
422  }
423  len = last + 2; /* +1 space, +1 EOS */
424  len += strlen(value);
425  list = realloc_safe(list, len);
426  sprintf(list + last, " %s", value);
427  return list;
428 }
429 
430 bool
431 crm_compress_string(const char *data, int length, int max, char **result, unsigned int *result_len)
432 {
433  int rc;
434  char *compressed = NULL;
435  char *uncompressed = strdup(data);
436 #ifdef CLOCK_MONOTONIC
437  struct timespec after_t;
438  struct timespec before_t;
439 #endif
440 
441  if(max == 0) {
442  max = (length * 1.1) + 600; /* recommended size */
443  }
444 
445 #ifdef CLOCK_MONOTONIC
446  clock_gettime(CLOCK_MONOTONIC, &before_t);
447 #endif
448 
449  compressed = calloc(max, sizeof(char));
450  CRM_ASSERT(compressed);
451 
452  *result_len = max;
453  rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length, CRM_BZ2_BLOCKS, 0,
454  CRM_BZ2_WORK);
455 
456  free(uncompressed);
457 
458  if (rc != BZ_OK) {
459  crm_err("Compression of %d bytes failed: %s " CRM_XS " bzerror=%d",
460  length, bz2_strerror(rc), rc);
461  free(compressed);
462  return FALSE;
463  }
464 
465 #ifdef CLOCK_MONOTONIC
466  clock_gettime(CLOCK_MONOTONIC, &after_t);
467 
468  crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms",
469  length, *result_len, length / (*result_len),
470  (after_t.tv_sec - before_t.tv_sec) * 1000 +
471  (after_t.tv_nsec - before_t.tv_nsec) / 1e6);
472 #else
473  crm_trace("Compressed %d bytes into %d (ratio %d:1)",
474  length, *result_len, length / (*result_len));
475 #endif
476 
477  *result = compressed;
478  return TRUE;
479 }
480 
492 gint
493 crm_alpha_sort(gconstpointer a, gconstpointer b)
494 {
495  if (!a && !b) {
496  return 0;
497  } else if (!a) {
498  return -1;
499  } else if (!b) {
500  return 1;
501  }
502  return strcasecmp(a, b);
503 }
504 
505 char *
506 crm_strdup_printf(char const *format, ...)
507 {
508  va_list ap;
509  int len = 0;
510  char *string = NULL;
511 
512  va_start(ap, format);
513  len = vasprintf (&string, format, ap);
514  CRM_ASSERT(len > 0);
515  va_end(ap);
516  return string;
517 }
CRM_BZ2_WORK
#define CRM_BZ2_WORK
Definition: xml.h:46
crm_str_table_dup
GHashTable * crm_str_table_dup(GHashTable *old_table)
Definition: strings.c:400
CRM_BZ2_BLOCKS
#define CRM_BZ2_BLOCKS
Definition: xml.h:45
data
char data[0]
Definition: internal.h:12
crm_strdup_printf
char * crm_strdup_printf(char const *format,...)
Definition: strings.c:506
crm_parse_ms
guint crm_parse_ms(const char *text)
Definition: strings.c:147
g_str_hash_traditional
guint g_str_hash_traditional(gconstpointer v)
Definition: strings.c:361
crm_err
#define crm_err(fmt, args...)
Definition: logging.h:241
crm_ends_with
gboolean crm_ends_with(const char *s, const char *match)
Definition: strings.c:313
crm_trace
#define crm_trace(fmt, args...)
Definition: logging.h:247
CRM_XS
#define CRM_XS
Definition: logging.h:34
crm_alpha_sort
gint crm_alpha_sort(gconstpointer a, gconstpointer b)
Compare two strings alphabetically (case-insensitive)
Definition: strings.c:493
crm_parse_int
int crm_parse_int(const char *text, const char *default_text)
Parse an integer value from a string.
Definition: strings.c:114
crm_ends_with_ext
gboolean crm_ends_with_ext(const char *s, const char *match)
Definition: strings.c:342
add_list_element
char * add_list_element(char *list, const char *value)
Definition: strings.c:412
crm_perror
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:219
crm_strcase_equal
gboolean crm_strcase_equal(gconstpointer a, gconstpointer b)
Definition: strings.c:374
crm_strcase_hash
guint crm_strcase_hash(gconstpointer v)
Definition: strings.c:380
crm_parse_ll
long long crm_parse_ll(const char *text, const char *default_text)
Parse a long long integer value from a string.
Definition: strings.c:91
crm_strip_trailing_newline
char * crm_strip_trailing_newline(char *str)
Definition: strings.c:208
crm_str_eq
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:224
crm_itoa_stack
char * crm_itoa_stack(int an_int, char *buffer, size_t len)
Definition: strings.c:24
crm_str_to_boolean
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:187
CRM_ASSERT
#define CRM_ASSERT(expr)
Definition: results.h:42
crm_is_true
gboolean crm_is_true(const char *s)
Definition: strings.c:176
crm_starts_with
bool crm_starts_with(const char *str, const char *prefix)
Check whether a string starts with a certain sequence.
Definition: strings.c:263
crm_internal.h
crm_compress_string
bool crm_compress_string(const char *data, int length, int max, char **result, unsigned int *result_len)
Definition: strings.c:431
crm_int_helper
long long crm_int_helper(const char *text, char **end_text)
Definition: strings.c:34
bz2_strerror
const char * bz2_strerror(int rc)
Definition: results.c:445
safe_str_neq
gboolean safe_str_neq(const char *a, const char *b)
Definition: strings.c:161