pacemaker  1.1.15-e174ec8
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
utils.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include <crm_internal.h>
20 #include <dlfcn.h>
21 
22 #ifndef _GNU_SOURCE
23 # define _GNU_SOURCE
24 #endif
25 
26 #include <sys/param.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <sys/stat.h>
30 #include <sys/utsname.h>
31 
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <limits.h>
37 #include <ctype.h>
38 #include <pwd.h>
39 #include <time.h>
40 #include <libgen.h>
41 #include <signal.h>
42 
43 #include <qb/qbdefs.h>
44 
45 #include <crm/crm.h>
46 #include <crm/lrmd.h>
47 #include <crm/services.h>
48 #include <crm/msg_xml.h>
49 #include <crm/cib/internal.h>
50 #include <crm/common/xml.h>
51 #include <crm/common/util.h>
52 #include <crm/common/ipc.h>
53 #include <crm/common/iso8601.h>
54 #include <crm/common/mainloop.h>
55 #include <crm/attrd.h>
56 #include <libxml2/libxml/relaxng.h>
57 
58 #ifndef MAXLINE
59 # define MAXLINE 512
60 #endif
61 
62 #ifdef HAVE_GETOPT_H
63 # include <getopt.h>
64 #endif
65 
66 #ifndef PW_BUFFER_LEN
67 # define PW_BUFFER_LEN 500
68 #endif
69 
70 CRM_TRACE_INIT_DATA(common);
71 
72 gboolean crm_config_error = FALSE;
73 gboolean crm_config_warning = FALSE;
74 char *crm_system_name = NULL;
75 
80 
81 static struct crm_option *crm_long_options = NULL;
82 static const char *crm_app_description = NULL;
83 static char *crm_short_options = NULL;
84 static const char *crm_app_usage = NULL;
85 
86 int
87 crm_exit(int rc)
88 {
90 
91 #if HAVE_LIBXML2
92  crm_trace("cleaning up libxml");
94 #endif
95 
96  crm_trace("exit %d", rc);
97  qb_log_fini();
98 
99  free(crm_short_options);
100  free(crm_system_name);
101 
102  exit(ABS(rc)); /* Always exit with a positive value so that it can be passed to crm_error
103  *
104  * Otherwise the system wraps it around and people
105  * have to jump through hoops figuring out what the
106  * error was
107  */
108  return rc; /* Can never happen, but allows return crm_exit(rc)
109  * where "return rc" was used previously - which
110  * keeps compilers happy.
111  */
112 }
113 
114 gboolean
115 check_time(const char *value)
116 {
117  if (crm_get_msec(value) < 5000) {
118  return FALSE;
119  }
120  return TRUE;
121 }
122 
123 gboolean
124 check_timer(const char *value)
125 {
126  if (crm_get_msec(value) < 0) {
127  return FALSE;
128  }
129  return TRUE;
130 }
131 
132 gboolean
133 check_sbd_timeout(const char *value)
134 {
135  const char *env_value = getenv("SBD_WATCHDOG_TIMEOUT");
136 
137  long sbd_timeout = crm_get_msec(env_value);
138  long st_timeout = crm_get_msec(value);
139 
140  if(value == NULL || st_timeout <= 0) {
141  crm_notice("Watchdog may be enabled but stonith-watchdog-timeout is disabled: %s", value);
142 
143  } else if(pcmk_locate_sbd() == 0) {
144  do_crm_log_always(LOG_EMERG, "Shutting down: stonith-watchdog-timeout is configured (%ldms) but SBD is not active", st_timeout);
146  return FALSE;
147 
148  } else if(st_timeout < sbd_timeout) {
149  do_crm_log_always(LOG_EMERG, "Shutting down: stonith-watchdog-timeout (%ldms) is too short (must be greater than %ldms)",
150  st_timeout, sbd_timeout);
152  return FALSE;
153  }
154 
155  crm_info("Watchdog functionality is consistent: %s delay exceeds timeout of %s", value, env_value);
156  return TRUE;
157 }
158 
159 
160 gboolean
161 check_boolean(const char *value)
162 {
163  int tmp = FALSE;
164 
165  if (crm_str_to_boolean(value, &tmp) != 1) {
166  return FALSE;
167  }
168  return TRUE;
169 }
170 
171 gboolean
172 check_number(const char *value)
173 {
174  errno = 0;
175  if (value == NULL) {
176  return FALSE;
177 
178  } else if (safe_str_eq(value, MINUS_INFINITY_S)) {
179 
180  } else if (safe_str_eq(value, INFINITY_S)) {
181 
182  } else {
183  crm_int_helper(value, NULL);
184  }
185 
186  if (errno != 0) {
187  return FALSE;
188  }
189  return TRUE;
190 }
191 
192 gboolean
193 check_quorum(const char *value)
194 {
195  if (safe_str_eq(value, "stop")) {
196  return TRUE;
197 
198  } else if (safe_str_eq(value, "freeze")) {
199  return TRUE;
200 
201  } else if (safe_str_eq(value, "ignore")) {
202  return TRUE;
203 
204  } else if (safe_str_eq(value, "suicide")) {
205  return TRUE;
206  }
207  return FALSE;
208 }
209 
210 gboolean
211 check_script(const char *value)
212 {
213  struct stat st;
214 
215  if(safe_str_eq(value, "/dev/null")) {
216  return TRUE;
217  }
218 
219  if(stat(value, &st) != 0) {
220  crm_err("Script %s does not exist", value);
221  return FALSE;
222  }
223 
224  if(S_ISREG(st.st_mode) == 0) {
225  crm_err("Script %s is not a regular file", value);
226  return FALSE;
227  }
228 
229  if( (st.st_mode & (S_IXUSR | S_IXGRP )) == 0) {
230  crm_err("Script %s is not executable", value);
231  return FALSE;
232  }
233 
234  return TRUE;
235 }
236 
237 gboolean
238 check_utilization(const char *value)
239 {
240  char *end = NULL;
241  long number = strtol(value, &end, 10);
242 
243  if(end && end[0] != '%') {
244  return FALSE;
245  } else if(number < 0) {
246  return FALSE;
247  }
248 
249  return TRUE;
250 }
251 
252 int
253 char2score(const char *score)
254 {
255  int score_f = 0;
256 
257  if (score == NULL) {
258 
259  } else if (safe_str_eq(score, MINUS_INFINITY_S)) {
260  score_f = -node_score_infinity;
261 
262  } else if (safe_str_eq(score, INFINITY_S)) {
263  score_f = node_score_infinity;
264 
265  } else if (safe_str_eq(score, "+" INFINITY_S)) {
266  score_f = node_score_infinity;
267 
268  } else if (safe_str_eq(score, "red")) {
269  score_f = node_score_red;
270 
271  } else if (safe_str_eq(score, "yellow")) {
272  score_f = node_score_yellow;
273 
274  } else if (safe_str_eq(score, "green")) {
275  score_f = node_score_green;
276 
277  } else {
278  score_f = crm_parse_int(score, NULL);
279  if (score_f > 0 && score_f > node_score_infinity) {
280  score_f = node_score_infinity;
281 
282  } else if (score_f < 0 && score_f < -node_score_infinity) {
283  score_f = -node_score_infinity;
284  }
285  }
286 
287  return score_f;
288 }
289 
290 char *
291 score2char_stack(int score, char *buf, size_t len)
292 {
293  if (score >= node_score_infinity) {
294  strncpy(buf, INFINITY_S, 9);
295  } else if (score <= -node_score_infinity) {
296  strncpy(buf, MINUS_INFINITY_S , 10);
297  } else {
298  return crm_itoa_stack(score, buf, len);
299  }
300 
301  return buf;
302 }
303 
304 char *
305 score2char(int score)
306 {
307  if (score >= node_score_infinity) {
308  return strdup(INFINITY_S);
309 
310  } else if (score <= -node_score_infinity) {
311  return strdup("-" INFINITY_S);
312  }
313  return crm_itoa(score);
314 }
315 
316 const char *
317 cluster_option(GHashTable * options, gboolean(*validate) (const char *),
318  const char *name, const char *old_name, const char *def_value)
319 {
320  const char *value = NULL;
321 
322  CRM_ASSERT(name != NULL);
323 
324  if (options != NULL) {
325  value = g_hash_table_lookup(options, name);
326  }
327 
328  if (value == NULL && old_name && options != NULL) {
329  value = g_hash_table_lookup(options, old_name);
330  if (value != NULL) {
331  crm_config_warn("Using deprecated name '%s' for"
332  " cluster option '%s'", old_name, name);
333  g_hash_table_insert(options, strdup(name), strdup(value));
334  value = g_hash_table_lookup(options, old_name);
335  }
336  }
337 
338  if (value == NULL) {
339  crm_trace("Using default value '%s' for cluster option '%s'", def_value, name);
340 
341  if (options == NULL) {
342  return def_value;
343 
344  } else if(def_value == NULL) {
345  return def_value;
346  }
347 
348  g_hash_table_insert(options, strdup(name), strdup(def_value));
349  value = g_hash_table_lookup(options, name);
350  }
351 
352  if (validate && validate(value) == FALSE) {
353  crm_config_err("Value '%s' for cluster option '%s' is invalid."
354  " Defaulting to %s", value, name, def_value);
355  g_hash_table_replace(options, strdup(name), strdup(def_value));
356  value = g_hash_table_lookup(options, name);
357  }
358 
359  return value;
360 }
361 
362 const char *
363 get_cluster_pref(GHashTable * options, pe_cluster_option * option_list, int len, const char *name)
364 {
365  int lpc = 0;
366  const char *value = NULL;
367  gboolean found = FALSE;
368 
369  for (lpc = 0; lpc < len; lpc++) {
370  if (safe_str_eq(name, option_list[lpc].name)) {
371  found = TRUE;
372  value = cluster_option(options,
373  option_list[lpc].is_valid,
374  option_list[lpc].name,
375  option_list[lpc].alt_name, option_list[lpc].default_value);
376  }
377  }
378  CRM_CHECK(found, crm_err("No option named: %s", name));
379  return value;
380 }
381 
382 void
383 config_metadata(const char *name, const char *version, const char *desc_short,
384  const char *desc_long, pe_cluster_option * option_list, int len)
385 {
386  int lpc = 0;
387 
388  fprintf(stdout, "<?xml version=\"1.0\"?>"
389  "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
390  "<resource-agent name=\"%s\">\n"
391  " <version>%s</version>\n"
392  " <longdesc lang=\"en\">%s</longdesc>\n"
393  " <shortdesc lang=\"en\">%s</shortdesc>\n"
394  " <parameters>\n", name, version, desc_long, desc_short);
395 
396  for (lpc = 0; lpc < len; lpc++) {
397  if (option_list[lpc].description_long == NULL && option_list[lpc].description_short == NULL) {
398  continue;
399  }
400  fprintf(stdout, " <parameter name=\"%s\" unique=\"0\">\n"
401  " <shortdesc lang=\"en\">%s</shortdesc>\n"
402  " <content type=\"%s\" default=\"%s\"/>\n"
403  " <longdesc lang=\"en\">%s%s%s</longdesc>\n"
404  " </parameter>\n",
405  option_list[lpc].name,
406  option_list[lpc].description_short,
407  option_list[lpc].type,
408  option_list[lpc].default_value,
409  option_list[lpc].description_long ? option_list[lpc].
410  description_long : option_list[lpc].description_short,
411  option_list[lpc].values ? " Allowed values: " : "",
412  option_list[lpc].values ? option_list[lpc].values : "");
413  }
414  fprintf(stdout, " </parameters>\n</resource-agent>\n");
415 }
416 
417 void
418 verify_all_options(GHashTable * options, pe_cluster_option * option_list, int len)
419 {
420  int lpc = 0;
421 
422  for (lpc = 0; lpc < len; lpc++) {
423  cluster_option(options,
424  option_list[lpc].is_valid,
425  option_list[lpc].name,
426  option_list[lpc].alt_name, option_list[lpc].default_value);
427  }
428 }
429 
430 char *
431 crm_concat(const char *prefix, const char *suffix, char join)
432 {
433  int len = 0;
434  char *new_str = NULL;
435 
436  CRM_ASSERT(prefix != NULL);
437  CRM_ASSERT(suffix != NULL);
438  len = strlen(prefix) + strlen(suffix) + 2;
439 
440  new_str = malloc(len);
441  if(new_str) {
442  sprintf(new_str, "%s%c%s", prefix, join, suffix);
443  new_str[len - 1] = 0;
444  }
445  return new_str;
446 }
447 
448 char *
449 generate_hash_key(const char *crm_msg_reference, const char *sys)
450 {
451  char *hash_key = crm_concat(sys ? sys : "none", crm_msg_reference, '_');
452 
453  crm_trace("created hash key: (%s)", hash_key);
454  return hash_key;
455 }
456 
457 
458 char *
459 crm_itoa_stack(int an_int, char *buffer, size_t len)
460 {
461  if (buffer != NULL) {
462  snprintf(buffer, len, "%d", an_int);
463  }
464 
465  return buffer;
466 }
467 
468 char *
469 crm_itoa(int an_int)
470 {
471  int len = 32;
472  char *buffer = NULL;
473 
474  buffer = malloc(len + 1);
475  if (buffer != NULL) {
476  snprintf(buffer, len, "%d", an_int);
477  }
478 
479  return buffer;
480 }
481 
482 int
483 crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
484 {
485  int rc = -1;
486  char *buffer = NULL;
487  struct passwd pwd;
488  struct passwd *pwentry = NULL;
489 
490  buffer = calloc(1, PW_BUFFER_LEN);
491  getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
492  if (pwentry) {
493  rc = 0;
494  if (uid) {
495  *uid = pwentry->pw_uid;
496  }
497  if (gid) {
498  *gid = pwentry->pw_gid;
499  }
500  crm_trace("Cluster user %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
501 
502  } else {
503  crm_err("Cluster user %s does not exist", name);
504  }
505 
506  free(buffer);
507  return rc;
508 }
509 
510 static int
511 crm_version_helper(const char *text, char **end_text)
512 {
513  int atoi_result = -1;
514 
515  CRM_ASSERT(end_text != NULL);
516 
517  errno = 0;
518 
519  if (text != NULL && text[0] != 0) {
520  atoi_result = (int)strtol(text, end_text, 10);
521 
522  if (errno == EINVAL) {
523  crm_err("Conversion of '%s' %c failed", text, text[0]);
524  atoi_result = -1;
525  }
526  }
527  return atoi_result;
528 }
529 
530 /*
531  * version1 < version2 : -1
532  * version1 = version2 : 0
533  * version1 > version2 : 1
534  */
535 int
536 compare_version(const char *version1, const char *version2)
537 {
538  int rc = 0;
539  int lpc = 0;
540  char *ver1_copy = NULL, *ver2_copy = NULL;
541  char *rest1 = NULL, *rest2 = NULL;
542 
543  if (version1 == NULL && version2 == NULL) {
544  return 0;
545  } else if (version1 == NULL) {
546  return -1;
547  } else if (version2 == NULL) {
548  return 1;
549  }
550 
551  ver1_copy = strdup(version1);
552  ver2_copy = strdup(version2);
553  rest1 = ver1_copy;
554  rest2 = ver2_copy;
555 
556  while (1) {
557  int digit1 = 0;
558  int digit2 = 0;
559 
560  lpc++;
561 
562  if (rest1 == rest2) {
563  break;
564  }
565 
566  if (rest1 != NULL) {
567  digit1 = crm_version_helper(rest1, &rest1);
568  }
569 
570  if (rest2 != NULL) {
571  digit2 = crm_version_helper(rest2, &rest2);
572  }
573 
574  if (digit1 < digit2) {
575  rc = -1;
576  break;
577 
578  } else if (digit1 > digit2) {
579  rc = 1;
580  break;
581  }
582 
583  if (rest1 != NULL && rest1[0] == '.') {
584  rest1++;
585  }
586  if (rest1 != NULL && rest1[0] == 0) {
587  rest1 = NULL;
588  }
589 
590  if (rest2 != NULL && rest2[0] == '.') {
591  rest2++;
592  }
593  if (rest2 != NULL && rest2[0] == 0) {
594  rest2 = NULL;
595  }
596  }
597 
598  free(ver1_copy);
599  free(ver2_copy);
600 
601  if (rc == 0) {
602  crm_trace("%s == %s (%d)", version1, version2, lpc);
603  } else if (rc < 0) {
604  crm_trace("%s < %s (%d)", version1, version2, lpc);
605  } else if (rc > 0) {
606  crm_trace("%s > %s (%d)", version1, version2, lpc);
607  }
608 
609  return rc;
610 }
611 
612 gboolean do_stderr = FALSE;
613 
614 void
616 {
617  free(data);
618 }
619 
620 #include <sys/types.h>
621 /* #include <stdlib.h> */
622 /* #include <limits.h> */
623 
624 long long
625 crm_int_helper(const char *text, char **end_text)
626 {
627  long long result = -1;
628  char *local_end_text = NULL;
629  int saved_errno = 0;
630 
631  errno = 0;
632 
633  if (text != NULL) {
634 #ifdef ANSI_ONLY
635  if (end_text != NULL) {
636  result = strtol(text, end_text, 10);
637  } else {
638  result = strtol(text, &local_end_text, 10);
639  }
640 #else
641  if (end_text != NULL) {
642  result = strtoll(text, end_text, 10);
643  } else {
644  result = strtoll(text, &local_end_text, 10);
645  }
646 #endif
647 
648  saved_errno = errno;
649 /* CRM_CHECK(errno != EINVAL); */
650  if (errno == EINVAL) {
651  crm_err("Conversion of %s failed", text);
652  result = -1;
653 
654  } else if (errno == ERANGE) {
655  crm_err("Conversion of %s was clipped: %lld", text, result);
656 
657  } else if (errno != 0) {
658  crm_perror(LOG_ERR, "Conversion of %s failed:", text);
659  }
660 
661  if (local_end_text != NULL && local_end_text[0] != '\0') {
662  crm_err("Characters left over after parsing '%s': '%s'", text, local_end_text);
663  }
664 
665  errno = saved_errno;
666  }
667  return result;
668 }
669 
670 int
671 crm_parse_int(const char *text, const char *default_text)
672 {
673  int atoi_result = -1;
674 
675  if (text != NULL) {
676  atoi_result = crm_int_helper(text, NULL);
677  if (errno == 0) {
678  return atoi_result;
679  }
680  }
681 
682  if (default_text != NULL) {
683  atoi_result = crm_int_helper(default_text, NULL);
684  if (errno == 0) {
685  return atoi_result;
686  }
687 
688  } else {
689  crm_err("No default conversion value supplied");
690  }
691 
692  return -1;
693 }
694 
695 gboolean
696 safe_str_neq(const char *a, const char *b)
697 {
698  if (a == b) {
699  return FALSE;
700 
701  } else if (a == NULL || b == NULL) {
702  return TRUE;
703 
704  } else if (strcasecmp(a, b) == 0) {
705  return FALSE;
706  }
707  return TRUE;
708 }
709 
710 gboolean
711 crm_is_true(const char *s)
712 {
713  gboolean ret = FALSE;
714 
715  if (s != NULL) {
716  crm_str_to_boolean(s, &ret);
717  }
718  return ret;
719 }
720 
721 int
722 crm_str_to_boolean(const char *s, int *ret)
723 {
724  if (s == NULL) {
725  return -1;
726 
727  } else if (strcasecmp(s, "true") == 0
728  || strcasecmp(s, "on") == 0
729  || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) {
730  *ret = TRUE;
731  return 1;
732 
733  } else if (strcasecmp(s, "false") == 0
734  || strcasecmp(s, "off") == 0
735  || strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0) {
736  *ret = FALSE;
737  return 1;
738  }
739  return -1;
740 }
741 
742 #ifndef NUMCHARS
743 # define NUMCHARS "0123456789."
744 #endif
745 
746 #ifndef WHITESPACE
747 # define WHITESPACE " \t\n\r\f"
748 #endif
749 
750 unsigned long long
751 crm_get_interval(const char *input)
752 {
753  unsigned long long msec = 0;
754 
755  if (input == NULL) {
756  return msec;
757 
758  } else if (input[0] != 'P') {
759  long long tmp = crm_get_msec(input);
760 
761  if(tmp > 0) {
762  msec = tmp;
763  }
764 
765  } else {
766  crm_time_t *interval = crm_time_parse_duration(input);
767 
768  msec = 1000 * crm_time_get_seconds(interval);
769  crm_time_free(interval);
770  }
771 
772  return msec;
773 }
774 
775 long long
776 crm_get_msec(const char *input)
777 {
778  const char *cp = input;
779  const char *units;
780  long long multiplier = 1000;
781  long long divisor = 1;
782  long long msec = -1;
783  char *end_text = NULL;
784 
785  /* double dret; */
786 
787  if (input == NULL) {
788  return msec;
789  }
790 
791  cp += strspn(cp, WHITESPACE);
792  units = cp + strspn(cp, NUMCHARS);
793  units += strspn(units, WHITESPACE);
794 
795  if (strchr(NUMCHARS, *cp) == NULL) {
796  return msec;
797  }
798 
799  if (strncasecmp(units, "ms", 2) == 0 || strncasecmp(units, "msec", 4) == 0) {
800  multiplier = 1;
801  divisor = 1;
802  } else if (strncasecmp(units, "us", 2) == 0 || strncasecmp(units, "usec", 4) == 0) {
803  multiplier = 1;
804  divisor = 1000;
805  } else if (strncasecmp(units, "s", 1) == 0 || strncasecmp(units, "sec", 3) == 0) {
806  multiplier = 1000;
807  divisor = 1;
808  } else if (strncasecmp(units, "m", 1) == 0 || strncasecmp(units, "min", 3) == 0) {
809  multiplier = 60 * 1000;
810  divisor = 1;
811  } else if (strncasecmp(units, "h", 1) == 0 || strncasecmp(units, "hr", 2) == 0) {
812  multiplier = 60 * 60 * 1000;
813  divisor = 1;
814  } else if (*units != EOS && *units != '\n' && *units != '\r') {
815  return msec;
816  }
817 
818  msec = crm_int_helper(cp, &end_text);
819  if (msec > LLONG_MAX/multiplier) {
820  /* arithmetics overflow while multiplier/divisor mutually exclusive */
821  return LLONG_MAX;
822  }
823  msec *= multiplier;
824  msec /= divisor;
825  /* dret += 0.5; */
826  /* msec = (long long)dret; */
827  return msec;
828 }
829 
830 char *
831 generate_op_key(const char *rsc_id, const char *op_type, int interval)
832 {
833  int len = 35;
834  char *op_id = NULL;
835 
836  CRM_CHECK(rsc_id != NULL, return NULL);
837  CRM_CHECK(op_type != NULL, return NULL);
838 
839  len += strlen(op_type);
840  len += strlen(rsc_id);
841  op_id = malloc(len);
842  CRM_CHECK(op_id != NULL, return NULL);
843  sprintf(op_id, "%s_%s_%d", rsc_id, op_type, interval);
844  return op_id;
845 }
846 
847 gboolean
848 parse_op_key(const char *key, char **rsc_id, char **op_type, int *interval)
849 {
850  char *notify = NULL;
851  char *mutable_key = NULL;
852  char *mutable_key_ptr = NULL;
853  int len = 0, offset = 0, ch = 0;
854 
855  CRM_CHECK(key != NULL, return FALSE);
856 
857  *interval = 0;
858  len = strlen(key);
859  offset = len - 1;
860 
861  crm_trace("Source: %s", key);
862 
863  while (offset > 0 && isdigit(key[offset])) {
864  int digits = len - offset;
865 
866  ch = key[offset] - '0';
867  CRM_CHECK(ch < 10, return FALSE);
868  CRM_CHECK(ch >= 0, return FALSE);
869  while (digits > 1) {
870  digits--;
871  ch = ch * 10;
872  }
873  *interval += ch;
874  offset--;
875  }
876 
877  crm_trace(" Interval: %d", *interval);
878  CRM_CHECK(key[offset] == '_', return FALSE);
879 
880  mutable_key = strdup(key);
881  mutable_key[offset] = 0;
882  offset--;
883 
884  while (offset > 0 && key[offset] != '_') {
885  offset--;
886  }
887 
888  CRM_CHECK(key[offset] == '_', free(mutable_key);
889  return FALSE);
890 
891  mutable_key_ptr = mutable_key + offset + 1;
892 
893  crm_trace(" Action: %s", mutable_key_ptr);
894 
895  *op_type = strdup(mutable_key_ptr);
896 
897  mutable_key[offset] = 0;
898  offset--;
899 
900  CRM_CHECK(mutable_key != mutable_key_ptr, free(mutable_key);
901  return FALSE);
902 
903  notify = strstr(mutable_key, "_post_notify");
904  if (notify && safe_str_eq(notify, "_post_notify")) {
905  notify[0] = 0;
906  }
907 
908  notify = strstr(mutable_key, "_pre_notify");
909  if (notify && safe_str_eq(notify, "_pre_notify")) {
910  notify[0] = 0;
911  }
912 
913  crm_trace(" Resource: %s", mutable_key);
914  *rsc_id = mutable_key;
915 
916  return TRUE;
917 }
918 
919 char *
920 generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
921 {
922  int len = 12;
923  char *op_id = NULL;
924 
925  CRM_CHECK(rsc_id != NULL, return NULL);
926  CRM_CHECK(op_type != NULL, return NULL);
927  CRM_CHECK(notify_type != NULL, return NULL);
928 
929  len += strlen(op_type);
930  len += strlen(rsc_id);
931  len += strlen(notify_type);
932  if(len > 0) {
933  op_id = malloc(len);
934  }
935  if (op_id != NULL) {
936  sprintf(op_id, "%s_%s_notify_%s_0", rsc_id, notify_type, op_type);
937  }
938  return op_id;
939 }
940 
941 char *
942 generate_transition_magic_v202(const char *transition_key, int op_status)
943 {
944  int len = 80;
945  char *fail_state = NULL;
946 
947  CRM_CHECK(transition_key != NULL, return NULL);
948 
949  len += strlen(transition_key);
950 
951  fail_state = malloc(len);
952  if (fail_state != NULL) {
953  snprintf(fail_state, len, "%d:%s", op_status, transition_key);
954  }
955  return fail_state;
956 }
957 
958 char *
959 generate_transition_magic(const char *transition_key, int op_status, int op_rc)
960 {
961  int len = 80;
962  char *fail_state = NULL;
963 
964  CRM_CHECK(transition_key != NULL, return NULL);
965 
966  len += strlen(transition_key);
967 
968  fail_state = malloc(len);
969  if (fail_state != NULL) {
970  snprintf(fail_state, len, "%d:%d;%s", op_status, op_rc, transition_key);
971  }
972  return fail_state;
973 }
974 
975 gboolean
976 decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
977  int *op_status, int *op_rc, int *target_rc)
978 {
979  int res = 0;
980  char *key = NULL;
981  gboolean result = TRUE;
982 
983  CRM_CHECK(magic != NULL, return FALSE);
984  CRM_CHECK(op_rc != NULL, return FALSE);
985  CRM_CHECK(op_status != NULL, return FALSE);
986 
987  key = calloc(1, strlen(magic) + 1);
988  res = sscanf(magic, "%d:%d;%s", op_status, op_rc, key);
989  if (res != 3) {
990  crm_warn("Only found %d items in: '%s'", res, magic);
991  free(key);
992  return FALSE;
993  }
994 
995  CRM_CHECK(decode_transition_key(key, uuid, transition_id, action_id, target_rc), result = FALSE);
996 
997  free(key);
998  return result;
999 }
1000 
1001 char *
1002 generate_transition_key(int transition_id, int action_id, int target_rc, const char *node)
1003 {
1004  int len = 40;
1005  char *fail_state = NULL;
1006 
1007  CRM_CHECK(node != NULL, return NULL);
1008 
1009  len += strlen(node);
1010 
1011  fail_state = malloc(len);
1012  if (fail_state != NULL) {
1013  snprintf(fail_state, len, "%d:%d:%d:%-*s", action_id, transition_id, target_rc, 36, node);
1014  }
1015  return fail_state;
1016 }
1017 
1018 gboolean
1019 decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
1020  int *target_rc)
1021 {
1022  int res = 0;
1023  gboolean done = FALSE;
1024 
1025  CRM_CHECK(uuid != NULL, return FALSE);
1026  CRM_CHECK(target_rc != NULL, return FALSE);
1027  CRM_CHECK(action_id != NULL, return FALSE);
1028  CRM_CHECK(transition_id != NULL, return FALSE);
1029 
1030  *uuid = calloc(1, 37);
1031  res = sscanf(key, "%d:%d:%d:%36s", action_id, transition_id, target_rc, *uuid);
1032  switch (res) {
1033  case 4:
1034  /* Post Pacemaker 0.6 */
1035  done = TRUE;
1036  break;
1037  case 3:
1038  case 2:
1039  /* this can be tricky - the UUID might start with an integer */
1040 
1041  /* Until Pacemaker 0.6 */
1042  done = TRUE;
1043  *target_rc = -1;
1044  res = sscanf(key, "%d:%d:%36s", action_id, transition_id, *uuid);
1045  if (res == 2) {
1046  *action_id = -1;
1047  res = sscanf(key, "%d:%36s", transition_id, *uuid);
1048  CRM_CHECK(res == 2, done = FALSE);
1049 
1050  } else if (res != 3) {
1051  CRM_CHECK(res == 3, done = FALSE);
1052  }
1053  break;
1054 
1055  case 1:
1056  /* Prior to Heartbeat 2.0.8 */
1057  done = TRUE;
1058  *action_id = -1;
1059  *target_rc = -1;
1060  res = sscanf(key, "%d:%36s", transition_id, *uuid);
1061  CRM_CHECK(res == 2, done = FALSE);
1062  break;
1063  default:
1064  crm_crit("Unhandled sscanf result (%d) for %s", res, key);
1065  }
1066 
1067  if (strlen(*uuid) != 36) {
1068  crm_warn("Bad UUID (%s) in sscanf result (%d) for %s", *uuid, res, key);
1069  }
1070 
1071  if (done == FALSE) {
1072  crm_err("Cannot decode '%s' rc=%d", key, res);
1073 
1074  free(*uuid);
1075  *uuid = NULL;
1076  *target_rc = -1;
1077  *action_id = -1;
1078  *transition_id = -1;
1079  }
1080 
1081  return done;
1082 }
1083 
1084 void
1085 filter_action_parameters(xmlNode * param_set, const char *version)
1086 {
1087  char *key = NULL;
1088  char *timeout = NULL;
1089  char *interval = NULL;
1090 
1091  const char *attr_filter[] = {
1092  XML_ATTR_ID,
1095  };
1096 
1097  gboolean do_delete = FALSE;
1098  int lpc = 0;
1099  static int meta_len = 0;
1100 
1101  if (meta_len == 0) {
1102  meta_len = strlen(CRM_META);
1103  }
1104 
1105  if (param_set == NULL) {
1106  return;
1107  }
1108 
1109  for (lpc = 0; lpc < DIMOF(attr_filter); lpc++) {
1110  xml_remove_prop(param_set, attr_filter[lpc]);
1111  }
1112 
1114  interval = crm_element_value_copy(param_set, key);
1115  free(key);
1116 
1118  timeout = crm_element_value_copy(param_set, key);
1119 
1120  if (param_set) {
1121  xmlAttrPtr xIter = param_set->properties;
1122 
1123  while (xIter) {
1124  const char *prop_name = (const char *)xIter->name;
1125 
1126  xIter = xIter->next;
1127  do_delete = FALSE;
1128  if (strncasecmp(prop_name, CRM_META, meta_len) == 0) {
1129  do_delete = TRUE;
1130  }
1131 
1132  if (do_delete) {
1133  xml_remove_prop(param_set, prop_name);
1134  }
1135  }
1136  }
1137 
1138  if (crm_get_msec(interval) > 0 && compare_version(version, "1.0.8") > 0) {
1139  /* Re-instate the operation's timeout value */
1140  if (timeout != NULL) {
1141  crm_xml_add(param_set, key, timeout);
1142  }
1143  }
1144 
1145  free(interval);
1146  free(timeout);
1147  free(key);
1148 }
1149 
1150 extern bool crm_is_daemon;
1151 
1152 /* coverity[+kill] */
1153 void
1154 crm_abort(const char *file, const char *function, int line,
1155  const char *assert_condition, gboolean do_core, gboolean do_fork)
1156 {
1157  int rc = 0;
1158  int pid = 0;
1159  int status = 0;
1160 
1161  /* Implied by the parent's error logging below */
1162  /* crm_write_blackbox(0); */
1163 
1164  if(crm_is_daemon == FALSE) {
1165  /* This is a command line tool - do not fork */
1166 
1167  /* crm_add_logfile(NULL); * Record it to a file? */
1168  crm_enable_stderr(TRUE); /* Make sure stderr is enabled so we can tell the caller */
1169  do_fork = FALSE; /* Just crash if needed */
1170  }
1171 
1172  if (do_core == FALSE) {
1173  crm_err("%s: Triggered assert at %s:%d : %s", function, file, line, assert_condition);
1174  return;
1175 
1176  } else if (do_fork) {
1177  pid = fork();
1178 
1179  } else {
1180  crm_err("%s: Triggered fatal assert at %s:%d : %s", function, file, line, assert_condition);
1181  }
1182 
1183  if (pid == -1) {
1184  crm_crit("%s: Cannot create core for non-fatal assert at %s:%d : %s",
1185  function, file, line, assert_condition);
1186  return;
1187 
1188  } else if(pid == 0) {
1189  /* Child process */
1190  abort();
1191  return;
1192  }
1193 
1194  /* Parent process */
1195  crm_err("%s: Forked child %d to record non-fatal assert at %s:%d : %s",
1196  function, pid, file, line, assert_condition);
1197  crm_write_blackbox(SIGTRAP, NULL);
1198 
1199  do {
1200  rc = waitpid(pid, &status, 0);
1201  if(rc == pid) {
1202  return; /* Job done */
1203  }
1204 
1205  } while(errno == EINTR);
1206 
1207  if (errno == ECHILD) {
1208  /* crm_mon does this */
1209  crm_trace("Cannot wait on forked child %d - SIGCHLD is probably set to SIG_IGN", pid);
1210  return;
1211  }
1212  crm_perror(LOG_ERR, "Cannot wait on forked child %d", pid);
1213 }
1214 
1215 int
1216 crm_pid_active(long pid, const char *daemon)
1217 {
1218  static int have_proc_pid = 0;
1219 
1220  if(have_proc_pid == 0) {
1221  char proc_path[PATH_MAX], exe_path[PATH_MAX];
1222 
1223  /* check to make sure pid hasn't been reused by another process */
1224  snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", (long unsigned int)getpid());
1225 
1226  have_proc_pid = 1;
1227  if(readlink(proc_path, exe_path, PATH_MAX - 1) < 0) {
1228  have_proc_pid = -1;
1229  }
1230  }
1231 
1232  if (pid <= 0) {
1233  return -1;
1234 
1235  } else if (kill(pid, 0) < 0 && errno == ESRCH) {
1236  return 0;
1237 
1238  } else if(daemon == NULL || have_proc_pid == -1) {
1239  return 1;
1240 
1241  } else {
1242  int rc = 0;
1243  char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
1244 
1245  /* check to make sure pid hasn't been reused by another process */
1246  snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", pid);
1247 
1248  rc = readlink(proc_path, exe_path, PATH_MAX - 1);
1249  if (rc < 0 && errno == EACCES) {
1250  crm_perror(LOG_INFO, "Could not read from %s", proc_path);
1251  return 1;
1252  } else if (rc < 0) {
1253  crm_perror(LOG_ERR, "Could not read from %s", proc_path);
1254  return 0;
1255  }
1256 
1257 
1258  exe_path[rc] = 0;
1259 
1260  if(daemon[0] != '/') {
1261  rc = snprintf(myexe_path, sizeof(proc_path), CRM_DAEMON_DIR"/%s", daemon);
1262  myexe_path[rc] = 0;
1263  } else {
1264  rc = snprintf(myexe_path, sizeof(proc_path), "%s", daemon);
1265  myexe_path[rc] = 0;
1266  }
1267 
1268  if (strcmp(exe_path, myexe_path) == 0) {
1269  return 1;
1270  }
1271  }
1272 
1273  return 0;
1274 }
1275 
1276 #define LOCKSTRLEN 11
1277 
1278 long
1279 crm_read_pidfile(const char *filename)
1280 {
1281  int fd;
1282  struct stat sbuf;
1283  long pid = -ENOENT;
1284  char buf[LOCKSTRLEN + 1];
1285 
1286  if ((fd = open(filename, O_RDONLY)) < 0) {
1287  goto bail;
1288  }
1289 
1290  if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
1291  sleep(2); /* if someone was about to create one,
1292  * give'm a sec to do so
1293  */
1294  }
1295 
1296  if (read(fd, buf, sizeof(buf)) < 1) {
1297  goto bail;
1298  }
1299 
1300  if (sscanf(buf, "%lu", &pid) > 0) {
1301  if (pid <= 0) {
1302  pid = -ESRCH;
1303  } else {
1304  crm_trace("Got pid %lu from %s\n", pid, filename);
1305  }
1306  }
1307 
1308  bail:
1309  if (fd >= 0) {
1310  close(fd);
1311  }
1312  return pid;
1313 }
1314 
1315 long
1316 crm_pidfile_inuse(const char *filename, long mypid, const char *daemon)
1317 {
1318  long pid = crm_read_pidfile(filename);
1319 
1320  if (pid < 2) {
1321  /* Invalid pid */
1322  pid = -ENOENT;
1323  unlink(filename);
1324 
1325  } else if (mypid && pid == mypid) {
1326  /* In use by us */
1327  pid = pcmk_ok;
1328 
1329  } else if (crm_pid_active(pid, daemon) == FALSE) {
1330  /* Contains a stale value */
1331  unlink(filename);
1332  pid = -ENOENT;
1333 
1334  } else if (mypid && pid != mypid) {
1335  /* locked by existing process - give up */
1336  pid = -EEXIST;
1337  }
1338 
1339  return pid;
1340 }
1341 
1342 static int
1343 crm_lock_pidfile(const char *filename, const char *name)
1344 {
1345  long mypid = 0;
1346  int fd = 0, rc = 0;
1347  char buf[LOCKSTRLEN + 1];
1348 
1349  mypid = (unsigned long)getpid();
1350 
1351  rc = crm_pidfile_inuse(filename, 0, name);
1352  if (rc == -ENOENT) {
1353  /* exists but the process is not active */
1354 
1355  } else if (rc != pcmk_ok) {
1356  /* locked by existing process - give up */
1357  return rc;
1358  }
1359 
1360  if ((fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
1361  /* Hmmh, why did we fail? Anyway, nothing we can do about it */
1362  return -errno;
1363  }
1364 
1365  snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN - 1, mypid);
1366  rc = write(fd, buf, LOCKSTRLEN);
1367  close(fd);
1368 
1369  if (rc != LOCKSTRLEN) {
1370  crm_perror(LOG_ERR, "Incomplete write to %s", filename);
1371  return -errno;
1372  }
1373 
1374  return crm_pidfile_inuse(filename, mypid, name);
1375 }
1376 
1377 void
1378 crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile)
1379 {
1380  int rc;
1381  long pid;
1382  const char *devnull = "/dev/null";
1383 
1384  if (daemonize == FALSE) {
1385  return;
1386  }
1387 
1388  /* Check before we even try... */
1389  rc = crm_pidfile_inuse(pidfile, 1, name);
1390  if(rc < pcmk_ok && rc != -ENOENT) {
1391  pid = crm_read_pidfile(pidfile);
1392  crm_err("%s: already running [pid %ld in %s]", name, pid, pidfile);
1393  printf("%s: already running [pid %ld in %s]\n", name, pid, pidfile);
1394  crm_exit(rc);
1395  }
1396 
1397  pid = fork();
1398  if (pid < 0) {
1399  fprintf(stderr, "%s: could not start daemon\n", name);
1400  crm_perror(LOG_ERR, "fork");
1401  crm_exit(EINVAL);
1402 
1403  } else if (pid > 0) {
1404  crm_exit(pcmk_ok);
1405  }
1406 
1407  rc = crm_lock_pidfile(pidfile, name);
1408  if(rc < pcmk_ok) {
1409  crm_err("Could not lock '%s' for %s: %s (%d)", pidfile, name, pcmk_strerror(rc), rc);
1410  printf("Could not lock '%s' for %s: %s (%d)\n", pidfile, name, pcmk_strerror(rc), rc);
1411  crm_exit(rc);
1412  }
1413 
1414  umask(S_IWGRP | S_IWOTH | S_IROTH);
1415 
1416  close(STDIN_FILENO);
1417  (void)open(devnull, O_RDONLY); /* Stdin: fd 0 */
1418  close(STDOUT_FILENO);
1419  (void)open(devnull, O_WRONLY); /* Stdout: fd 1 */
1420  close(STDERR_FILENO);
1421  (void)open(devnull, O_WRONLY); /* Stderr: fd 2 */
1422 }
1423 
1424 char *
1426 {
1427  int len;
1428 
1429  if (str == NULL) {
1430  return str;
1431  }
1432 
1433  for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
1434  str[len] = '\0';
1435  }
1436 
1437  return str;
1438 }
1439 
1440 gboolean
1441 crm_str_eq(const char *a, const char *b, gboolean use_case)
1442 {
1443  if (use_case) {
1444  return g_strcmp0(a, b) == 0;
1445 
1446  /* TODO - Figure out which calls, if any, really need to be case independent */
1447  } else if (a == b) {
1448  return TRUE;
1449 
1450  } else if (a == NULL || b == NULL) {
1451  /* shouldn't be comparing NULLs */
1452  return FALSE;
1453 
1454  } else if (strcasecmp(a, b) == 0) {
1455  return TRUE;
1456  }
1457  return FALSE;
1458 }
1459 
1460 char *
1461 crm_meta_name(const char *field)
1462 {
1463  int lpc = 0;
1464  int max = 0;
1465  char *crm_name = NULL;
1466 
1467  CRM_CHECK(field != NULL, return NULL);
1468  crm_name = crm_concat(CRM_META, field, '_');
1469 
1470  /* Massage the names so they can be used as shell variables */
1471  max = strlen(crm_name);
1472  for (; lpc < max; lpc++) {
1473  switch (crm_name[lpc]) {
1474  case '-':
1475  crm_name[lpc] = '_';
1476  break;
1477  }
1478  }
1479  return crm_name;
1480 }
1481 
1482 const char *
1483 crm_meta_value(GHashTable * hash, const char *field)
1484 {
1485  char *key = NULL;
1486  const char *value = NULL;
1487 
1488  key = crm_meta_name(field);
1489  if (key) {
1490  value = g_hash_table_lookup(hash, key);
1491  free(key);
1492  }
1493 
1494  return value;
1495 }
1496 
1497 static struct option *
1498 crm_create_long_opts(struct crm_option *long_options)
1499 {
1500  struct option *long_opts = NULL;
1501 
1502 #ifdef HAVE_GETOPT_H
1503  int index = 0, lpc = 0;
1504 
1505  /*
1506  * A previous, possibly poor, choice of '?' as the short form of --help
1507  * means that getopt_long() returns '?' for both --help and for "unknown option"
1508  *
1509  * This dummy entry allows us to differentiate between the two in crm_get_option()
1510  * and exit with the correct error code
1511  */
1512  long_opts = realloc_safe(long_opts, (index + 1) * sizeof(struct option));
1513  long_opts[index].name = "__dummmy__";
1514  long_opts[index].has_arg = 0;
1515  long_opts[index].flag = 0;
1516  long_opts[index].val = '_';
1517  index++;
1518 
1519  for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
1520  if (long_options[lpc].name[0] == '-') {
1521  continue;
1522  }
1523 
1524  long_opts = realloc_safe(long_opts, (index + 1) * sizeof(struct option));
1525  /*fprintf(stderr, "Creating %d %s = %c\n", index,
1526  * long_options[lpc].name, long_options[lpc].val); */
1527  long_opts[index].name = long_options[lpc].name;
1528  long_opts[index].has_arg = long_options[lpc].has_arg;
1529  long_opts[index].flag = long_options[lpc].flag;
1530  long_opts[index].val = long_options[lpc].val;
1531  index++;
1532  }
1533 
1534  /* Now create the list terminator */
1535  long_opts = realloc_safe(long_opts, (index + 1) * sizeof(struct option));
1536  long_opts[index].name = NULL;
1537  long_opts[index].has_arg = 0;
1538  long_opts[index].flag = 0;
1539  long_opts[index].val = 0;
1540 #endif
1541 
1542  return long_opts;
1543 }
1544 
1545 void
1546 crm_set_options(const char *short_options, const char *app_usage, struct crm_option *long_options,
1547  const char *app_desc)
1548 {
1549  if (short_options) {
1550  crm_short_options = strdup(short_options);
1551 
1552  } else if (long_options) {
1553  int lpc = 0;
1554  int opt_string_len = 0;
1555  char *local_short_options = NULL;
1556 
1557  for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
1558  if (long_options[lpc].val && long_options[lpc].val != '-' && long_options[lpc].val < UCHAR_MAX) {
1559  local_short_options = realloc_safe(local_short_options, opt_string_len + 4);
1560  local_short_options[opt_string_len++] = long_options[lpc].val;
1561  /* getopt(3) says: Two colons mean an option takes an optional arg; */
1562  if (long_options[lpc].has_arg == optional_argument) {
1563  local_short_options[opt_string_len++] = ':';
1564  }
1565  if (long_options[lpc].has_arg >= required_argument) {
1566  local_short_options[opt_string_len++] = ':';
1567  }
1568  local_short_options[opt_string_len] = 0;
1569  }
1570  }
1571  crm_short_options = local_short_options;
1572  crm_trace("Generated short option string: '%s'", local_short_options);
1573  }
1574 
1575  if (long_options) {
1576  crm_long_options = long_options;
1577  }
1578  if (app_desc) {
1579  crm_app_description = app_desc;
1580  }
1581  if (app_usage) {
1582  crm_app_usage = app_usage;
1583  }
1584 }
1585 
1586 int
1587 crm_get_option(int argc, char **argv, int *index)
1588 {
1589  return crm_get_option_long(argc, argv, index, NULL);
1590 }
1591 
1592 int
1593 crm_get_option_long(int argc, char **argv, int *index, const char **longname)
1594 {
1595 #ifdef HAVE_GETOPT_H
1596  static struct option *long_opts = NULL;
1597 
1598  if (long_opts == NULL && crm_long_options) {
1599  long_opts = crm_create_long_opts(crm_long_options);
1600  }
1601 
1602  *index = 0;
1603  if (long_opts) {
1604  int flag = getopt_long(argc, argv, crm_short_options, long_opts, index);
1605 
1606  switch (flag) {
1607  case 0:
1608  if (long_opts[*index].val) {
1609  return long_opts[*index].val;
1610  } else if (longname) {
1611  *longname = long_opts[*index].name;
1612  } else {
1613  crm_notice("Unhandled option --%s", long_opts[*index].name);
1614  return flag;
1615  }
1616  case -1: /* End of option processing */
1617  break;
1618  case ':':
1619  crm_trace("Missing argument");
1620  crm_help('?', 1);
1621  break;
1622  case '?':
1623  crm_help('?', *index ? 0 : 1);
1624  break;
1625  }
1626  return flag;
1627  }
1628 #endif
1629 
1630  if (crm_short_options) {
1631  return getopt(argc, argv, crm_short_options);
1632  }
1633 
1634  return -1;
1635 }
1636 
1637 int
1638 crm_help(char cmd, int exit_code)
1639 {
1640  int i = 0;
1641  FILE *stream = (exit_code ? stderr : stdout);
1642 
1643  if (cmd == 'v' || cmd == '$') {
1644  fprintf(stream, "Pacemaker %s\n", PACEMAKER_VERSION);
1645  fprintf(stream, "Written by Andrew Beekhof\n");
1646  goto out;
1647  }
1648 
1649  if (cmd == '!') {
1650  fprintf(stream, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
1651  goto out;
1652  }
1653 
1654  fprintf(stream, "%s - %s\n", crm_system_name, crm_app_description);
1655 
1656  if (crm_app_usage) {
1657  fprintf(stream, "Usage: %s %s\n", crm_system_name, crm_app_usage);
1658  }
1659 
1660  if (crm_long_options) {
1661  fprintf(stream, "Options:\n");
1662  for (i = 0; crm_long_options[i].name != NULL; i++) {
1663  if (crm_long_options[i].flags & pcmk_option_hidden) {
1664 
1665  } else if (crm_long_options[i].flags & pcmk_option_paragraph) {
1666  fprintf(stream, "%s\n\n", crm_long_options[i].desc);
1667 
1668  } else if (crm_long_options[i].flags & pcmk_option_example) {
1669  fprintf(stream, "\t#%s\n\n", crm_long_options[i].desc);
1670 
1671  } else if (crm_long_options[i].val == '-' && crm_long_options[i].desc) {
1672  fprintf(stream, "%s\n", crm_long_options[i].desc);
1673 
1674  } else {
1675  /* is val printable as char ? */
1676  if (crm_long_options[i].val && crm_long_options[i].val <= UCHAR_MAX) {
1677  fprintf(stream, " -%c,", crm_long_options[i].val);
1678  } else {
1679  fputs(" ", stream);
1680  }
1681  fprintf(stream, " --%s%s\t%s\n", crm_long_options[i].name,
1682  crm_long_options[i].has_arg == optional_argument ? "[=value]" :
1683  crm_long_options[i].has_arg == required_argument ? "=value" : "",
1684  crm_long_options[i].desc ? crm_long_options[i].desc : "");
1685  }
1686  }
1687 
1688  } else if (crm_short_options) {
1689  fprintf(stream, "Usage: %s - %s\n", crm_system_name, crm_app_description);
1690  for (i = 0; crm_short_options[i] != 0; i++) {
1691  int has_arg = no_argument /* 0 */;
1692 
1693  if (crm_short_options[i + 1] == ':') {
1694  if (crm_short_options[i + 2] == ':')
1695  has_arg = optional_argument /* 2 */;
1696  else
1697  has_arg = required_argument /* 1 */;
1698  }
1699 
1700  fprintf(stream, " -%c %s\n", crm_short_options[i],
1701  has_arg == optional_argument ? "[value]" :
1702  has_arg == required_argument ? "{value}" : "");
1703  i += has_arg;
1704  }
1705  }
1706 
1707  fprintf(stream, "\nReport bugs to %s\n", PACKAGE_BUGREPORT);
1708 
1709  out:
1710  return crm_exit(exit_code);
1711 }
1712 
1713 void cib_ipc_servers_init(qb_ipcs_service_t **ipcs_ro,
1714  qb_ipcs_service_t **ipcs_rw,
1715  qb_ipcs_service_t **ipcs_shm,
1716  struct qb_ipcs_service_handlers *ro_cb,
1717  struct qb_ipcs_service_handlers *rw_cb)
1718 {
1719  *ipcs_ro = mainloop_add_ipc_server(cib_channel_ro, QB_IPC_NATIVE, ro_cb);
1720  *ipcs_rw = mainloop_add_ipc_server(cib_channel_rw, QB_IPC_NATIVE, rw_cb);
1721  *ipcs_shm = mainloop_add_ipc_server(cib_channel_shm, QB_IPC_SHM, rw_cb);
1722 
1723  if (*ipcs_ro == NULL || *ipcs_rw == NULL || *ipcs_shm == NULL) {
1724  crm_err("Failed to create cib servers: exiting and inhibiting respawn.");
1725  crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
1727  }
1728 }
1729 
1730 void cib_ipc_servers_destroy(qb_ipcs_service_t *ipcs_ro,
1731  qb_ipcs_service_t *ipcs_rw,
1732  qb_ipcs_service_t *ipcs_shm)
1733 {
1734  qb_ipcs_destroy(ipcs_ro);
1735  qb_ipcs_destroy(ipcs_rw);
1736  qb_ipcs_destroy(ipcs_shm);
1737 }
1738 
1739 qb_ipcs_service_t *
1740 crmd_ipc_server_init(struct qb_ipcs_service_handlers *cb)
1741 {
1742  return mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_NATIVE, cb);
1743 }
1744 
1745 void
1746 attrd_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb)
1747 {
1748  *ipcs = mainloop_add_ipc_server(T_ATTRD, QB_IPC_NATIVE, cb);
1749 
1750  if (*ipcs == NULL) {
1751  crm_err("Failed to create attrd servers: exiting and inhibiting respawn.");
1752  crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
1754  }
1755 }
1756 
1757 void
1758 stonith_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb)
1759 {
1760  *ipcs = mainloop_add_ipc_server("stonith-ng", QB_IPC_NATIVE, cb);
1761 
1762  if (*ipcs == NULL) {
1763  crm_err("Failed to create stonith-ng servers: exiting and inhibiting respawn.");
1764  crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
1766  }
1767 }
1768 
1769 int
1770 attrd_update_delegate(crm_ipc_t * ipc, char command, const char *host, const char *name,
1771  const char *value, const char *section, const char *set, const char *dampen,
1772  const char *user_name, int options)
1773 {
1774  int rc = -ENOTCONN;
1775  int max = 5;
1776  const char *task = NULL;
1777  const char *name_as = NULL;
1778  xmlNode *update = create_xml_node(NULL, __FUNCTION__);
1779 
1780  static gboolean connected = TRUE;
1781  static crm_ipc_t *local_ipc = NULL;
1782  static enum crm_ipc_flags flags = crm_ipc_flags_none;
1783 
1784  if (ipc == NULL && local_ipc == NULL) {
1785  local_ipc = crm_ipc_new(T_ATTRD, 0);
1786  flags |= crm_ipc_client_response;
1787  connected = FALSE;
1788  }
1789 
1790  if (ipc == NULL) {
1791  ipc = local_ipc;
1792  }
1793 
1794  /* remap common aliases */
1795  if (safe_str_eq(section, "reboot")) {
1796  section = XML_CIB_TAG_STATUS;
1797 
1798  } else if (safe_str_eq(section, "forever")) {
1799  section = XML_CIB_TAG_NODES;
1800  }
1801 
1802  crm_xml_add(update, F_TYPE, T_ATTRD);
1803  crm_xml_add(update, F_ORIG, crm_system_name?crm_system_name:"unknown");
1804 
1805  if (name == NULL && command == 'U') {
1806  command = 'R';
1807  }
1808 
1809  switch (command) {
1810  case 'u':
1811  task = ATTRD_OP_UPDATE;
1812  name_as = F_ATTRD_REGEX;
1813  break;
1814  case 'D':
1815  case 'U':
1816  case 'v':
1817  task = ATTRD_OP_UPDATE;
1818  name_as = F_ATTRD_ATTRIBUTE;
1819  break;
1820  case 'R':
1821  task = ATTRD_OP_REFRESH;
1822  break;
1823  case 'B':
1824  task = ATTRD_OP_UPDATE_BOTH;
1825  name_as = F_ATTRD_ATTRIBUTE;
1826  break;
1827  case 'Y':
1828  task = ATTRD_OP_UPDATE_DELAY;
1829  name_as = F_ATTRD_ATTRIBUTE;
1830  break;
1831  case 'Q':
1832  task = ATTRD_OP_QUERY;
1833  name_as = F_ATTRD_ATTRIBUTE;
1834  break;
1835  case 'C':
1836  task = ATTRD_OP_PEER_REMOVE;
1837  break;
1838  }
1839 
1840  if (name_as != NULL) {
1841  if (name == NULL) {
1842  rc = -EINVAL;
1843  goto done;
1844  }
1845  crm_xml_add(update, name_as, name);
1846  }
1847 
1848  crm_xml_add(update, F_ATTRD_TASK, task);
1849  crm_xml_add(update, F_ATTRD_VALUE, value);
1850  crm_xml_add(update, F_ATTRD_DAMPEN, dampen);
1851  crm_xml_add(update, F_ATTRD_SECTION, section);
1852  crm_xml_add(update, F_ATTRD_HOST, host);
1853  crm_xml_add(update, F_ATTRD_SET, set);
1854  crm_xml_add_int(update, F_ATTRD_IS_REMOTE, is_set(options, attrd_opt_remote));
1855  crm_xml_add_int(update, F_ATTRD_IS_PRIVATE, is_set(options, attrd_opt_private));
1856 #if ENABLE_ACL
1857  if (user_name) {
1858  crm_xml_add(update, F_ATTRD_USER, user_name);
1859  }
1860 #endif
1861 
1862  while (max > 0) {
1863  if (connected == FALSE) {
1864  crm_info("Connecting to cluster... %d retries remaining", max);
1865  connected = crm_ipc_connect(ipc);
1866  }
1867 
1868  if (connected) {
1869  rc = crm_ipc_send(ipc, update, flags, 0, NULL);
1870  } else {
1871  crm_perror(LOG_INFO, "Connection to cluster attribute manager failed");
1872  }
1873 
1874  if (ipc != local_ipc) {
1875  break;
1876 
1877  } else if (rc > 0) {
1878  break;
1879 
1880  } else if (rc == -EAGAIN || rc == -EALREADY) {
1881  sleep(5 - max);
1882  max--;
1883 
1884  } else {
1885  crm_ipc_close(ipc);
1886  connected = FALSE;
1887  sleep(5 - max);
1888  max--;
1889  }
1890  }
1891 
1892 done:
1893  free_xml(update);
1894  if (rc > 0) {
1895  crm_debug("Sent update: %s=%s for %s", name, value, host ? host : "localhost");
1896  rc = pcmk_ok;
1897 
1898  } else {
1899  crm_debug("Could not send update %s=%s for %s: %s (%d)", name, value,
1900  host ? host : "localhost", pcmk_strerror(rc), rc);
1901  }
1902  return rc;
1903 }
1904 
1905 #define FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
1906 static void
1907 append_digest(lrmd_event_data_t * op, xmlNode * update, const char *version, const char *magic,
1908  int level)
1909 {
1910  /* this will enable us to later determine that the
1911  * resource's parameters have changed and we should force
1912  * a restart
1913  */
1914  char *digest = NULL;
1915  xmlNode *args_xml = NULL;
1916 
1917  if (op->params == NULL) {
1918  return;
1919  }
1920 
1921  args_xml = create_xml_node(NULL, XML_TAG_PARAMS);
1922  g_hash_table_foreach(op->params, hash2field, args_xml);
1923  filter_action_parameters(args_xml, version);
1924  digest = calculate_operation_digest(args_xml, version);
1925 
1926 #if 0
1927  if (level < get_crm_log_level()
1928  && op->interval == 0 && crm_str_eq(op->op_type, CRMD_ACTION_START, TRUE)) {
1929  char *digest_source = dump_xml_unformatted(args_xml);
1930 
1931  do_crm_log(level, "Calculated digest %s for %s (%s). Source: %s\n",
1932  digest, ID(update), magic, digest_source);
1933  free(digest_source);
1934  }
1935 #endif
1936  crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest);
1937 
1938  free_xml(args_xml);
1939  free(digest);
1940 }
1941 
1942 int
1944 {
1945  int rc = 0;
1946 
1947  if (op && op->user_data) {
1948  int dummy = 0;
1949  char *uuid = NULL;
1950 
1951  decode_transition_key(op->user_data, &uuid, &dummy, &dummy, &rc);
1952  free(uuid);
1953  }
1954  return rc;
1955 }
1956 
1957 gboolean
1959 {
1960  switch (op->op_status) {
1961  case PCMK_LRM_OP_CANCELLED:
1962  case PCMK_LRM_OP_PENDING:
1963  return FALSE;
1964  break;
1965 
1967  case PCMK_LRM_OP_TIMEOUT:
1968  case PCMK_LRM_OP_ERROR:
1969  return TRUE;
1970  break;
1971 
1972  default:
1973  if (target_rc != op->rc) {
1974  return TRUE;
1975  }
1976  }
1977 
1978  return FALSE;
1979 }
1980 
1981 xmlNode *
1982 create_operation_update(xmlNode * parent, lrmd_event_data_t * op, const char * caller_version,
1983  int target_rc, const char * node, const char * origin, int level)
1984 {
1985  char *key = NULL;
1986  char *magic = NULL;
1987  char *op_id = NULL;
1988  char *op_id_additional = NULL;
1989  char *local_user_data = NULL;
1990  const char *exit_reason = NULL;
1991 
1992  xmlNode *xml_op = NULL;
1993  const char *task = NULL;
1994  gboolean dc_munges_migrate_ops = (compare_version(caller_version, "3.0.3") < 0);
1995  gboolean dc_needs_unique_ops = (compare_version(caller_version, "3.0.6") < 0);
1996 
1997  CRM_CHECK(op != NULL, return NULL);
1998  do_crm_log(level, "%s: Updating resource %s after %s op %s (interval=%d)",
1999  origin, op->rsc_id, op->op_type, services_lrm_status_str(op->op_status),
2000  op->interval);
2001 
2002  crm_trace("DC version: %s", caller_version);
2003 
2004  task = op->op_type;
2005  /* remap the task name under various scenarios
2006  * this makes life easier for the PE when trying determine the current state
2007  */
2008  if (crm_str_eq(task, "reload", TRUE)) {
2009  if (op->op_status == PCMK_LRM_OP_DONE) {
2010  task = CRMD_ACTION_START;
2011  } else {
2012  task = CRMD_ACTION_STATUS;
2013  }
2014 
2015  } else if (dc_munges_migrate_ops && crm_str_eq(task, CRMD_ACTION_MIGRATE, TRUE)) {
2016  /* if the migrate_from fails it will have enough info to do the right thing */
2017  if (op->op_status == PCMK_LRM_OP_DONE) {
2018  task = CRMD_ACTION_STOP;
2019  } else {
2020  task = CRMD_ACTION_STATUS;
2021  }
2022 
2023  } else if (dc_munges_migrate_ops
2024  && op->op_status == PCMK_LRM_OP_DONE
2025  && crm_str_eq(task, CRMD_ACTION_MIGRATED, TRUE)) {
2026  task = CRMD_ACTION_START;
2027  }
2028 
2029  key = generate_op_key(op->rsc_id, task, op->interval);
2030  if (dc_needs_unique_ops && op->interval > 0) {
2031  op_id = strdup(key);
2032 
2033  } else if (crm_str_eq(task, CRMD_ACTION_NOTIFY, TRUE)) {
2034  const char *n_type = crm_meta_value(op->params, "notify_type");
2035  const char *n_task = crm_meta_value(op->params, "notify_operation");
2036 
2037  CRM_LOG_ASSERT(n_type != NULL);
2038  CRM_LOG_ASSERT(n_task != NULL);
2039  op_id = generate_notify_key(op->rsc_id, n_type, n_task);
2040 
2041  /* these are not yet allowed to fail */
2043  op->rc = 0;
2044 
2045  } else if (did_rsc_op_fail(op, target_rc)) {
2046  op_id = generate_op_key(op->rsc_id, "last_failure", 0);
2047  if (op->interval == 0) {
2048  /* Ensure 'last' gets updated too in case recording-pending="true" */
2049  op_id_additional = generate_op_key(op->rsc_id, "last", 0);
2050  }
2051  exit_reason = op->exit_reason;
2052 
2053  } else if (op->interval > 0) {
2054  op_id = strdup(key);
2055 
2056  } else {
2057  op_id = generate_op_key(op->rsc_id, "last", 0);
2058  }
2059 
2060  again:
2061  xml_op = find_entity(parent, XML_LRM_TAG_RSC_OP, op_id);
2062  if (xml_op == NULL) {
2063  xml_op = create_xml_node(parent, XML_LRM_TAG_RSC_OP);
2064  }
2065 
2066  if (op->user_data == NULL) {
2067  crm_debug("Generating fake transition key for:"
2068  " %s_%s_%d %d from %s",
2069  op->rsc_id, op->op_type, op->interval, op->call_id, origin);
2070  local_user_data = generate_transition_key(-1, op->call_id, target_rc, FAKE_TE_ID);
2071  op->user_data = local_user_data;
2072  }
2073 
2074  if(magic == NULL) {
2075  magic = generate_transition_magic(op->user_data, op->op_status, op->rc);
2076  }
2077 
2078  crm_xml_add(xml_op, XML_ATTR_ID, op_id);
2079  crm_xml_add(xml_op, XML_LRM_ATTR_TASK_KEY, key);
2080  crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task);
2081  crm_xml_add(xml_op, XML_ATTR_ORIGIN, origin);
2082  crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version);
2084  crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic);
2085  crm_xml_add(xml_op, XML_LRM_ATTR_EXIT_REASON, exit_reason);
2086  crm_xml_add(xml_op, XML_LRM_ATTR_TARGET, node); /* For context during triage */
2087 
2089  crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc);
2092 
2093  if (compare_version("2.1", caller_version) <= 0) {
2094  if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) {
2095  crm_trace("Timing data (%s_%s_%d): last=%u change=%u exec=%u queue=%u",
2096  op->rsc_id, op->op_type, op->interval,
2097  op->t_run, op->t_rcchange, op->exec_time, op->queue_time);
2098 
2099  if (op->interval == 0) {
2100  /* The values are the same for non-recurring ops */
2103 
2104  } else if(op->t_rcchange) {
2105  /* last-run is not accurate for recurring ops */
2107 
2108  } else {
2109  /* ...but is better than nothing otherwise */
2111  }
2112 
2115  }
2116  }
2117 
2118  if (crm_str_eq(op->op_type, CRMD_ACTION_MIGRATE, TRUE)
2119  || crm_str_eq(op->op_type, CRMD_ACTION_MIGRATED, TRUE)) {
2120  /*
2121  * Record migrate_source and migrate_target always for migrate ops.
2122  */
2123  const char *name = XML_LRM_ATTR_MIGRATE_SOURCE;
2124 
2125  crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
2126 
2128  crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
2129  }
2130 
2131  append_digest(op, xml_op, caller_version, magic, LOG_DEBUG);
2132 
2133  if (op_id_additional) {
2134  free(op_id);
2135  op_id = op_id_additional;
2136  op_id_additional = NULL;
2137  goto again;
2138  }
2139 
2140  if (local_user_data) {
2141  free(local_user_data);
2142  op->user_data = NULL;
2143  }
2144  free(magic);
2145  free(op_id);
2146  free(key);
2147  return xml_op;
2148 }
2149 
2150 bool
2151 pcmk_acl_required(const char *user)
2152 {
2153 #if ENABLE_ACL
2154  if(user == NULL || strlen(user) == 0) {
2155  crm_trace("no user set");
2156  return FALSE;
2157 
2158  } else if (strcmp(user, CRM_DAEMON_USER) == 0) {
2159  return FALSE;
2160 
2161  } else if (strcmp(user, "root") == 0) {
2162  return FALSE;
2163  }
2164  crm_trace("acls required for %s", user);
2165  return TRUE;
2166 #else
2167  crm_trace("acls not supported");
2168  return FALSE;
2169 #endif
2170 }
2171 
2172 #if ENABLE_ACL
2173 char *
2174 uid2username(uid_t uid)
2175 {
2176  struct passwd *pwent = getpwuid(uid);
2177 
2178  if (pwent == NULL) {
2179  crm_perror(LOG_ERR, "Cannot get password entry of uid: %d", uid);
2180  return NULL;
2181 
2182  } else {
2183  return strdup(pwent->pw_name);
2184  }
2185 }
2186 
2187 const char *
2188 crm_acl_get_set_user(xmlNode * request, const char *field, const char *peer_user)
2189 {
2190  /* field is only checked for backwards compatibility */
2191  static const char *effective_user = NULL;
2192  const char *requested_user = NULL;
2193  const char *user = NULL;
2194 
2195  if(effective_user == NULL) {
2196  effective_user = uid2username(geteuid());
2197  }
2198 
2199  requested_user = crm_element_value(request, XML_ACL_TAG_USER);
2200  if(requested_user == NULL) {
2201  requested_user = crm_element_value(request, field);
2202  }
2203 
2204  if (is_privileged(effective_user) == FALSE) {
2205  /* We're not running as a privileged user, set or overwrite any existing value for $XML_ACL_TAG_USER */
2206  user = effective_user;
2207 
2208  } else if(peer_user == NULL && requested_user == NULL) {
2209  /* No user known or requested, use 'effective_user' and make sure one is set for the request */
2210  user = effective_user;
2211 
2212  } else if(peer_user == NULL) {
2213  /* No user known, trusting 'requested_user' */
2214  user = requested_user;
2215 
2216  } else if (is_privileged(peer_user) == FALSE) {
2217  /* The peer is not a privileged user, set or overwrite any existing value for $XML_ACL_TAG_USER */
2218  user = peer_user;
2219 
2220  } else if (requested_user == NULL) {
2221  /* Even if we're privileged, make sure there is always a value set */
2222  user = peer_user;
2223 
2224  } else {
2225  /* Legal delegation to 'requested_user' */
2226  user = requested_user;
2227  }
2228 
2229  /* Yes, pointer comparision */
2230  if(user != crm_element_value(request, XML_ACL_TAG_USER)) {
2231  crm_xml_add(request, XML_ACL_TAG_USER, user);
2232  }
2233 
2234  if(field != NULL && user != crm_element_value(request, field)) {
2235  crm_xml_add(request, field, user);
2236  }
2237 
2238  return requested_user;
2239 }
2240 
2241 void
2242 determine_request_user(const char *user, xmlNode * request, const char *field)
2243 {
2244  /* Get our internal validation out of the way first */
2245  CRM_CHECK(user != NULL && request != NULL && field != NULL, return);
2246 
2247  /* If our peer is a privileged user, we might be doing something on behalf of someone else */
2248  if (is_privileged(user) == FALSE) {
2249  /* We're not a privileged user, set or overwrite any existing value for $field */
2250  crm_xml_replace(request, field, user);
2251 
2252  } else if (crm_element_value(request, field) == NULL) {
2253  /* Even if we're privileged, make sure there is always a value set */
2254  crm_xml_replace(request, field, user);
2255 
2256 /* } else { Legal delegation */
2257  }
2258 
2259  crm_trace("Processing msg as user '%s'", crm_element_value(request, field));
2260 }
2261 #endif
2262 
2263 /*
2264  * This re-implements g_str_hash as it was prior to glib2-2.28:
2265  *
2266  * http://git.gnome.org/browse/glib/commit/?id=354d655ba8a54b754cb5a3efb42767327775696c
2267  *
2268  * Note that the new g_str_hash is presumably a *better* hash (it's actually
2269  * a correct implementation of DJB's hash), but we need to preserve existing
2270  * behaviour, because the hash key ultimately determines the "sort" order
2271  * when iterating through GHashTables, which affects allocation of scores to
2272  * clone instances when iterating through rsc->allowed_nodes. It (somehow)
2273  * also appears to have some minor impact on the ordering of a few
2274  * pseudo_event IDs in the transition graph.
2275  */
2276 guint
2277 g_str_hash_traditional(gconstpointer v)
2278 {
2279  const signed char *p;
2280  guint32 h = 0;
2281 
2282  for (p = v; *p != '\0'; p++)
2283  h = (h << 5) - h + *p;
2284 
2285  return h;
2286 }
2287 
2288 guint
2289 crm_strcase_hash(gconstpointer v)
2290 {
2291  const signed char *p;
2292  guint32 h = 0;
2293 
2294  for (p = v; *p != '\0'; p++)
2295  h = (h << 5) - h + g_ascii_tolower(*p);
2296 
2297  return h;
2298 }
2299 
2300 void *
2301 find_library_function(void **handle, const char *lib, const char *fn, gboolean fatal)
2302 {
2303  char *error;
2304  void *a_function;
2305 
2306  if (*handle == NULL) {
2307  *handle = dlopen(lib, RTLD_LAZY);
2308  }
2309 
2310  if (!(*handle)) {
2311  crm_err("%sCould not open %s: %s", fatal ? "Fatal: " : "", lib, dlerror());
2312  if (fatal) {
2314  }
2315  return NULL;
2316  }
2317 
2318  a_function = dlsym(*handle, fn);
2319  if (a_function == NULL) {
2320  error = dlerror();
2321  crm_err("%sCould not find %s in %s: %s", fatal ? "Fatal: " : "", fn, lib, error);
2322  if (fatal) {
2324  }
2325  }
2326 
2327  return a_function;
2328 }
2329 
2330 char *
2331 add_list_element(char *list, const char *value)
2332 {
2333  int len = 0;
2334  int last = 0;
2335 
2336  if (value == NULL) {
2337  return list;
2338  }
2339  if (list) {
2340  last = strlen(list);
2341  }
2342  len = last + 2; /* +1 space, +1 EOS */
2343  len += strlen(value);
2344  list = realloc_safe(list, len);
2345  sprintf(list + last, " %s", value);
2346  return list;
2347 }
2348 
2349 void *
2350 convert_const_pointer(const void *ptr)
2351 {
2352  /* Worst function ever */
2353  return (void *)ptr;
2354 }
2355 
2356 #ifdef HAVE_UUID_UUID_H
2357 # include <uuid/uuid.h>
2358 #endif
2359 
2360 char *
2362 {
2363  unsigned char uuid[16];
2364  char *buffer = malloc(37); /* Including NUL byte */
2365 
2366  uuid_generate(uuid);
2367  uuid_unparse(uuid, buffer);
2368  return buffer;
2369 }
2370 
2371 #include <md5.h>
2372 
2373 char *
2374 crm_md5sum(const char *buffer)
2375 {
2376  int lpc = 0, len = 0;
2377  char *digest = NULL;
2378  unsigned char raw_digest[MD5_DIGEST_SIZE];
2379 
2380  if (buffer == NULL) {
2381  buffer = "";
2382  }
2383  len = strlen(buffer);
2384 
2385  crm_trace("Beginning digest of %d bytes", len);
2386  digest = malloc(2 * MD5_DIGEST_SIZE + 1);
2387  if(digest) {
2388  md5_buffer(buffer, len, raw_digest);
2389  for (lpc = 0; lpc < MD5_DIGEST_SIZE; lpc++) {
2390  sprintf(digest + (2 * lpc), "%02x", raw_digest[lpc]);
2391  }
2392  digest[(2 * MD5_DIGEST_SIZE)] = 0;
2393  crm_trace("Digest %s.", digest);
2394 
2395  } else {
2396  crm_err("Could not create digest");
2397  }
2398  return digest;
2399 }
2400 
2401 #include <time.h>
2402 #include <bzlib.h>
2403 
2404 bool
2405 crm_compress_string(const char *data, int length, int max, char **result, unsigned int *result_len)
2406 {
2407  int rc;
2408  char *compressed = NULL;
2409  char *uncompressed = strdup(data);
2410  struct timespec after_t;
2411  struct timespec before_t;
2412 
2413  if(max == 0) {
2414  max = (length * 1.1) + 600; /* recomended size */
2415  }
2416 
2417 #ifdef CLOCK_MONOTONIC
2418  clock_gettime(CLOCK_MONOTONIC, &before_t);
2419 #endif
2420 
2421  /* coverity[returned_null] Ignore */
2422  compressed = malloc(max);
2423 
2424  *result_len = max;
2425  rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length, CRM_BZ2_BLOCKS, 0,
2426  CRM_BZ2_WORK);
2427 
2428  free(uncompressed);
2429 
2430  if (rc != BZ_OK) {
2431  crm_err("Compression of %d bytes failed: %s (%d)", length, bz2_strerror(rc), rc);
2432  free(compressed);
2433  return FALSE;
2434  }
2435 
2436 #ifdef CLOCK_MONOTONIC
2437  clock_gettime(CLOCK_MONOTONIC, &after_t);
2438 
2439  crm_info("Compressed %d bytes into %d (ratio %d:1) in %dms",
2440  length, *result_len, length / (*result_len),
2441  (after_t.tv_sec - before_t.tv_sec) * 1000 + (after_t.tv_nsec -
2442  before_t.tv_nsec) / 1000000);
2443 #else
2444  crm_info("Compressed %d bytes into %d (ratio %d:1)",
2445  length, *result_len, length / (*result_len));
2446 #endif
2447 
2448  *result = compressed;
2449  return TRUE;
2450 }
2451 
2452 #ifdef HAVE_GNUTLS_GNUTLS_H
2453 void
2454 crm_gnutls_global_init(void)
2455 {
2456  signal(SIGPIPE, SIG_IGN);
2457  gnutls_global_init();
2458 }
2459 #endif
2460 
Services API.
#define T_ATTRD
Definition: msg_xml.h:50
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:164
#define XML_RSC_OP_LAST_CHANGE
Definition: msg_xml.h:283
void * find_library_function(void **handle, const char *lib, const char *fn, gboolean fatal)
Definition: utils.c:2301
void verify_all_options(GHashTable *options, pe_cluster_option *option_list, int len)
Definition: utils.c:418
#define F_ATTRD_VALUE
Definition: crm_internal.h:278
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, int *interval)
Definition: utils.c:848
void crm_write_blackbox(int nsig, struct qb_log_callsite *callsite)
Definition: logging.c:407
bool crm_ipc_connect(crm_ipc_t *client)
Establish an IPC connection to a Pacemaker component.
Definition: ipc.c:798
A dumping ground.
#define F_TYPE
Definition: msg_xml.h:34
long long crm_get_msec(const char *input)
Definition: utils.c:776
int crm_get_option_long(int argc, char **argv, int *index, const char **longname)
Definition: utils.c:1593
#define crm_notice(fmt, args...)
Definition: logging.h:250
int crm_user_lookup(const char *name, uid_t *uid, gid_t *gid)
Definition: utils.c:483
#define CRMD_ACTION_MIGRATED
Definition: crm.h:150
gboolean do_stderr
Definition: utils.c:612
int attrd_update_delegate(crm_ipc_t *ipc, char command, const char *host, const char *name, const char *value, const char *section, const char *set, const char *dampen, const char *user_name, int options)
Definition: utils.c:1770
void crm_enable_stderr(int enable)
Definition: logging.c:890
void * convert_const_pointer(const void *ptr)
Definition: utils.c:2350
bool crm_compress_string(const char *data, int length, int max, char **result, unsigned int *result_len)
Definition: utils.c:2405
#define crm_crit(fmt, args...)
Definition: logging.h:247
gboolean check_utilization(const char *value)
Definition: utils.c:238
#define INFINITY
Definition: crm.h:77
const char * user_data
Definition: lrmd.h:196
const char * crm_meta_value(GHashTable *hash, const char *field)
Definition: utils.c:1483
const char * rsc_id
Definition: lrmd.h:192
#define XML_ATTR_TRANSITION_MAGIC
Definition: msg_xml.h:357
void cib_ipc_servers_destroy(qb_ipcs_service_t *ipcs_ro, qb_ipcs_service_t *ipcs_rw, qb_ipcs_service_t *ipcs_shm)
Definition: utils.c:1730
void attrd_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb)
Definition: utils.c:1746
char * crm_concat(const char *prefix, const char *suffix, char join)
Definition: utils.c:431
struct crm_time_s crm_time_t
Definition: iso8601.h:37
const char * pcmk_strerror(int rc)
Definition: logging.c:1113
int node_score_infinity
Definition: utils.c:79
gboolean check_time(const char *value)
Definition: utils.c:115
#define crm_config_err(fmt...)
Definition: crm_internal.h:270
#define F_ATTRD_HOST
Definition: crm_internal.h:285
unsigned int queue_time
Definition: lrmd.h:222
void crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile)
Definition: utils.c:1378
void crm_xml_cleanup(void)
Definition: xml.c:5361
gboolean decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id, int *target_rc)
Definition: utils.c:1019
#define F_ATTRD_REGEX
Definition: crm_internal.h:276
#define pcmk_ok
Definition: error.h:42
void g_hash_destroy_str(gpointer data)
Definition: utils.c:615
#define CRMD_ACTION_NOTIFY
Definition: crm.h:163
void cib_ipc_servers_init(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, qb_ipcs_service_t **ipcs_shm, struct qb_ipcs_service_handlers *ro_cb, struct qb_ipcs_service_handlers *rw_cb)
Definition: utils.c:1713
char * crm_md5sum(const char *buffer)
Definition: utils.c:2374
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
Definition: xml.c:2476
#define XML_RSC_OP_T_EXEC
Definition: msg_xml.h:285
gboolean check_sbd_timeout(const char *value)
Definition: utils.c:133
char * generate_transition_key(int transition_id, int action_id, int target_rc, const char *node)
Definition: utils.c:1002
#define F_ATTRD_USER
Definition: crm_internal.h:287
#define XML_LRM_ATTR_INTERVAL
Definition: msg_xml.h:262
gboolean safe_str_neq(const char *a, const char *b)
Definition: utils.c:696
gboolean crm_config_warning
Definition: utils.c:73
int crm_help(char cmd, int exit_code)
Definition: utils.c:1638
#define XML_LRM_ATTR_OP_DIGEST
Definition: msg_xml.h:276
#define XML_ATTR_TIMEOUT
Definition: msg_xml.h:94
#define ATTRD_OP_UPDATE
Definition: crm_internal.h:293
void mainloop_cleanup(void)
Definition: mainloop.c:409
int node_score_red
Definition: utils.c:76
char * crm_element_value_copy(xmlNode *data, const char *name)
Definition: xml.c:4034
Local Resource Manager.
unsigned int t_rcchange
Definition: lrmd.h:218
crm_time_t * crm_time_parse_duration(const char *duration_str)
Definition: iso8601.c:830
AIS_Host host
Definition: internal.h:52
#define MINUS_INFINITY_S
Definition: crm.h:75
#define F_ATTRD_SECTION
Definition: crm_internal.h:282
char * crm_itoa_stack(int an_int, char *buffer, size_t len)
Definition: utils.c:459
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:150
void crm_set_options(const char *short_options, const char *app_usage, struct crm_option *long_options, const char *app_desc)
Definition: utils.c:1546
uint32_t pid
Definition: internal.h:49
enum ocf_exitcode rc
Definition: lrmd.h:210
const char * get_cluster_pref(GHashTable *options, pe_cluster_option *option_list, int len, const char *name)
Definition: utils.c:363
char * crm_system_name
Definition: utils.c:74
#define cib_channel_rw
Definition: internal.h:80
#define XML_RSC_OP_T_QUEUE
Definition: msg_xml.h:286
#define XML_CIB_TAG_NODES
Definition: msg_xml.h:158
#define PACEMAKER_VERSION
Definition: config.h:527
#define attrd_opt_private
Definition: attrd.h:25
#define F_ATTRD_SET
Definition: crm_internal.h:279
#define ATTRD_OP_REFRESH
Definition: crm_internal.h:297
Wrappers for and extensions to glib mainloop.
char version[256]
Definition: plugin.c:84
void config_metadata(const char *name, const char *version, const char *desc_short, const char *desc_long, pe_cluster_option *option_list, int len)
Definition: utils.c:383
long crm_read_pidfile(const char *filename)
Definition: utils.c:1279
const char * crm_xml_replace(xmlNode *node, const char *name, const char *value)
Definition: xml.c:2744
#define CRM_BZ2_WORK
Definition: xml.h:49
#define ATTRD_OP_PEER_REMOVE
Definition: crm_internal.h:292
bool pcmk_acl_required(const char *user)
Definition: utils.c:2151
guint g_str_hash_traditional(gconstpointer v)
Definition: utils.c:2277
unsigned int exec_time
Definition: lrmd.h:220
void stonith_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb)
Definition: utils.c:1758
#define XML_ATTR_ORIGIN
Definition: msg_xml.h:95
#define CRMD_ACTION_START
Definition: crm.h:152
xmlNode * create_operation_update(xmlNode *parent, lrmd_event_data_t *op, const char *caller_version, int target_rc, const char *node, const char *origin, int level)
Definition: utils.c:1982
char * crm_meta_name(const char *field)
Definition: utils.c:1461
gboolean check_quorum(const char *value)
Definition: utils.c:193
#define XML_LRM_ATTR_TASK_KEY
Definition: msg_xml.h:264
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:263
void hash2field(gpointer key, gpointer value, gpointer user_data)
Definition: xml.c:5011
#define CRMD_ACTION_STOP
Definition: crm.h:155
void * params
Definition: lrmd.h:229
#define PW_BUFFER_LEN
Definition: utils.c:67
char * calculate_operation_digest(xmlNode *local_cib, const char *version)
Calculate and return digest of XML operation.
Definition: digest.c:175
#define crm_warn(fmt, args...)
Definition: logging.h:249
op_status
Definition: services.h:120
int crm_pid_active(long pid, const char *daemon)
Definition: utils.c:1216
int daemon(int nochdir, int noclose)
const char * exit_reason
Definition: lrmd.h:238
#define F_ATTRD_ATTRIBUTE
Definition: crm_internal.h:275
char * generate_hash_key(const char *crm_msg_reference, const char *sys)
Definition: utils.c:449
#define ATTRD_OP_UPDATE_BOTH
Definition: crm_internal.h:294
#define crm_debug(fmt, args...)
Definition: logging.h:253
void determine_request_user(const char *user, xmlNode *request, const char *field)
bool crm_is_daemon
Definition: logging.c:49
struct crm_ipc_s crm_ipc_t
Definition: ipc.h:61
Utility functions.
#define F_ATTRD_IS_PRIVATE
Definition: crm_internal.h:281
#define XML_ATTR_ID
Definition: msg_xml.h:100
#define CRM_DAEMON_DIR
Definition: config.h:41
char * score2char(int score)
Definition: utils.c:305
#define BUILD_VERSION
Definition: config.h:23
#define LOCKSTRLEN
Definition: utils.c:1276
#define pcmk_option_example
Definition: crm_internal.h:74
unsigned long long crm_get_interval(const char *input)
Definition: utils.c:751
#define INFINITY_S
Definition: crm.h:74
gboolean did_rsc_op_fail(lrmd_event_data_t *op, int target_rc)
Definition: utils.c:1958
#define crm_trace(fmt, args...)
Definition: logging.h:254
#define do_crm_log(level, fmt, args...)
Log a message.
Definition: logging.h:129
const char * cluster_option(GHashTable *options, gboolean(*validate)(const char *), const char *name, const char *old_name, const char *def_value)
Definition: utils.c:317
#define pcmk_option_paragraph
Definition: crm_internal.h:73
#define NUMCHARS
Definition: utils.c:743
int node_score_yellow
Definition: utils.c:78
Wrappers for and extensions to libxml2.
ISO_8601 Date handling.
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:2796
const char * crm_element_value(xmlNode *data, const char *name)
Definition: xml.c:5842
long long crm_int_helper(const char *text, char **end_text)
Definition: utils.c:625
char * crm_itoa(int an_int)
Definition: utils.c:469
gboolean crm_is_true(const char *s)
Definition: utils.c:711
#define CRM_DAEMON_USER
Definition: config.h:47
int crm_exit(int rc)
Definition: utils.c:87
#define F_ATTRD_TASK
Definition: crm_internal.h:277
#define F_ATTRD_IS_REMOTE
Definition: crm_internal.h:280
#define XML_LRM_ATTR_MIGRATE_TARGET
Definition: msg_xml.h:289
char * crm_generate_uuid(void)
Definition: utils.c:2361
#define XML_LRM_ATTR_EXIT_REASON
Definition: msg_xml.h:281
#define F_ORIG
Definition: msg_xml.h:22
#define do_crm_log_always(level, fmt, args...)
Log a message using constant severity.
Definition: logging.h:213
void free_xml(xmlNode *child)
Definition: xml.c:2851
#define EOS
Definition: crm.h:40
#define attrd_opt_remote
Definition: attrd.h:24
void filter_action_parameters(xmlNode *param_set, const char *version)
Definition: utils.c:1085
int * flag
Definition: crm_internal.h:86
int node_score_green
Definition: utils.c:77
CRM_TRACE_INIT_DATA(common)
const char * op_type
Definition: lrmd.h:194
#define WHITESPACE
Definition: utils.c:747
gboolean decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id, int *op_status, int *op_rc, int *target_rc)
Definition: utils.c:976
#define cib_channel_shm
Definition: internal.h:81
#define CRM_SYSTEM_CRMD
Definition: crm.h:84
long long int crm_time_get_seconds(crm_time_t *dt)
Definition: iso8601.c:269
#define DAEMON_RESPAWN_STOP
Definition: crm.h:67
int compare_version(const char *version1, const char *version2)
Definition: utils.c:536
unsigned int t_run
Definition: lrmd.h:216
gboolean check_script(const char *value)
Definition: utils.c:211
#define crm_config_warn(fmt...)
Definition: crm_internal.h:271
#define XML_ATTR_TRANSITION_KEY
Definition: msg_xml.h:358
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Definition: xml.c:2698
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Definition: xml.c:2786
unsigned int get_crm_log_level(void)
Definition: logging.c:920
#define ATTRD_OP_QUERY
Definition: crm_internal.h:296
void crm_abort(const char *file, const char *function, int line, const char *assert_condition, gboolean do_core, gboolean do_fork)
Definition: utils.c:1154
char * generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition: utils.c:920
gboolean check_timer(const char *value)
Definition: utils.c:124
long crm_pidfile_inuse(const char *filename, long mypid, const char *daemon)
Definition: utils.c:1316
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:226
#define CRM_META
Definition: crm.h:55
char * generate_transition_magic_v202(const char *transition_key, int op_status)
Definition: utils.c:942
int char2score(const char *score)
Definition: utils.c:253
#define crm_err(fmt, args...)
Definition: logging.h:248
#define FAKE_TE_ID
Definition: utils.c:1905
int crm_parse_int(const char *text, const char *default_text)
Definition: utils.c:671
int crm_ipc_send(crm_ipc_t *client, xmlNode *message, enum crm_ipc_flags flags, int32_t ms_timeout, xmlNode **reply)
Definition: ipc.c:1114
const char * bz2_strerror(int rc)
Definition: logging.c:1176
gboolean check_number(const char *value)
Definition: utils.c:172
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:4046
int rsc_op_expected_rc(lrmd_event_data_t *op)
Definition: utils.c:1943
crm_ipc_t * crm_ipc_new(const char *name, size_t max_size)
Definition: ipc.c:770
#define F_ATTRD_DAMPEN
Definition: crm_internal.h:283
char * dump_xml_unformatted(xmlNode *msg)
Definition: xml.c:3990
#define DIMOF(a)
Definition: crm.h:41
#define XML_LRM_ATTR_CALLID
Definition: msg_xml.h:275
#define CRMD_ACTION_MIGRATE
Definition: crm.h:149
#define CRM_ASSERT(expr)
Definition: error.h:35
char data[0]
Definition: internal.h:58
#define XML_LRM_ATTR_OPSTATUS
Definition: msg_xml.h:273
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:83
#define pcmk_option_hidden
Definition: crm_internal.h:72
int crm_get_option(int argc, char **argv, int *index)
Definition: utils.c:1587
char * generate_op_key(const char *rsc_id, const char *op_type, int interval)
Definition: utils.c:831
#define XML_LRM_ATTR_RC
Definition: msg_xml.h:274
#define MD5_DIGEST_SIZE
Definition: md5.h:26
void * md5_buffer(const char *buffer, size_t len, void *resblock)
Definition: md5.c:210
Wrappers for and extensions to libqb IPC.
#define PACKAGE_BUGREPORT
Definition: config.h:533
#define XML_CIB_TAG_STATUS
Definition: msg_xml.h:156
char * generate_transition_magic(const char *transition_key, int op_status, int op_rc)
Definition: utils.c:959
int crm_str_to_boolean(const char *s, int *ret)
Definition: utils.c:722
const char * crm_acl_get_set_user(xmlNode *request, const char *field, const char *peer_user)
pid_t pcmk_locate_sbd(void)
Definition: watchdog.c:215
gboolean crm_config_error
Definition: utils.c:72
#define CRM_BZ2_BLOCKS
Definition: xml.h:48
#define XML_LRM_ATTR_TARGET
Definition: msg_xml.h:265
#define XML_LRM_TAG_RSC_OP
Definition: msg_xml.h:235
#define XML_RSC_OP_LAST_RUN
Definition: msg_xml.h:284
#define ID(x)
Definition: msg_xml.h:419
#define XML_ACL_TAG_USER
Definition: msg_xml.h:368
#define safe_str_eq(a, b)
Definition: util.h:74
qb_ipcs_service_t * mainloop_add_ipc_server(const char *name, enum qb_ipc_type type, struct qb_ipcs_service_handlers *callbacks)
Definition: mainloop.c:587
#define XML_LRM_ATTR_MIGRATE_SOURCE
Definition: msg_xml.h:288
#define CRM_FEATURES
Definition: config.h:53
gboolean check_boolean(const char *value)
Definition: utils.c:161
crm_ipc_flags
Definition: ipc.h:41
void crm_ipc_close(crm_ipc_t *client)
Definition: ipc.c:829
#define ATTRD_OP_UPDATE_DELAY
Definition: crm_internal.h:295
char * crm_strip_trailing_newline(char *str)
Definition: utils.c:1425
#define XML_TAG_PARAMS
Definition: msg_xml.h:179
#define crm_info(fmt, args...)
Definition: logging.h:251
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: utils.c:1441
char * uid2username(uid_t uid)
#define cib_channel_ro
Definition: internal.h:79
char * score2char_stack(int score, char *buf, size_t len)
Definition: utils.c:291
uint64_t flags
Definition: remote.c:121
qb_ipcs_service_t * crmd_ipc_server_init(struct qb_ipcs_service_handlers *cb)
Definition: utils.c:1740
const char * name
Definition: crm_internal.h:79
enum crm_ais_msg_types type
Definition: internal.h:51
char * add_list_element(char *list, const char *value)
Definition: utils.c:2331
guint crm_strcase_hash(gconstpointer v)
Definition: utils.c:2289
#define CRMD_ACTION_STATUS
Definition: crm.h:166
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:115