pacemaker  2.0.2-debe490
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 
69  if (local_end_text != NULL && local_end_text[0] != '\0') {
70  crm_err("Characters left over after parsing '%s': '%s'", text, local_end_text);
71  }
72 
73  errno = saved_errno;
74  }
75  return result;
76 }
77 
86 long long
87 crm_parse_ll(const char *text, const char *default_text)
88 {
89  if (text == NULL) {
90  text = default_text;
91  if (text == NULL) {
92  crm_err("No default conversion value supplied");
93  errno = EINVAL;
94  return -1;
95  }
96  }
97  return crm_int_helper(text, NULL);
98 }
99 
109 int
110 crm_parse_int(const char *text, const char *default_text)
111 {
112  long long result = crm_parse_ll(text, default_text);
113 
114  if (result < INT_MIN) {
115  // If errno is ERANGE, crm_parse_ll() has already logged a message
116  if (errno != ERANGE) {
117  crm_err("Conversion of %s was clipped: %lld", text, result);
118  errno = ERANGE;
119  }
120  return INT_MIN;
121 
122  } else if (result > INT_MAX) {
123  // If errno is ERANGE, crm_parse_ll() has already logged a message
124  if (errno != ERANGE) {
125  crm_err("Conversion of %s was clipped: %lld", text, result);
126  errno = ERANGE;
127  }
128  return INT_MAX;
129  }
130 
131  return (int) result;
132 }
133 
142 guint
143 crm_parse_ms(const char *text)
144 {
145  if (text) {
146  long long ms = crm_int_helper(text, NULL);
147 
148  if ((ms < 0) || (ms > G_MAXUINT)) {
149  errno = ERANGE;
150  }
151  return errno? 0 : (guint) ms;
152  }
153  return 0;
154 }
155 
156 gboolean
157 safe_str_neq(const char *a, const char *b)
158 {
159  if (a == b) {
160  return FALSE;
161 
162  } else if (a == NULL || b == NULL) {
163  return TRUE;
164 
165  } else if (strcasecmp(a, b) == 0) {
166  return FALSE;
167  }
168  return TRUE;
169 }
170 
171 gboolean
172 crm_is_true(const char *s)
173 {
174  gboolean ret = FALSE;
175 
176  if (s != NULL) {
177  crm_str_to_boolean(s, &ret);
178  }
179  return ret;
180 }
181 
182 int
183 crm_str_to_boolean(const char *s, int *ret)
184 {
185  if (s == NULL) {
186  return -1;
187 
188  } else if (strcasecmp(s, "true") == 0
189  || strcasecmp(s, "on") == 0
190  || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) {
191  *ret = TRUE;
192  return 1;
193 
194  } else if (strcasecmp(s, "false") == 0
195  || strcasecmp(s, "off") == 0
196  || strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0) {
197  *ret = FALSE;
198  return 1;
199  }
200  return -1;
201 }
202 
203 char *
205 {
206  int len;
207 
208  if (str == NULL) {
209  return str;
210  }
211 
212  for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
213  str[len] = '\0';
214  }
215 
216  return str;
217 }
218 
219 gboolean
220 crm_str_eq(const char *a, const char *b, gboolean use_case)
221 {
222  if (use_case) {
223  return g_strcmp0(a, b) == 0;
224 
225  /* TODO - Figure out which calls, if any, really need to be case independent */
226  } else if (a == b) {
227  return TRUE;
228 
229  } else if (a == NULL || b == NULL) {
230  /* shouldn't be comparing NULLs */
231  return FALSE;
232 
233  } else if (strcasecmp(a, b) == 0) {
234  return TRUE;
235  }
236  return FALSE;
237 }
238 
239 static inline const char * null2emptystr(const char *);
240 static inline const char *
241 null2emptystr(const char *input)
242 {
243  return (input == NULL) ? "" : input;
244 }
245 
258 bool
259 crm_starts_with(const char *str, const char *prefix)
260 {
261  const char *s = str;
262  const char *p = prefix;
263 
264  if (!s || !p) {
265  return FALSE;
266  }
267  while (*s && *p) {
268  if (*s++ != *p++) {
269  return FALSE;
270  }
271  }
272  return (*p == 0);
273 }
274 
275 static inline int crm_ends_with_internal(const char *, const char *, gboolean);
276 static inline int
277 crm_ends_with_internal(const char *s, const char *match, gboolean as_extension)
278 {
279  if ((s == NULL) || (match == NULL)) {
280  return 0;
281  } else {
282  size_t slen, mlen;
283 
284  if (match[0] != '\0'
285  && (as_extension /* following commented out for inefficiency:
286  || strchr(&match[1], match[0]) == NULL */))
287  return !strcmp(null2emptystr(strrchr(s, match[0])), match);
288 
289  if ((mlen = strlen(match)) == 0)
290  return 1;
291  slen = strlen(s);
292  return ((slen >= mlen) && !strcmp(s + slen - mlen, match));
293  }
294 }
295 
308 gboolean
309 crm_ends_with(const char *s, const char *match)
310 {
311  return crm_ends_with_internal(s, match, FALSE);
312 }
313 
337 gboolean
338 crm_ends_with_ext(const char *s, const char *match)
339 {
340  return crm_ends_with_internal(s, match, TRUE);
341 }
342 
343 /*
344  * This re-implements g_str_hash as it was prior to glib2-2.28:
345  *
346  * https://gitlab.gnome.org/GNOME/glib/commit/354d655ba8a54b754cb5a3efb42767327775696c
347  *
348  * Note that the new g_str_hash is presumably a *better* hash (it's actually
349  * a correct implementation of DJB's hash), but we need to preserve existing
350  * behaviour, because the hash key ultimately determines the "sort" order
351  * when iterating through GHashTables, which affects allocation of scores to
352  * clone instances when iterating through rsc->allowed_nodes. It (somehow)
353  * also appears to have some minor impact on the ordering of a few
354  * pseudo_event IDs in the transition graph.
355  */
356 guint
357 g_str_hash_traditional(gconstpointer v)
358 {
359  const signed char *p;
360  guint32 h = 0;
361 
362  for (p = v; *p != '\0'; p++)
363  h = (h << 5) - h + *p;
364 
365  return h;
366 }
367 
368 /* used with hash tables where case does not matter */
369 gboolean
370 crm_strcase_equal(gconstpointer a, gconstpointer b)
371 {
372  return crm_str_eq((const char *) a, (const char *) b, FALSE);
373 }
374 
375 guint
376 crm_strcase_hash(gconstpointer v)
377 {
378  const signed char *p;
379  guint32 h = 0;
380 
381  for (p = v; *p != '\0'; p++)
382  h = (h << 5) - h + g_ascii_tolower(*p);
383 
384  return h;
385 }
386 
387 static void
388 copy_str_table_entry(gpointer key, gpointer value, gpointer user_data)
389 {
390  if (key && value && user_data) {
391  g_hash_table_insert((GHashTable*)user_data, strdup(key), strdup(value));
392  }
393 }
394 
395 GHashTable *
396 crm_str_table_dup(GHashTable *old_table)
397 {
398  GHashTable *new_table = NULL;
399 
400  if (old_table) {
401  new_table = crm_str_table_new();
402  g_hash_table_foreach(old_table, copy_str_table_entry, new_table);
403  }
404  return new_table;
405 }
406 
407 char *
408 add_list_element(char *list, const char *value)
409 {
410  int len = 0;
411  int last = 0;
412 
413  if (value == NULL) {
414  return list;
415  }
416  if (list) {
417  last = strlen(list);
418  }
419  len = last + 2; /* +1 space, +1 EOS */
420  len += strlen(value);
421  list = realloc_safe(list, len);
422  sprintf(list + last, " %s", value);
423  return list;
424 }
425 
426 bool
427 crm_compress_string(const char *data, int length, int max, char **result, unsigned int *result_len)
428 {
429  int rc;
430  char *compressed = NULL;
431  char *uncompressed = strdup(data);
432 #ifdef CLOCK_MONOTONIC
433  struct timespec after_t;
434  struct timespec before_t;
435 #endif
436 
437  if(max == 0) {
438  max = (length * 1.1) + 600; /* recommended size */
439  }
440 
441 #ifdef CLOCK_MONOTONIC
442  clock_gettime(CLOCK_MONOTONIC, &before_t);
443 #endif
444 
445  compressed = calloc(max, sizeof(char));
446  CRM_ASSERT(compressed);
447 
448  *result_len = max;
449  rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length, CRM_BZ2_BLOCKS, 0,
450  CRM_BZ2_WORK);
451 
452  free(uncompressed);
453 
454  if (rc != BZ_OK) {
455  crm_err("Compression of %d bytes failed: %s " CRM_XS " bzerror=%d",
456  length, bz2_strerror(rc), rc);
457  free(compressed);
458  return FALSE;
459  }
460 
461 #ifdef CLOCK_MONOTONIC
462  clock_gettime(CLOCK_MONOTONIC, &after_t);
463 
464  crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms",
465  length, *result_len, length / (*result_len),
466  difftime (after_t.tv_sec, before_t.tv_sec) * 1000 +
467  (after_t.tv_nsec - before_t.tv_nsec) / 1e6);
468 #else
469  crm_trace("Compressed %d bytes into %d (ratio %d:1)",
470  length, *result_len, length / (*result_len));
471 #endif
472 
473  *result = compressed;
474  return TRUE;
475 }
476 
488 gint
489 crm_alpha_sort(gconstpointer a, gconstpointer b)
490 {
491  if (!a && !b) {
492  return 0;
493  } else if (!a) {
494  return -1;
495  } else if (!b) {
496  return 1;
497  }
498  return strcasecmp(a, b);
499 }
500 
501 char *
502 crm_strdup_printf(char const *format, ...)
503 {
504  va_list ap;
505  int len = 0;
506  char *string = NULL;
507 
508  va_start(ap, format);
509  len = vasprintf (&string, format, ap);
510  CRM_ASSERT(len > 0);
511  va_end(ap);
512  return string;
513 }
const char * bz2_strerror(int rc)
Definition: results.c:443
gboolean safe_str_neq(const char *a, const char *b)
Definition: strings.c:157
guint g_str_hash_traditional(gconstpointer v)
Definition: strings.c:357
long long crm_int_helper(const char *text, char **end_text)
Definition: strings.c:34
int crm_parse_int(const char *text, const char *default_text)
Parse an integer value from a string.
Definition: strings.c:110
bool crm_starts_with(const char *str, const char *prefix)
Check whether a string starts with a certain sequence.
Definition: strings.c:259
#define CRM_BZ2_WORK
Definition: xml.h:46
char * add_list_element(char *list, const char *value)
Definition: strings.c:408
gint crm_alpha_sort(gconstpointer a, gconstpointer b)
Compare two strings alphabetically (case-insensitive)
Definition: strings.c:489
#define crm_trace(fmt, args...)
Definition: logging.h:246
guint crm_strcase_hash(gconstpointer v)
Definition: strings.c:376
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:220
char * crm_itoa_stack(int an_int, char *buf, size_t len)
Definition: strings.c:24
gboolean crm_ends_with_ext(const char *s, const char *match)
Definition: strings.c:338
gboolean crm_ends_with(const char *s, const char *match)
Definition: strings.c:309
#define CRM_XS
Definition: logging.h:34
bool crm_compress_string(const char *data, int length, int max, char **result, unsigned int *result_len)
Definition: strings.c:427
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:183
guint crm_parse_ms(const char *text)
Definition: strings.c:143
long long crm_parse_ll(const char *text, const char *default_text)
Parse a long long integer value from a string.
Definition: strings.c:87
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:218
#define crm_err(fmt, args...)
Definition: logging.h:240
#define CRM_ASSERT(expr)
Definition: results.h:42
GHashTable * crm_str_table_dup(GHashTable *old_table)
Definition: strings.c:396
char data[0]
Definition: internal.h:92
gboolean crm_is_true(const char *s)
Definition: strings.c:172
#define CRM_BZ2_BLOCKS
Definition: xml.h:45
char * crm_strip_trailing_newline(char *str)
Definition: strings.c:204
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
gboolean crm_strcase_equal(gconstpointer a, gconstpointer b)
Definition: strings.c:370