pacemaker  2.1.9-49aab99839
Scalable High-Availability cluster resource manager
strings.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2024 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 <regex.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include <float.h> // DBL_MIN
22 #include <limits.h>
23 #include <bzlib.h>
24 #include <sys/types.h>
25 
41 static int
42 scan_ll(const char *text, long long *result, long long default_value,
43  char **end_text)
44 {
45  long long local_result = default_value;
46  char *local_end_text = NULL;
47  int rc = pcmk_rc_ok;
48 
49  errno = 0;
50  if (text != NULL) {
51  local_result = strtoll(text, &local_end_text, 10);
52  if (errno == ERANGE) {
53  rc = EOVERFLOW;
54  crm_debug("Integer parsed from '%s' was clipped to %lld",
55  text, local_result);
56 
57  } else if (local_end_text == text) {
58  rc = EINVAL;
59  local_result = default_value;
60  crm_debug("Could not parse integer from '%s' (using %lld instead): "
61  "No digits found", text, default_value);
62 
63  } else if (errno != 0) {
64  rc = errno;
65  local_result = default_value;
66  crm_debug("Could not parse integer from '%s' (using %lld instead): "
67  "%s", text, default_value, pcmk_rc_str(rc));
68  }
69 
70  if ((end_text == NULL) && !pcmk__str_empty(local_end_text)) {
71  crm_debug("Characters left over after parsing '%s': '%s'",
72  text, local_end_text);
73  }
74  errno = rc;
75  }
76  if (end_text != NULL) {
77  *end_text = local_end_text;
78  }
79  if (result != NULL) {
80  *result = local_result;
81  }
82  return rc;
83 }
84 
95 int
96 pcmk__scan_ll(const char *text, long long *result, long long default_value)
97 {
98  long long local_result = default_value;
99  int rc = pcmk_rc_ok;
100 
101  if (text != NULL) {
102  rc = scan_ll(text, &local_result, default_value, NULL);
103  if (rc != pcmk_rc_ok) {
104  local_result = default_value;
105  }
106  }
107  if (result != NULL) {
108  *result = local_result;
109  }
110  return rc;
111 }
112 
125 int
126 pcmk__scan_min_int(const char *text, int *result, int minimum)
127 {
128  int rc;
129  long long result_ll;
130 
131  rc = pcmk__scan_ll(text, &result_ll, (long long) minimum);
132 
133  if (result_ll < (long long) minimum) {
134  crm_warn("Clipped '%s' to minimum acceptable value %d", text, minimum);
135  result_ll = (long long) minimum;
136 
137  } else if (result_ll > INT_MAX) {
138  crm_warn("Clipped '%s' to maximum integer %d", text, INT_MAX);
139  result_ll = (long long) INT_MAX;
140  rc = EOVERFLOW;
141  }
142 
143  if (result != NULL) {
144  *result = (int) result_ll;
145  }
146  return rc;
147 }
148 
159 int
160 pcmk__scan_port(const char *text, int *port)
161 {
162  long long port_ll;
163  int rc = pcmk__scan_ll(text, &port_ll, -1LL);
164 
165  if (rc != pcmk_rc_ok) {
166  crm_warn("'%s' is not a valid port: %s", text, pcmk_rc_str(rc));
167 
168  } else if ((text != NULL) // wasn't default or invalid
169  && ((port_ll < 0LL) || (port_ll > 65535LL))) {
170  crm_warn("Ignoring port specification '%s' "
171  "not in valid range (0-65535)", text);
172  rc = (port_ll < 0LL)? pcmk_rc_before_range : pcmk_rc_after_range;
173  port_ll = -1LL;
174  }
175  if (port != NULL) {
176  *port = (int) port_ll;
177  }
178  return rc;
179 }
180 
200 int
201 pcmk__scan_double(const char *text, double *result, const char *default_text,
202  char **end_text)
203 {
204  int rc = pcmk_rc_ok;
205  char *local_end_text = NULL;
206 
207  pcmk__assert(result != NULL);
209 
210  text = (text != NULL) ? text : default_text;
211 
212  if (text == NULL) {
213  rc = EINVAL;
214  crm_debug("No text and no default conversion value supplied");
215 
216  } else {
217  errno = 0;
218  *result = strtod(text, &local_end_text);
219 
220  if (errno == ERANGE) {
221  /*
222  * Overflow: strtod() returns +/- HUGE_VAL and sets errno to
223  * ERANGE
224  *
225  * Underflow: strtod() returns "a value whose magnitude is
226  * no greater than the smallest normalized
227  * positive" double. Whether ERANGE is set is
228  * implementation-defined.
229  */
230  const char *over_under;
231 
232  if (QB_ABS(*result) > DBL_MIN) {
233  rc = EOVERFLOW;
234  over_under = "over";
235  } else {
236  rc = pcmk_rc_underflow;
237  over_under = "under";
238  }
239 
240  crm_debug("Floating-point value parsed from '%s' would %sflow "
241  "(using %g instead)", text, over_under, *result);
242 
243  } else if (errno != 0) {
244  rc = errno;
245  // strtod() set *result = 0 on parse failure
247 
248  crm_debug("Could not parse floating-point value from '%s' (using "
249  "%.1f instead): %s", text, PCMK__PARSE_DBL_DEFAULT,
250  pcmk_rc_str(rc));
251 
252  } else if (local_end_text == text) {
253  // errno == 0, but nothing was parsed
254  rc = EINVAL;
256 
257  crm_debug("Could not parse floating-point value from '%s' (using "
258  "%.1f instead): No digits found", text,
260 
261  } else if (QB_ABS(*result) <= DBL_MIN) {
262  /*
263  * errno == 0 and text was parsed, but value might have
264  * underflowed.
265  *
266  * ERANGE might not be set for underflow. Check magnitude
267  * of *result, but also make sure the input number is not
268  * actually zero (0 <= DBL_MIN is not underflow).
269  *
270  * This check must come last. A parse failure in strtod()
271  * also sets *result == 0, so a parse failure would match
272  * this test condition prematurely.
273  */
274  for (const char *p = text; p != local_end_text; p++) {
275  if (strchr("0.eE", *p) == NULL) {
276  rc = pcmk_rc_underflow;
277  crm_debug("Floating-point value parsed from '%s' would "
278  "underflow (using %g instead)", text, *result);
279  break;
280  }
281  }
282 
283  } else {
284  crm_trace("Floating-point value parsed successfully from "
285  "'%s': %g", text, *result);
286  }
287 
288  if ((end_text == NULL) && !pcmk__str_empty(local_end_text)) {
289  crm_debug("Characters left over after parsing '%s': '%s'",
290  text, local_end_text);
291  }
292  }
293 
294  if (end_text != NULL) {
295  *end_text = local_end_text;
296  }
297 
298  return rc;
299 }
300 
312 int
313 pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val,
314  guint *result)
315 {
316  const char *value;
317  long long value_ll;
318  int rc = pcmk_rc_ok;
319 
320  CRM_CHECK((table != NULL) && (key != NULL), return EINVAL);
321 
322  if (result != NULL) {
323  *result = default_val;
324  }
325 
326  value = g_hash_table_lookup(table, key);
327  if (value == NULL) {
328  return pcmk_rc_ok;
329  }
330 
331  rc = pcmk__scan_ll(value, &value_ll, 0LL);
332  if (rc != pcmk_rc_ok) {
333  crm_warn("Using default (%u) for %s because '%s' is not a "
334  "valid integer: %s", default_val, key, value, pcmk_rc_str(rc));
335  return rc;
336  }
337 
338  if ((value_ll < 0) || (value_ll > G_MAXUINT)) {
339  crm_warn("Using default (%u) for %s because '%s' is not in valid range",
340  default_val, key, value);
341  return ERANGE;
342  }
343 
344  if (result != NULL) {
345  *result = (guint) value_ll;
346  }
347  return pcmk_rc_ok;
348 }
349 
360 long long
361 crm_get_msec(const char *input)
362 {
363  char *units = NULL; // Do not free; will point to part of input
364  long long multiplier = 1000;
365  long long divisor = 1;
366  long long msec = PCMK__PARSE_INT_DEFAULT;
367  int rc = pcmk_rc_ok;
368 
369  if (input == NULL) {
371  }
372 
373  // Skip initial whitespace
374  while (isspace(*input)) {
375  input++;
376  }
377 
378  rc = scan_ll(input, &msec, PCMK__PARSE_INT_DEFAULT, &units);
379 
380  if ((rc == EOVERFLOW) && (msec > 0)) {
381  crm_warn("'%s' will be clipped to %lld", input, msec);
382 
383  } else if ((rc != pcmk_rc_ok) || (msec < 0)) {
384  crm_warn("'%s' is not a valid time duration: %s",
385  input, ((rc == pcmk_rc_ok)? "Negative" : pcmk_rc_str(rc)));
387  }
388 
389  /* If the number is a decimal, scan_ll() reads only the integer part. Skip
390  * any remaining digits or decimal characters.
391  *
392  * @COMPAT Well-formed and malformed decimals are both accepted inputs. For
393  * example, "3.14 ms" and "3.1.4 ms" are treated the same as "3ms" and
394  * parsed successfully. At a compatibility break, decide if this is still
395  * desired.
396  */
397  while (isdigit(*units) || (*units == '.')) {
398  units++;
399  }
400 
401  // Skip any additional whitespace after the number
402  while (isspace(*units)) {
403  units++;
404  }
405 
406  /* @COMPAT Use exact comparisons. Currently, we match too liberally, and the
407  * second strncasecmp() in each case is redundant.
408  */
409  if ((*units == '\0')
410  || (strncasecmp(units, "s", 1) == 0)
411  || (strncasecmp(units, "sec", 3) == 0)) {
412  multiplier = 1000;
413  divisor = 1;
414 
415  } else if ((strncasecmp(units, "ms", 2) == 0)
416  || (strncasecmp(units, "msec", 4) == 0)) {
417  multiplier = 1;
418  divisor = 1;
419 
420  } else if ((strncasecmp(units, "us", 2) == 0)
421  || (strncasecmp(units, "usec", 4) == 0)) {
422  multiplier = 1;
423  divisor = 1000;
424 
425  } else if ((strncasecmp(units, "m", 1) == 0)
426  || (strncasecmp(units, "min", 3) == 0)) {
427  multiplier = 60 * 1000;
428  divisor = 1;
429 
430  } else if ((strncasecmp(units, "h", 1) == 0)
431  || (strncasecmp(units, "hr", 2) == 0)) {
432  multiplier = 60 * 60 * 1000;
433  divisor = 1;
434 
435  } else {
436  // Invalid units
438  }
439 
440  // Apply units, capping at LLONG_MAX
441  if (msec > (LLONG_MAX / multiplier)) {
442  return LLONG_MAX;
443  }
444  return (msec * multiplier) / divisor;
445 }
446 
461 int
462 pcmk_parse_interval_spec(const char *input, guint *result_ms)
463 {
464  long long msec = PCMK__PARSE_INT_DEFAULT;
465  int rc = pcmk_rc_ok;
466 
467  if (input == NULL) {
468  msec = 0;
469  goto done;
470  }
471 
472  if (input[0] == 'P') {
474 
475  if (period_s != NULL) {
476  msec = crm_time_get_seconds(period_s);
477  msec = QB_MIN(msec, G_MAXUINT / 1000) * 1000;
478  crm_time_free(period_s);
479  }
480 
481  } else {
482  msec = crm_get_msec(input);
483  }
484 
485  if (msec < 0) {
486  crm_warn("Using 0 instead of invalid interval specification '%s'",
487  input);
488  msec = 0;
489  rc = EINVAL;
490  }
491 
492 done:
493  if (result_ms != NULL) {
494  *result_ms = (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
495  }
496  return rc;
497 }
498 
499 gboolean
500 crm_is_true(const char *s)
501 {
502  gboolean ret = FALSE;
503 
504  return (crm_str_to_boolean(s, &ret) < 0)? FALSE : ret;
505 }
506 
507 int
508 crm_str_to_boolean(const char *s, int *ret)
509 {
510  if (s == NULL) {
511  return -1;
512  }
513 
514  if (pcmk__strcase_any_of(s, PCMK_VALUE_TRUE, "on", "yes", "y", "1", NULL)) {
515  if (ret != NULL) {
516  *ret = TRUE;
517  }
518  return 1;
519  }
520 
521  if (pcmk__strcase_any_of(s, PCMK_VALUE_FALSE, "off", "no", "n", "0",
522  NULL)) {
523  if (ret != NULL) {
524  *ret = FALSE;
525  }
526  return 1;
527  }
528  return -1;
529 }
530 
539 char *
540 pcmk__trim(char *str)
541 {
542  int len;
543 
544  if (str == NULL) {
545  return str;
546  }
547 
548  for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
549  str[len] = '\0';
550  }
551 
552  return str;
553 }
554 
567 bool
568 pcmk__starts_with(const char *str, const char *prefix)
569 {
570  const char *s = str;
571  const char *p = prefix;
572 
573  if (!s || !p) {
574  return false;
575  }
576  while (*s && *p) {
577  if (*s++ != *p++) {
578  return false;
579  }
580  }
581  return (*p == 0);
582 }
583 
584 static inline bool
585 ends_with(const char *s, const char *match, bool as_extension)
586 {
587  if (pcmk__str_empty(match)) {
588  return true;
589  } else if (s == NULL) {
590  return false;
591  } else {
592  size_t slen, mlen;
593 
594  /* Besides as_extension, we could also check
595  !strchr(&match[1], match[0]) but that would be inefficient.
596  */
597  if (as_extension) {
598  s = strrchr(s, match[0]);
599  return (s == NULL)? false : !strcmp(s, match);
600  }
601 
602  mlen = strlen(match);
603  slen = strlen(s);
604  return ((slen >= mlen) && !strcmp(s + slen - mlen, match));
605  }
606 }
607 
619 bool
620 pcmk__ends_with(const char *s, const char *match)
621 {
622  return ends_with(s, match, false);
623 }
624 
646 bool
647 pcmk__ends_with_ext(const char *s, const char *match)
648 {
649  return ends_with(s, match, true);
650 }
651 
671 static guint
672 pcmk__str_hash(gconstpointer v)
673 {
674  const signed char *p;
675  guint32 h = 0;
676 
677  for (p = v; *p != '\0'; p++)
678  h = (h << 5) - h + *p;
679 
680  return h;
681 }
682 
694 GHashTable *
695 pcmk__strkey_table(GDestroyNotify key_destroy_func,
696  GDestroyNotify value_destroy_func)
697 {
698  return g_hash_table_new_full(pcmk__str_hash, g_str_equal,
699  key_destroy_func, value_destroy_func);
700 }
701 
712 void
713 pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
714 {
715  pcmk__assert((table != NULL) && (name != NULL));
716 
717  g_hash_table_insert(table, pcmk__str_copy(name), pcmk__str_copy(value));
718 }
719 
720 /* used with hash tables where case does not matter */
721 static gboolean
722 pcmk__strcase_equal(gconstpointer a, gconstpointer b)
723 {
724  return pcmk__str_eq((const char *)a, (const char *)b, pcmk__str_casei);
725 }
726 
727 static guint
728 pcmk__strcase_hash(gconstpointer v)
729 {
730  const signed char *p;
731  guint32 h = 0;
732 
733  for (p = v; *p != '\0'; p++)
734  h = (h << 5) - h + g_ascii_tolower(*p);
735 
736  return h;
737 }
738 
750 GHashTable *
751 pcmk__strikey_table(GDestroyNotify key_destroy_func,
752  GDestroyNotify value_destroy_func)
753 {
754  return g_hash_table_new_full(pcmk__strcase_hash, pcmk__strcase_equal,
755  key_destroy_func, value_destroy_func);
756 }
757 
758 static void
759 copy_str_table_entry(gpointer key, gpointer value, gpointer user_data)
760 {
761  if (key && value && user_data) {
762  pcmk__insert_dup((GHashTable *) user_data,
763  (const char *) key, (const char *) value);
764  }
765 }
766 
777 GHashTable *
778 pcmk__str_table_dup(GHashTable *old_table)
779 {
780  GHashTable *new_table = NULL;
781 
782  if (old_table) {
783  new_table = pcmk__strkey_table(free, free);
784  g_hash_table_foreach(old_table, copy_str_table_entry, new_table);
785  }
786  return new_table;
787 }
788 
805 void
806 pcmk__add_separated_word(GString **list, size_t init_size, const char *word,
807  const char *separator)
808 {
809  pcmk__assert(list != NULL);
810 
811  if (pcmk__str_empty(word)) {
812  return;
813  }
814 
815  if (*list == NULL) {
816  if (init_size > 0) {
817  *list = g_string_sized_new(init_size);
818  } else {
819  *list = g_string_new(NULL);
820  }
821  }
822 
823  if ((*list)->len == 0) {
824  // Don't add a separator before the first word in the list
825  separator = "";
826 
827  } else if (separator == NULL) {
828  // Default to space-separated
829  separator = " ";
830  }
831 
832  g_string_append(*list, separator);
833  g_string_append(*list, word);
834 }
835 
848 int
849 pcmk__compress(const char *data, unsigned int length, unsigned int max,
850  char **result, unsigned int *result_len)
851 {
852  int rc;
853  char *compressed = NULL;
854  char *uncompressed = strdup(data);
855 #ifdef CLOCK_MONOTONIC
856  struct timespec after_t;
857  struct timespec before_t;
858 #endif
859 
860  if (max == 0) {
861  max = (length * 1.01) + 601; // Size guaranteed to hold result
862  }
863 
864 #ifdef CLOCK_MONOTONIC
865  clock_gettime(CLOCK_MONOTONIC, &before_t);
866 #endif
867 
868  compressed = pcmk__assert_alloc((size_t) max, sizeof(char));
869 
870  *result_len = max;
871  rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length,
873  rc = pcmk__bzlib2rc(rc);
874 
875  free(uncompressed);
876 
877  if (rc != pcmk_rc_ok) {
878  crm_err("Compression of %d bytes failed: %s " CRM_XS " rc=%d",
879  length, pcmk_rc_str(rc), rc);
880  free(compressed);
881  return rc;
882  }
883 
884 #ifdef CLOCK_MONOTONIC
885  clock_gettime(CLOCK_MONOTONIC, &after_t);
886 
887  crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms",
888  length, *result_len, length / (*result_len),
889  (after_t.tv_sec - before_t.tv_sec) * 1000 +
890  (after_t.tv_nsec - before_t.tv_nsec) / 1e6);
891 #else
892  crm_trace("Compressed %d bytes into %d (ratio %d:1)",
893  length, *result_len, length / (*result_len));
894 #endif
895 
896  *result = compressed;
897  return pcmk_rc_ok;
898 }
899 
900 char *
901 crm_strdup_printf(char const *format, ...)
902 {
903  va_list ap;
904  int len = 0;
905  char *string = NULL;
906 
907  va_start(ap, format);
908  len = vasprintf(&string, format, ap);
909  pcmk__assert(len > 0);
910  va_end(ap);
911  return string;
912 }
913 
914 int
915 pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
916 {
917  char *remainder = NULL;
918  int rc = pcmk_rc_ok;
919 
920  pcmk__assert((start != NULL) && (end != NULL));
921 
922  *start = PCMK__PARSE_INT_DEFAULT;
924 
925  crm_trace("Attempting to decode: [%s]", srcstring);
926  if (pcmk__str_eq(srcstring, "", pcmk__str_null_matches)) {
927  return ENODATA;
928  } else if (pcmk__str_eq(srcstring, "-", pcmk__str_none)) {
929  return pcmk_rc_bad_input;
930  }
931 
932  /* String starts with a dash, so this is either a range with
933  * no beginning or garbage.
934  * */
935  if (*srcstring == '-') {
936  int rc = scan_ll(srcstring+1, end, PCMK__PARSE_INT_DEFAULT, &remainder);
937 
938  if (rc != pcmk_rc_ok || *remainder != '\0') {
939  return pcmk_rc_bad_input;
940  } else {
941  return pcmk_rc_ok;
942  }
943  }
944 
945  rc = scan_ll(srcstring, start, PCMK__PARSE_INT_DEFAULT, &remainder);
946  if (rc != pcmk_rc_ok) {
947  return rc;
948  }
949 
950  if (*remainder && *remainder == '-') {
951  if (*(remainder+1)) {
952  char *more_remainder = NULL;
953  int rc = scan_ll(remainder+1, end, PCMK__PARSE_INT_DEFAULT,
954  &more_remainder);
955 
956  if (rc != pcmk_rc_ok) {
957  return rc;
958  } else if (*more_remainder != '\0') {
959  return pcmk_rc_bad_input;
960  }
961  }
962  } else if (*remainder && *remainder != '-') {
963  *start = PCMK__PARSE_INT_DEFAULT;
964  return pcmk_rc_bad_input;
965  } else {
966  /* The input string contained only one number. Set start and end
967  * to the same value and return pcmk_rc_ok. This gives the caller
968  * a way to tell this condition apart from a range with no end.
969  */
970  *end = *start;
971  }
972 
973  return pcmk_rc_ok;
974 }
975 
992 gboolean
993 pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
994 {
995  for (const GList *ele = lst; ele != NULL; ele = ele->next) {
996  if (pcmk__str_eq(s, ele->data, flags)) {
997  return TRUE;
998  }
999  }
1000 
1001  return FALSE;
1002 }
1003 
1004 static bool
1005 str_any_of(const char *s, va_list args, uint32_t flags)
1006 {
1007  if (s == NULL) {
1009  }
1010 
1011  while (1) {
1012  const char *ele = va_arg(args, const char *);
1013 
1014  if (ele == NULL) {
1015  break;
1016  } else if (pcmk__str_eq(s, ele, flags)) {
1017  return true;
1018  }
1019  }
1020 
1021  return false;
1022 }
1023 
1037 bool
1038 pcmk__strcase_any_of(const char *s, ...)
1039 {
1040  va_list ap;
1041  bool rc;
1042 
1043  va_start(ap, s);
1044  rc = str_any_of(s, ap, pcmk__str_casei);
1045  va_end(ap);
1046  return rc;
1047 }
1048 
1061 bool
1062 pcmk__str_any_of(const char *s, ...)
1063 {
1064  va_list ap;
1065  bool rc;
1066 
1067  va_start(ap, s);
1068  rc = str_any_of(s, ap, pcmk__str_none);
1069  va_end(ap);
1070  return rc;
1071 }
1072 
1089 int
1090 pcmk__numeric_strcasecmp(const char *s1, const char *s2)
1091 {
1092  pcmk__assert((s1 != NULL) && (s2 != NULL));
1093 
1094  while (*s1 && *s2) {
1095  if (isdigit(*s1) && isdigit(*s2)) {
1096  // If node names contain a number, sort numerically
1097 
1098  char *end1 = NULL;
1099  char *end2 = NULL;
1100  long num1 = strtol(s1, &end1, 10);
1101  long num2 = strtol(s2, &end2, 10);
1102 
1103  // allow ordering e.g. 007 > 7
1104  size_t len1 = end1 - s1;
1105  size_t len2 = end2 - s2;
1106 
1107  if (num1 < num2) {
1108  return -1;
1109  } else if (num1 > num2) {
1110  return 1;
1111  } else if (len1 < len2) {
1112  return -1;
1113  } else if (len1 > len2) {
1114  return 1;
1115  }
1116  s1 = end1;
1117  s2 = end2;
1118  } else {
1119  // Compare non-digits case-insensitively
1120  int lower1 = tolower(*s1);
1121  int lower2 = tolower(*s2);
1122 
1123  if (lower1 < lower2) {
1124  return -1;
1125  } else if (lower1 > lower2) {
1126  return 1;
1127  }
1128  ++s1;
1129  ++s2;
1130  }
1131  }
1132  if (!*s1 && *s2) {
1133  return -1;
1134  } else if (*s1 && !*s2) {
1135  return 1;
1136  }
1137  return 0;
1138 }
1139 
1174 int
1175 pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
1176 {
1177  /* If this flag is set, the second string is a regex. */
1179  regex_t r_patt;
1180  int reg_flags = REG_EXTENDED | REG_NOSUB;
1181  int regcomp_rc = 0;
1182  int rc = 0;
1183 
1184  if (s1 == NULL || s2 == NULL) {
1185  return 1;
1186  }
1187 
1189  reg_flags |= REG_ICASE;
1190  }
1191  regcomp_rc = regcomp(&r_patt, s2, reg_flags);
1192  if (regcomp_rc != 0) {
1193  rc = 1;
1194  crm_err("Bad regex '%s' for update: %s", s2, strerror(regcomp_rc));
1195  } else {
1196  rc = regexec(&r_patt, s1, 0, NULL, 0);
1197  regfree(&r_patt);
1198  if (rc != 0) {
1199  rc = 1;
1200  }
1201  }
1202  return rc;
1203  }
1204 
1205  /* If the strings are the same pointer, return 0 immediately. */
1206  if (s1 == s2) {
1207  return 0;
1208  }
1209 
1210  /* If this flag is set, return 0 if either (or both) of the input strings
1211  * are NULL. If neither one is NULL, we need to continue and compare
1212  * them normally.
1213  */
1215  if (s1 == NULL || s2 == NULL) {
1216  return 0;
1217  }
1218  }
1219 
1220  /* Handle the cases where one is NULL and the str_null_matches flag is not set.
1221  * A NULL string always sorts to the beginning.
1222  */
1223  if (s1 == NULL) {
1224  return -1;
1225  } else if (s2 == NULL) {
1226  return 1;
1227  }
1228 
1229  /* If this flag is set, return 0 if either (or both) of the input strings
1230  * are "*". If neither one is, we need to continue and compare them
1231  * normally.
1232  */
1234  if (strcmp(s1, "*") == 0 || strcmp(s2, "*") == 0) {
1235  return 0;
1236  }
1237  }
1238 
1240  return strcasecmp(s1, s2);
1241  } else {
1242  return strcmp(s1, s2);
1243  }
1244 }
1245 
1259 char *
1260 pcmk__str_copy_as(const char *file, const char *function, uint32_t line,
1261  const char *str)
1262 {
1263  if (str != NULL) {
1264  char *result = strdup(str);
1265 
1266  if (result == NULL) {
1267  crm_abort(file, function, line, "Out of memory", FALSE, TRUE);
1269  }
1270  return result;
1271  }
1272  return NULL;
1273 }
1274 
1288 void
1289 pcmk__str_update(char **str, const char *value)
1290 {
1291  if ((str != NULL) && !pcmk__str_eq(*str, value, pcmk__str_none)) {
1292  free(*str);
1293  *str = pcmk__str_copy(value);
1294  }
1295 }
1296 
1307 void
1308 pcmk__g_strcat(GString *buffer, ...)
1309 {
1310  va_list ap;
1311 
1312  pcmk__assert(buffer != NULL);
1313  va_start(ap, buffer);
1314 
1315  while (true) {
1316  const char *ele = va_arg(ap, const char *);
1317 
1318  if (ele == NULL) {
1319  break;
1320  }
1321  g_string_append(buffer, ele);
1322  }
1323  va_end(ap);
1324 }
1325 
1326 // Deprecated functions kept only for backward API compatibility
1327 // LCOV_EXCL_START
1328 
1329 #include <crm/common/util_compat.h>
1330 
1331 gboolean
1332 safe_str_neq(const char *a, const char *b)
1333 {
1334  if (a == b) {
1335  return FALSE;
1336 
1337  } else if (a == NULL || b == NULL) {
1338  return TRUE;
1339 
1340  } else if (strcasecmp(a, b) == 0) {
1341  return FALSE;
1342  }
1343  return TRUE;
1344 }
1345 
1346 gboolean
1347 crm_str_eq(const char *a, const char *b, gboolean use_case)
1348 {
1349  if (use_case) {
1350  return g_strcmp0(a, b) == 0;
1351 
1352  /* TODO - Figure out which calls, if any, really need to be case independent */
1353  } else if (a == b) {
1354  return TRUE;
1355 
1356  } else if (a == NULL || b == NULL) {
1357  /* shouldn't be comparing NULLs */
1358  return FALSE;
1359 
1360  } else if (strcasecmp(a, b) == 0) {
1361  return TRUE;
1362  }
1363  return FALSE;
1364 }
1365 
1366 char *
1367 crm_itoa_stack(int an_int, char *buffer, size_t len)
1368 {
1369  if (buffer != NULL) {
1370  snprintf(buffer, len, "%d", an_int);
1371  }
1372  return buffer;
1373 }
1374 
1375 guint
1376 g_str_hash_traditional(gconstpointer v)
1377 {
1378  return pcmk__str_hash(v);
1379 }
1380 
1381 gboolean
1382 crm_strcase_equal(gconstpointer a, gconstpointer b)
1383 {
1384  return pcmk__strcase_equal(a, b);
1385 }
1386 
1387 guint
1388 crm_strcase_hash(gconstpointer v)
1389 {
1390  return pcmk__strcase_hash(v);
1391 }
1392 
1393 GHashTable *
1394 crm_str_table_dup(GHashTable *old_table)
1395 {
1396  return pcmk__str_table_dup(old_table);
1397 }
1398 
1399 long long
1400 crm_parse_ll(const char *text, const char *default_text)
1401 {
1402  long long result;
1403 
1404  if (text == NULL) {
1405  text = default_text;
1406  if (text == NULL) {
1407  crm_err("No default conversion value supplied");
1408  errno = EINVAL;
1409  return PCMK__PARSE_INT_DEFAULT;
1410  }
1411  }
1412  (void) scan_ll(text, &result, PCMK__PARSE_INT_DEFAULT, NULL);
1413  return result;
1414 }
1415 
1416 int
1417 crm_parse_int(const char *text, const char *default_text)
1418 {
1419  long long result = crm_parse_ll(text, default_text);
1420 
1421  if (result < INT_MIN) {
1422  // If errno is ERANGE, crm_parse_ll() has already logged a message
1423  if (errno != ERANGE) {
1424  crm_err("Conversion of %s was clipped: %lld", text, result);
1425  errno = ERANGE;
1426  }
1427  return INT_MIN;
1428 
1429  } else if (result > INT_MAX) {
1430  // If errno is ERANGE, crm_parse_ll() has already logged a message
1431  if (errno != ERANGE) {
1432  crm_err("Conversion of %s was clipped: %lld", text, result);
1433  errno = ERANGE;
1434  }
1435  return INT_MAX;
1436  }
1437 
1438  return (int) result;
1439 }
1440 
1441 char *
1443 {
1444  return pcmk__trim(str);
1445 }
1446 
1447 int
1448 pcmk_numeric_strcasecmp(const char *s1, const char *s2)
1449 {
1450  return pcmk__numeric_strcasecmp(s1, s2);
1451 }
1452 
1453 // LCOV_EXCL_STOP
1454 // End deprecated API
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
bool pcmk__starts_with(const char *str, const char *prefix)
Check whether a string starts with a certain sequence.
Definition: strings.c:568
char data[0]
Definition: cpg.c:58
char * crm_strdup_printf(char const *format,...)
Definition: strings.c:901
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:938
#define PCMK_VALUE_FALSE
Definition: options.h:152
int pcmk_numeric_strcasecmp(const char *s1, const char *s2)
Definition: strings.c:1448
const char * name
Definition: cib.c:26
struct crm_time_s crm_time_t
Definition: iso8601.h:32
guint g_str_hash_traditional(gconstpointer v)
Definition: strings.c:1376
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
Definition: results.c:1199
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:695
#define PCMK__PARSE_DBL_DEFAULT
int pcmk__scan_port(const char *text, int *port)
Definition: strings.c:160
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition: strings.c:96
int pcmk__numeric_strcasecmp(const char *s1, const char *s2)
Definition: strings.c:1090
bool pcmk__str_any_of(const char *s,...)
Definition: strings.c:1062
crm_time_t * crm_time_parse_duration(const char *duration_str)
Parse a time duration from an ISO 8601 duration specification.
Definition: iso8601.c:1121
int pcmk__scan_double(const char *text, double *result, const char *default_text, char **end_text)
Definition: strings.c:201
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:503
void pcmk__str_update(char **str, const char *value)
Definition: strings.c:1289
#define PCMK__PARSE_INT_DEFAULT
#define crm_warn(fmt, args...)
Definition: logging.h:394
int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, guint *result)
Definition: strings.c:313
#define crm_debug(fmt, args...)
Definition: logging.h:402
void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator)
Definition: strings.c:806
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition: strings.c:915
External (OS/environmental) problem.
Definition: results.h:272
int pcmk__compress(const char *data, unsigned int length, unsigned int max, char **result, unsigned int *result_len)
Definition: strings.c:849
#define crm_trace(fmt, args...)
Definition: logging.h:404
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:94
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition: strings.c:713
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition: strings.c:361
#define PCMK_VALUE_TRUE
Definition: options.h:215
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:126
GHashTable * pcmk__str_table_dup(GHashTable *old_table)
Definition: strings.c:778
int pcmk_parse_interval_spec(const char *input, guint *result_ms)
Parse milliseconds from a Pacemaker interval specification.
Definition: strings.c:462
long long crm_time_get_seconds(const crm_time_t *dt)
Definition: iso8601.c:331
char * crm_itoa_stack(int an_int, char *buffer, size_t len)
Definition: strings.c:1367
#define pcmk__str_copy(str)
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:508
#define pcmk__assert(expr)
#define CRM_XS
Definition: logging.h:56
char * crm_strip_trailing_newline(char *str)
Definition: strings.c:1442
int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
Definition: strings.c:1175
GHashTable * pcmk__strikey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:751
#define CRM_BZ2_BLOCKS
Definition: xml_io.h:33
gboolean crm_is_true(const char *s)
Definition: strings.c:500
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition: strings.c:647
int pcmk__bzlib2rc(int bz2)
Map a bz2 return code to the most similar Pacemaker return code.
Definition: results.c:908
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:1347
int crm_parse_int(const char *text, const char *default_text)
Definition: strings.c:1417
#define ENODATA
Definition: portability.h:106
long long crm_parse_ll(const char *text, const char *default_text)
Definition: strings.c:1400
pcmk__action_result_t result
Definition: pcmk_fence.c:35
#define crm_err(fmt, args...)
Definition: logging.h:391
Deprecated Pacemaker utilities.
xmlNode * input
gboolean crm_strcase_equal(gconstpointer a, gconstpointer b)
Definition: strings.c:1382
void pcmk__g_strcat(GString *buffer,...)
Definition: strings.c:1308
char * pcmk__str_copy_as(const char *file, const char *function, uint32_t line, const char *str)
Definition: strings.c:1260
gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
Definition: strings.c:993
gboolean safe_str_neq(const char *a, const char *b)
Definition: strings.c:1332
guint crm_strcase_hash(gconstpointer v)
Definition: strings.c:1388
GHashTable * crm_str_table_dup(GHashTable *old_table)
Definition: strings.c:1394
char * pcmk__trim(char *str)
Definition: strings.c:540
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:297
#define CRM_BZ2_WORK
Definition: xml_io.h:34
bool pcmk__ends_with(const char *s, const char *match)
Definition: strings.c:620
uint64_t flags
Definition: remote.c:215
bool pcmk__strcase_any_of(const char *s,...)
Definition: strings.c:1038
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:150