pacemaker  2.1.0-7c3f660
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
services.c
Go to the documentation of this file.
1 /*
2  * Copyright 2010-2021 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 <sys/types.h>
17 #include <sys/stat.h>
18 #include <stdio.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <dirent.h>
22 #include <fcntl.h>
23 
24 #include <crm/crm.h>
25 #include <crm/common/mainloop.h>
26 #include <crm/services.h>
27 #include <crm/services_internal.h>
28 #include <crm/stonith-ng.h>
29 #include <crm/msg_xml.h>
30 #include "services_private.h"
31 #include "services_lsb.h"
32 
33 #if SUPPORT_UPSTART
34 # include <upstart.h>
35 #endif
36 
37 #if SUPPORT_SYSTEMD
38 # include <systemd.h>
39 #endif
40 
41 #if SUPPORT_NAGIOS
42 # include <services_nagios.h>
43 #endif
44 
45 /* TODO: Develop a rollover strategy */
46 
47 static int operations = 0;
48 static GHashTable *recurring_actions = NULL;
49 
50 /* ops waiting to run async because of conflicting active
51  * pending ops */
52 static GList *blocked_ops = NULL;
53 
54 /* ops currently active (in-flight) */
55 static GList *inflight_ops = NULL;
56 
57 static void handle_blocked_ops(void);
58 
70 const char *
71 resources_find_service_class(const char *agent)
72 {
73  if (services__lsb_agent_exists(agent)) {
75  }
76 
77 #if SUPPORT_SYSTEMD
78  if (systemd_unit_exists(agent)) {
80  }
81 #endif
82 
83 #if SUPPORT_UPSTART
84  if (upstart_job_exists(agent)) {
86  }
87 #endif
88  return NULL;
89 }
90 
91 static inline void
92 init_recurring_actions(void)
93 {
94  if (recurring_actions == NULL) {
95  recurring_actions = pcmk__strkey_table(NULL, NULL);
96  }
97 }
98 
107 static inline gboolean
108 inflight_systemd_or_upstart(svc_action_t *op)
109 {
112  g_list_find(inflight_ops, op) != NULL;
113 }
114 
127 static char *
128 expand_resource_class(const char *rsc, const char *standard, const char *agent)
129 {
130  char *expanded_class = NULL;
131 
132  if (strcasecmp(standard, PCMK_RESOURCE_CLASS_SERVICE) == 0) {
133  const char *found_class = resources_find_service_class(agent);
134 
135  if (found_class) {
136  crm_debug("Found %s agent %s for %s", found_class, agent, rsc);
137  expanded_class = strdup(found_class);
138  } else {
139  crm_info("Assuming resource class lsb for agent %s for %s",
140  agent, rsc);
141  expanded_class = strdup(PCMK_RESOURCE_CLASS_LSB);
142  }
143  } else {
144  expanded_class = strdup(standard);
145  }
146  CRM_ASSERT(expanded_class);
147  return expanded_class;
148 }
149 
150 #if SUPPORT_NAGIOS
151 
159 static char *
160 dup_file_path(const char *filename, const char *dirname)
161 {
162  return (*filename == '/')? strdup(filename)
163  : crm_strdup_printf("%s/%s", dirname, filename);
164 }
165 #endif
166 
167 svc_action_t *
168 services__create_resource_action(const char *name, const char *standard,
169  const char *provider, const char *agent,
170  const char *action, guint interval_ms, int timeout,
171  GHashTable *params, enum svc_action_flags flags)
172 {
173  svc_action_t *op = NULL;
174  uint32_t ra_caps = 0;
175 
176  /*
177  * Do some up front sanity checks before we go off and
178  * build the svc_action_t instance.
179  */
180 
181  if (pcmk__str_empty(name)) {
182  crm_err("Cannot create operation without resource name");
183  goto return_error;
184  }
185 
186  if (pcmk__str_empty(standard)) {
187  crm_err("Cannot create operation for %s without resource class", name);
188  goto return_error;
189  }
190  ra_caps = pcmk_get_ra_caps(standard);
191 
192  if (pcmk_is_set(ra_caps, pcmk_ra_cap_provider)
193  && pcmk__str_empty(provider)) {
194  crm_err("Cannot create operation for %s without provider", name);
195  goto return_error;
196  }
197 
198  if (pcmk__str_empty(agent)) {
199  crm_err("Cannot create operation for %s without agent name", name);
200  goto return_error;
201  }
202 
203  if (pcmk__str_empty(action)) {
204  crm_err("Cannot create operation for %s without operation name", name);
205  goto return_error;
206  }
207 
208  /*
209  * Sanity checks passed, proceed!
210  */
211 
212  op = calloc(1, sizeof(svc_action_t));
213  op->opaque = calloc(1, sizeof(svc_action_private_t));
214  op->rsc = strdup(name);
215  op->interval_ms = interval_ms;
216  op->timeout = timeout;
217  op->standard = expand_resource_class(name, standard, agent);
218  op->agent = strdup(agent);
219  op->sequence = ++operations;
220  op->flags = flags;
221  op->id = pcmk__op_key(name, action, interval_ms);
222 
223  if (pcmk_is_set(ra_caps, pcmk_ra_cap_status)
224  && pcmk__str_eq(action, "monitor", pcmk__str_casei)) {
225 
226  op->action = strdup("status");
227  } else {
228  op->action = strdup(action);
229  }
230 
231  if (pcmk_is_set(ra_caps, pcmk_ra_cap_provider)) {
232  op->provider = strdup(provider);
233  }
234 
235  if (pcmk_is_set(ra_caps, pcmk_ra_cap_params)) {
236  op->params = params;
237  params = NULL; // so we don't free them in this function
238  }
239 
240  if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
241  char *dirs = strdup(OCF_RA_PATH);
242  char *dir = NULL;
243  char *buf = NULL;
244  struct stat st;
245 
246  if (pcmk__str_empty(dirs)) {
247  free(dirs);
248  services__handle_exec_error(op, ENOMEM);
249  return op;
250  }
251 
252  for (dir = strtok(dirs, ":"); dir != NULL; dir = strtok(NULL, ":")) {
253  buf = crm_strdup_printf("%s/%s/%s", dir, provider, agent);
254  if (stat(buf, &st) == 0) {
255  break;
256  }
257  free(buf);
258  buf = NULL;
259  }
260 
261  free(dirs);
262 
263  if (buf) {
264  op->opaque->exec = buf;
265  } else {
266  services__handle_exec_error(op, ENOENT);
267  return op;
268  }
269 
270  op->opaque->args[0] = strdup(op->opaque->exec);
271  op->opaque->args[1] = strdup(op->action);
272 
273  } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_LSB) == 0) {
275  op->opaque->args[0] = strdup(op->opaque->exec);
276  op->opaque->args[1] = strdup(op->action);
277 
278 #if SUPPORT_SYSTEMD
279  } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
280  op->opaque->exec = strdup("systemd-dbus");
281 #endif
282 #if SUPPORT_UPSTART
283  } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_UPSTART) == 0) {
284  op->opaque->exec = strdup("upstart-dbus");
285 #endif
286 #if SUPPORT_NAGIOS
287  } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_NAGIOS) == 0) {
288  op->opaque->exec = dup_file_path(op->agent, NAGIOS_PLUGIN_DIR);
289  op->opaque->args[0] = strdup(op->opaque->exec);
290 
291  if (pcmk__str_eq(op->action, "monitor", pcmk__str_casei) && (op->interval_ms == 0)) {
292  /* Invoke --version for a nagios probe */
293  op->opaque->args[1] = strdup("--version");
294 
295  } else if (op->params) {
296  GHashTableIter iter;
297  char *key = NULL;
298  char *value = NULL;
299  int index = 1;
300  static int args_size = sizeof(op->opaque->args) / sizeof(char *);
301 
302  g_hash_table_iter_init(&iter, op->params);
303 
304  while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value) &&
305  index <= args_size - 3) {
306 
307  if (pcmk__str_eq(key, XML_ATTR_CRM_VERSION, pcmk__str_casei) || strstr(key, CRM_META "_")) {
308  continue;
309  }
310  op->opaque->args[index++] = crm_strdup_printf("--%s", key);
311  op->opaque->args[index++] = strdup(value);
312  }
313  }
314 
315  // Nagios actions don't need to keep the parameters
316  if (op->params != NULL) {
317  g_hash_table_destroy(op->params);
318  op->params = NULL;
319  }
320 #endif
321  } else {
322  crm_err("Unknown resource standard: %s", op->standard);
323  services__handle_exec_error(op, ENOENT);
324  }
325 
326  return_error:
327  if(params) {
328  g_hash_table_destroy(params);
329  }
330 
331  return op;
332 }
333 
334 svc_action_t *
335 resources_action_create(const char *name, const char *standard,
336  const char *provider, const char *agent,
337  const char *action, guint interval_ms, int timeout,
338  GHashTable *params, enum svc_action_flags flags)
339 {
340  svc_action_t *op = services__create_resource_action(name, standard,
341  provider, agent, action, interval_ms, timeout,
342  params, flags);
343  if (op == NULL || op->rc != 0) {
345  return NULL;
346  } else {
347  return op;
348  }
349 }
350 
351 svc_action_t *
352 services_action_create_generic(const char *exec, const char *args[])
353 {
354  svc_action_t *op;
355  unsigned int cur_arg;
356 
357  op = calloc(1, sizeof(*op));
358  op->opaque = calloc(1, sizeof(svc_action_private_t));
359 
360  op->opaque->exec = strdup(exec);
361  op->opaque->args[0] = strdup(exec);
362 
363  for (cur_arg = 1; args && args[cur_arg - 1]; cur_arg++) {
364  op->opaque->args[cur_arg] = strdup(args[cur_arg - 1]);
365 
366  if (cur_arg == PCMK__NELEM(op->opaque->args) - 1) {
367  crm_err("svc_action_t args list not long enough for '%s' execution request.", exec);
368  break;
369  }
370  }
371 
372  return op;
373 }
374 
389 svc_action_t *
390 services_alert_create(const char *id, const char *exec, int timeout,
391  GHashTable *params, int sequence, void *cb_data)
392 {
394 
395  CRM_ASSERT(action);
396  action->timeout = timeout;
397  action->id = strdup(id);
398  action->params = params;
399  action->sequence = sequence;
400  action->cb_data = cb_data;
401  return action;
402 }
403 
419 int
420 services_action_user(svc_action_t *op, const char *user)
421 {
422  CRM_CHECK((op != NULL) && (user != NULL), return -EINVAL);
423  return crm_user_lookup(user, &(op->opaque->uid), &(op->opaque->gid));
424 }
425 
437 gboolean
439 {
440  action->synchronous = false;
441  action->opaque->callback = cb;
442  return services_os_action_execute(action);
443 }
444 
445 #if SUPPORT_DBUS
446 
453 void
454 services_set_op_pending(svc_action_t *op, DBusPendingCall *pending)
455 {
456  if (op->opaque->pending && (op->opaque->pending != pending)) {
457  if (pending) {
458  crm_info("Lost pending %s DBus call (%p)", op->id, op->opaque->pending);
459  } else {
460  crm_trace("Done with pending %s DBus call (%p)", op->id, op->opaque->pending);
461  }
462  dbus_pending_call_unref(op->opaque->pending);
463  }
464  op->opaque->pending = pending;
465  if (pending) {
466  crm_trace("Updated pending %s DBus call (%p)", op->id, pending);
467  } else {
468  crm_trace("Cleared pending %s DBus call", op->id);
469  }
470 }
471 #endif
472 
473 void
475 {
476  if ((op == NULL) || (op->opaque == NULL)) {
477  return;
478  }
479 
480 #if SUPPORT_DBUS
481  if(op->opaque->timerid != 0) {
482  crm_trace("Removing timer for call %s to %s", op->action, op->rsc);
483  g_source_remove(op->opaque->timerid);
484  op->opaque->timerid = 0;
485  }
486 
487  if(op->opaque->pending) {
488  if (dbus_pending_call_get_completed(op->opaque->pending)) {
489  // This should never be the case
490  crm_warn("Result of %s op %s was unhandled",
491  op->standard, op->id);
492  } else {
493  crm_debug("Will ignore any result of canceled %s op %s",
494  op->standard, op->id);
495  }
496  dbus_pending_call_cancel(op->opaque->pending);
497  services_set_op_pending(op, NULL);
498  }
499 #endif
500 
501  if (op->opaque->stderr_gsource) {
503  op->opaque->stderr_gsource = NULL;
504  }
505 
506  if (op->opaque->stdout_gsource) {
508  op->opaque->stdout_gsource = NULL;
509  }
510 }
511 
512 void
514 {
515  unsigned int i;
516 
517  if (op == NULL) {
518  return;
519  }
520 
521  /* The operation should be removed from all tracking lists by this point.
522  * If it's not, we have a bug somewhere, so bail. That may lead to a
523  * memory leak, but it's better than a use-after-free segmentation fault.
524  */
525  CRM_CHECK(g_list_find(inflight_ops, op) == NULL, return);
526  CRM_CHECK(g_list_find(blocked_ops, op) == NULL, return);
527  CRM_CHECK((recurring_actions == NULL)
528  || (g_hash_table_lookup(recurring_actions, op->id) == NULL),
529  return);
530 
532 
533  if (op->opaque->repeat_timer) {
534  g_source_remove(op->opaque->repeat_timer);
535  op->opaque->repeat_timer = 0;
536  }
537 
538  free(op->id);
539  free(op->opaque->exec);
540 
541  for (i = 0; i < PCMK__NELEM(op->opaque->args); i++) {
542  free(op->opaque->args[i]);
543  }
544 
545  free(op->opaque);
546  free(op->rsc);
547  free(op->action);
548 
549  free(op->standard);
550  free(op->agent);
551  free(op->provider);
552 
553  free(op->stdout_data);
554  free(op->stderr_data);
555 
556  if (op->params) {
557  g_hash_table_destroy(op->params);
558  op->params = NULL;
559  }
560 
561  free(op);
562 }
563 
564 gboolean
566 {
567  crm_info("Cancelling %s operation %s", op->standard, op->id);
568 
569  if (recurring_actions) {
570  g_hash_table_remove(recurring_actions, op->id);
571  }
572 
573  if (op->opaque->repeat_timer) {
574  g_source_remove(op->opaque->repeat_timer);
575  op->opaque->repeat_timer = 0;
576  }
577 
578  return TRUE;
579 }
580 
590 gboolean
591 services_action_cancel(const char *name, const char *action, guint interval_ms)
592 {
593  gboolean cancelled = FALSE;
594  char *id = pcmk__op_key(name, action, interval_ms);
595  svc_action_t *op = NULL;
596 
597  /* We can only cancel a recurring action */
598  init_recurring_actions();
599  op = g_hash_table_lookup(recurring_actions, id);
600  if (op == NULL) {
601  goto done;
602  }
603 
604  /* Tell operation_finalize() not to reschedule the operation */
605  op->cancel = TRUE;
606 
607  /* Stop tracking it as a recurring operation, and stop its repeat timer */
609 
610  /* If the op has a PID, it's an in-flight child process, so kill it.
611  *
612  * Whether the kill succeeds or fails, the main loop will send the op to
613  * operation_finished() (and thus operation_finalize()) when the process
614  * goes away.
615  */
616  if (op->pid != 0) {
617  crm_info("Terminating in-flight op %s[%d] early because it was cancelled",
618  id, op->pid);
619  cancelled = mainloop_child_kill(op->pid);
620  if (cancelled == FALSE) {
621  crm_err("Termination of %s[%d] failed", id, op->pid);
622  }
623  goto done;
624  }
625 
626 #if SUPPORT_DBUS
627  // In-flight systemd and upstart ops don't have a pid
628  if (inflight_systemd_or_upstart(op)) {
629  inflight_ops = g_list_remove(inflight_ops, op);
630 
631  /* This will cause any result that comes in later to be discarded, so we
632  * don't call the callback and free the operation twice.
633  */
635  }
636 #endif
637 
638  // The rest of this is essentially equivalent to operation_finalize(),
639  // except without calling handle_blocked_ops()
640 
641  // Report operation as cancelled
643  if (op->opaque->callback) {
644  op->opaque->callback(op);
645  }
646 
647  blocked_ops = g_list_remove(blocked_ops, op);
649  cancelled = TRUE;
650  // @TODO Initiate handle_blocked_ops() asynchronously
651 
652 done:
653  free(id);
654  return cancelled;
655 }
656 
657 gboolean
658 services_action_kick(const char *name, const char *action, guint interval_ms)
659 {
660  svc_action_t * op = NULL;
661  char *id = pcmk__op_key(name, action, interval_ms);
662 
663  init_recurring_actions();
664  op = g_hash_table_lookup(recurring_actions, id);
665  free(id);
666 
667  if (op == NULL) {
668  return FALSE;
669  }
670 
671 
672  if (op->pid || inflight_systemd_or_upstart(op)) {
673  return TRUE;
674  } else {
675  if (op->opaque->repeat_timer) {
676  g_source_remove(op->opaque->repeat_timer);
677  op->opaque->repeat_timer = 0;
678  }
680  return TRUE;
681  }
682 
683 }
684 
693 static gboolean
694 handle_duplicate_recurring(svc_action_t * op)
695 {
696  svc_action_t * dup = NULL;
697 
698  /* check for duplicates */
699  dup = g_hash_table_lookup(recurring_actions, op->id);
700 
701  if (dup && (dup != op)) {
702  /* update user data */
703  if (op->opaque->callback) {
704  dup->opaque->callback = op->opaque->callback;
705  dup->cb_data = op->cb_data;
706  op->cb_data = NULL;
707  }
708  /* immediately execute the next interval */
709  if (dup->pid != 0) {
710  if (op->opaque->repeat_timer) {
711  g_source_remove(op->opaque->repeat_timer);
712  op->opaque->repeat_timer = 0;
713  }
715  }
716  /* free the duplicate */
718  return TRUE;
719  }
720 
721  return FALSE;
722 }
723 
724 inline static gboolean
725 action_exec_helper(svc_action_t * op)
726 {
727  /* Whether a/synchronous must be decided (op->synchronous) beforehand. */
728  if (op->standard
729  && (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_UPSTART) == 0)) {
730 #if SUPPORT_UPSTART
731  return upstart_job_exec(op);
732 #endif
733  } else if (op->standard && strcasecmp(op->standard,
735 #if SUPPORT_SYSTEMD
736  return systemd_unit_exec(op);
737 #endif
738  } else {
739  return services_os_action_execute(op);
740  }
741  /* The 'op' has probably been freed if the execution functions return TRUE
742  for the asynchronous 'op'. */
743  /* Avoid using the 'op' in here. */
744 
745  return FALSE;
746 }
747 
748 void
750 {
751  if (op == NULL) {
752  return;
753  }
754 
755  CRM_ASSERT(op->synchronous == FALSE);
756 
757  /* keep track of ops that are in-flight to avoid collisions in the same namespace */
758  if (op->rsc) {
759  inflight_ops = g_list_append(inflight_ops, op);
760  }
761 }
762 
769 void
771 {
772  /* Op is no longer in-flight or blocked */
773  inflight_ops = g_list_remove(inflight_ops, op);
774  blocked_ops = g_list_remove(blocked_ops, op);
775 
776  /* Op is no longer blocking other ops, so check if any need to run */
777  handle_blocked_ops();
778 }
779 
780 gboolean
782  void (*action_callback) (svc_action_t *),
783  void (*action_fork_callback) (svc_action_t *))
784 {
785  op->synchronous = false;
786  if (action_callback) {
787  op->opaque->callback = action_callback;
788  }
789  if (action_fork_callback) {
790  op->opaque->fork_callback = action_fork_callback;
791  }
792 
793  if (op->interval_ms > 0) {
794  init_recurring_actions();
795  if (handle_duplicate_recurring(op) == TRUE) {
796  /* entry rescheduled, dup freed */
797  /* exit early */
798  return TRUE;
799  }
800  g_hash_table_replace(recurring_actions, op->id, op);
801  }
802 
804  && op->rsc && is_op_blocked(op->rsc)) {
805  blocked_ops = g_list_append(blocked_ops, op);
806  return TRUE;
807  }
808 
809  return action_exec_helper(op);
810 }
811 
812 gboolean
814  void (*action_callback) (svc_action_t *))
815 {
816  return services_action_async_fork_notify(op, action_callback, NULL);
817 }
818 
819 static gboolean processing_blocked_ops = FALSE;
820 
821 gboolean
822 is_op_blocked(const char *rsc)
823 {
824  GList *gIter = NULL;
825  svc_action_t *op = NULL;
826 
827  for (gIter = inflight_ops; gIter != NULL; gIter = gIter->next) {
828  op = gIter->data;
829  if (pcmk__str_eq(op->rsc, rsc, pcmk__str_casei)) {
830  return TRUE;
831  }
832  }
833 
834  return FALSE;
835 }
836 
837 static void
838 handle_blocked_ops(void)
839 {
840  GList *executed_ops = NULL;
841  GList *gIter = NULL;
842  svc_action_t *op = NULL;
843  gboolean res = FALSE;
844 
845  if (processing_blocked_ops) {
846  /* avoid nested calling of this function */
847  return;
848  }
849 
850  processing_blocked_ops = TRUE;
851 
852  /* n^2 operation here, but blocked ops are incredibly rare. this list
853  * will be empty 99% of the time. */
854  for (gIter = blocked_ops; gIter != NULL; gIter = gIter->next) {
855  op = gIter->data;
856  if (is_op_blocked(op->rsc)) {
857  continue;
858  }
859  executed_ops = g_list_append(executed_ops, op);
860  res = action_exec_helper(op);
861  if (res == FALSE) {
863  /* this can cause this function to be called recursively
864  * which is why we have processing_blocked_ops static variable */
865  operation_finalize(op);
866  }
867  }
868 
869  for (gIter = executed_ops; gIter != NULL; gIter = gIter->next) {
870  op = gIter->data;
871  blocked_ops = g_list_remove(blocked_ops, op);
872  }
873  g_list_free(executed_ops);
874 
875  processing_blocked_ops = FALSE;
876 }
877 
878 static gboolean
879 action_get_metadata(svc_action_t *op)
880 {
881  const char *class = op->standard;
882 
883  if (op->agent == NULL) {
884  crm_err("meta-data requested without specifying agent");
885  return FALSE;
886  }
887 
888  if (class == NULL) {
889  crm_err("meta-data requested for agent %s without specifying class",
890  op->agent);
891  return FALSE;
892  }
893 
894  if (!strcmp(class, PCMK_RESOURCE_CLASS_SERVICE)) {
895  class = resources_find_service_class(op->agent);
896  }
897 
898  if (class == NULL) {
899  crm_err("meta-data requested for %s, but could not determine class",
900  op->agent);
901  return FALSE;
902  }
903 
904  if (pcmk__str_eq(class, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei)) {
905  return (services__get_lsb_metadata(op->agent, &op->stdout_data) >= 0);
906  }
907 
908 #if SUPPORT_NAGIOS
909  if (pcmk__str_eq(class, PCMK_RESOURCE_CLASS_NAGIOS, pcmk__str_casei)) {
910  return services__get_nagios_metadata(op->agent, &op->stdout_data) >= 0;
911  }
912 #endif
913 
914  return action_exec_helper(op);
915 }
916 
917 gboolean
919 {
920  gboolean rc = TRUE;
921 
922  if (op == NULL) {
923  crm_trace("No operation to execute");
924  return FALSE;
925  }
926 
927  op->synchronous = true;
928 
929  if (pcmk__str_eq(op->action, "meta-data", pcmk__str_casei)) {
930  /* Synchronous meta-data operations are handled specially. Since most
931  * resource classes don't provide any meta-data, it has to be
932  * synthesized from available information about the agent.
933  *
934  * services_action_async() doesn't treat meta-data actions specially, so
935  * it will result in an error for classes that don't support the action.
936  */
937  rc = action_get_metadata(op);
938  } else {
939  rc = action_exec_helper(op);
940  }
941  crm_trace(" > " PCMK__OP_FMT ": %s = %d",
942  op->rsc, op->action, op->interval_ms, op->opaque->exec, op->rc);
943  if (op->stdout_data) {
944  crm_trace(" > stdout: %s", op->stdout_data);
945  }
946  if (op->stderr_data) {
947  crm_trace(" > stderr: %s", op->stderr_data);
948  }
949  return rc;
950 }
951 
952 GList *
953 get_directory_list(const char *root, gboolean files, gboolean executable)
954 {
955  return services_os_get_directory_list(root, files, executable);
956 }
957 
958 GList *
960 {
961  GList *standards = NULL;
962  GList *agents = NULL;
963 
964  standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_OCF));
965  standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_LSB));
966  standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_SERVICE));
967 
968 #if SUPPORT_SYSTEMD
969  agents = systemd_unit_listall();
970  if (agents) {
971  standards = g_list_append(standards,
973  g_list_free_full(agents, free);
974  }
975 #endif
976 
977 #if SUPPORT_UPSTART
978  agents = upstart_job_listall();
979  if (agents) {
980  standards = g_list_append(standards,
982  g_list_free_full(agents, free);
983  }
984 #endif
985 
986 #if SUPPORT_NAGIOS
987  agents = services__list_nagios_agents();
988  if (agents) {
989  standards = g_list_append(standards,
991  g_list_free_full(agents, free);
992  }
993 #endif
994 
995  return standards;
996 }
997 
998 GList *
999 resources_list_providers(const char *standard)
1000 {
1003  }
1004 
1005  return NULL;
1006 }
1007 
1008 GList *
1009 resources_list_agents(const char *standard, const char *provider)
1010 {
1011  if ((standard == NULL)
1012  || (strcasecmp(standard, PCMK_RESOURCE_CLASS_SERVICE) == 0)) {
1013 
1014  GList *tmp1;
1015  GList *tmp2;
1016  GList *result = services__list_lsb_agents();
1017 
1018  if (standard == NULL) {
1019  tmp1 = result;
1020  tmp2 = resources_os_list_ocf_agents(NULL);
1021  if (tmp2) {
1022  result = g_list_concat(tmp1, tmp2);
1023  }
1024  }
1025 #if SUPPORT_SYSTEMD
1026  tmp1 = result;
1027  tmp2 = systemd_unit_listall();
1028  if (tmp2) {
1029  result = g_list_concat(tmp1, tmp2);
1030  }
1031 #endif
1032 
1033 #if SUPPORT_UPSTART
1034  tmp1 = result;
1035  tmp2 = upstart_job_listall();
1036  if (tmp2) {
1037  result = g_list_concat(tmp1, tmp2);
1038  }
1039 #endif
1040 
1041  return result;
1042 
1043  } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
1044  return resources_os_list_ocf_agents(provider);
1045  } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_LSB) == 0) {
1046  return services__list_lsb_agents();
1047 #if SUPPORT_SYSTEMD
1048  } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
1049  return systemd_unit_listall();
1050 #endif
1051 #if SUPPORT_UPSTART
1052  } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_UPSTART) == 0) {
1053  return upstart_job_listall();
1054 #endif
1055 #if SUPPORT_NAGIOS
1056  } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_NAGIOS) == 0) {
1058 #endif
1059  }
1060 
1061  return NULL;
1062 }
1063 
1064 gboolean
1065 resources_agent_exists(const char *standard, const char *provider, const char *agent)
1066 {
1067  GList *standards = NULL;
1068  GList *providers = NULL;
1069  GList *iter = NULL;
1070  gboolean rc = FALSE;
1071  gboolean has_providers = FALSE;
1072 
1073  standards = resources_list_standards();
1074  for (iter = standards; iter != NULL; iter = iter->next) {
1075  if (pcmk__str_eq(iter->data, standard, pcmk__str_none)) {
1076  rc = TRUE;
1077  break;
1078  }
1079  }
1080 
1081  if (rc == FALSE) {
1082  goto done;
1083  }
1084 
1085  rc = FALSE;
1086 
1087  has_providers = pcmk_is_set(pcmk_get_ra_caps(standard), pcmk_ra_cap_provider);
1088  if (has_providers == TRUE && provider != NULL) {
1089  providers = resources_list_providers(standard);
1090  for (iter = providers; iter != NULL; iter = iter->next) {
1091  if (pcmk__str_eq(iter->data, provider, pcmk__str_none)) {
1092  rc = TRUE;
1093  break;
1094  }
1095  }
1096  } else if (has_providers == FALSE && provider == NULL) {
1097  rc = TRUE;
1098  }
1099 
1100  if (rc == FALSE) {
1101  goto done;
1102  }
1103 
1104  if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_SERVICE, pcmk__str_casei)) {
1105  if (services__lsb_agent_exists(agent)) {
1106  rc = TRUE;
1107 #if SUPPORT_SYSTEMD
1108  } else if (systemd_unit_exists(agent)) {
1109  rc = TRUE;
1110 #endif
1111 
1112 #if SUPPORT_UPSTART
1113  } else if (upstart_job_exists(agent)) {
1114  rc = TRUE;
1115 #endif
1116  } else {
1117  rc = FALSE;
1118  }
1119 
1120  } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_OCF, pcmk__str_casei)) {
1121  rc = services__ocf_agent_exists(provider, agent);
1122 
1123  } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei)) {
1124  rc = services__lsb_agent_exists(agent);
1125 
1126 #if SUPPORT_SYSTEMD
1127  } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_SYSTEMD, pcmk__str_casei)) {
1128  rc = systemd_unit_exists(agent);
1129 #endif
1130 
1131 #if SUPPORT_UPSTART
1132  } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_UPSTART, pcmk__str_casei)) {
1133  rc = upstart_job_exists(agent);
1134 #endif
1135 
1136 #if SUPPORT_NAGIOS
1137  } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_NAGIOS, pcmk__str_casei)) {
1138  rc = services__nagios_agent_exists(agent);
1139 #endif
1140 
1141  } else {
1142  rc = FALSE;
1143  }
1144 
1145 done:
1146  g_list_free(standards);
1147  g_list_free(providers);
1148  return rc;
1149 }
Services API.
gboolean services_action_cancel(const char *name, const char *action, guint interval_ms)
Cancel a recurring action.
Definition: services.c:591
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:218
A dumping ground.
void services_action_free(svc_action_t *op)
Definition: services.c:513
gboolean services__ocf_agent_exists(const char *provider, const char *agent)
guint interval_ms
Definition: services.h:123
char * standard
Definition: services.h:125
gboolean services_action_kick(const char *name, const char *action, guint interval_ms)
Definition: services.c:658
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:929
gboolean upstart_job_exists(const char *name)
Definition: upstart.c:231
gboolean mainloop_child_kill(pid_t pid)
Definition: mainloop.c:1215
char * id
Definition: services.h:120
mainloop_io_t * stderr_gsource
const char * resources_find_service_class(const char *agent)
Find first service class that can provide a specified agent.
Definition: services.c:71
GList * resources_os_list_ocf_agents(const char *provider)
GList * resources_os_list_ocf_providers(void)
#define PCMK_RESOURCE_CLASS_SYSTEMD
Definition: services.h:46
gboolean recurring_action_timer(gpointer data)
svc_action_t * services_action_create_generic(const char *exec, const char *args[])
Definition: services.c:352
void(* fork_callback)(svc_action_t *op)
int services__get_nagios_metadata(const char *type, char **output)
gboolean services_action_async_fork_notify(svc_action_t *op, void(*action_callback)(svc_action_t *), void(*action_fork_callback)(svc_action_t *))
Definition: services.c:781
GList * services_os_get_directory_list(const char *root, gboolean files, gboolean executable)
svc_action_t * resources_action_create(const char *name, const char *standard, const char *provider, const char *agent, const char *action, guint interval_ms, int timeout, GHashTable *params, enum svc_action_flags flags)
Create a new resource action.
Definition: services.c:335
char * rsc
Definition: services.h:121
int crm_user_lookup(const char *name, uid_t *uid, gid_t *gid)
Definition: utils.c:129
gboolean services_os_action_execute(svc_action_t *op)
svc_action_flags
Definition: services.h:112
gboolean upstart_job_exec(svc_action_t *op)
Definition: upstart.c:418
gboolean is_op_blocked(const char *rsc)
Definition: services.c:822
Wrappers for and extensions to glib mainloop.
char * services__lsb_agent_path(const char *agent)
Definition: services_lsb.c:251
const char * action
Definition: pcmk_fence.c:30
int services__get_lsb_metadata(const char *type, char **output)
Definition: services_lsb.c:102
void services_action_cleanup(svc_action_t *op)
Definition: services.c:474
GList * services__list_lsb_agents(void)
Definition: services_lsb.c:245
enum svc_action_flags flags
Definition: services.h:142
#define crm_warn(fmt, args...)
Definition: logging.h:351
#define PCMK_RESOURCE_CLASS_OCF
Definition: services.h:43
gboolean cancel_recurring_action(svc_action_t *op)
Definition: services.c:565
int rc
Definition: pcmk_fence.c:35
svc_action_private_t * opaque
Definition: services.h:155
GList * upstart_job_listall(void)
Definition: upstart.c:149
#define crm_debug(fmt, args...)
Definition: logging.h:355
svc_action_t * services_alert_create(const char *id, const char *exec, int timeout, GHashTable *params, int sequence, void *cb_data)
Create an alert agent action.
Definition: services.c:390
gboolean operation_finalize(svc_action_t *op)
svc_action_t * services__create_resource_action(const char *name, const char *standard, const char *provider, const char *agent, const char *action, guint interval_ms, int timeout, GHashTable *params, enum svc_action_flags flags)
Create a new resource action.
Definition: services.c:168
gboolean services_action_sync(svc_action_t *op)
Definition: services.c:918
char * stdout_data
Definition: services.h:145
gboolean systemd_unit_exists(const char *name)
Definition: systemd.c:491
#define PCMK_RESOURCE_CLASS_SERVICE
Definition: services.h:44
GHashTable * params
Definition: services.h:130
#define crm_trace(fmt, args...)
Definition: logging.h:356
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:114
void(* callback)(svc_action_t *op)
char * agent
Definition: services.h:127
gboolean services__nagios_agent_exists(const char *name)
int synchronous
Definition: services.h:141
#define PCMK__NELEM(a)
Definition: internal.h:38
GList * resources_list_providers(const char *standard)
Get a list of providers.
Definition: services.c:999
int sequence
Definition: services.h:139
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition: operations.c:45
GList * systemd_unit_listall(void)
Definition: systemd.c:394
int services_action_user(svc_action_t *op, const char *user)
Set the user and group that an action will execute as.
Definition: services.c:420
char * args[MAX_ARGC]
void services_add_inflight_op(svc_action_t *op)
Definition: services.c:749
void services_untrack_op(svc_action_t *op)
Definition: services.c:770
char * action
Definition: services.h:122
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:610
#define PCMK_RESOURCE_CLASS_NAGIOS
Definition: services.h:48
#define PCMK_RESOURCE_CLASS_LSB
Definition: services.h:45
#define NAGIOS_PLUGIN_DIR
Definition: config.h:463
#define CRM_META
Definition: crm.h:78
GList * resources_list_standards(void)
Definition: services.c:959
#define crm_err(fmt, args...)
Definition: logging.h:350
#define CRM_ASSERT(expr)
Definition: results.h:42
GList * get_directory_list(const char *root, gboolean files, gboolean executable)
Get a list of files or directories in a given path.
Definition: services.c:953
#define PCMK_RESOURCE_CLASS_UPSTART
Definition: services.h:47
Fencing aka. STONITH.
GList * services__list_nagios_agents(void)
void services__handle_exec_error(svc_action_t *op, int error)
gboolean resources_agent_exists(const char *standard, const char *provider, const char *agent)
Definition: services.c:1065
GList * resources_list_agents(const char *standard, const char *provider)
Get a list of resource agents.
Definition: services.c:1009
mainloop_io_t * stdout_gsource
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:112
gboolean services_alert_async(svc_action_t *action, void(*cb)(svc_action_t *op))
Execute an alert agent action.
Definition: services.c:438
void mainloop_del_fd(mainloop_io_t *client)
Definition: mainloop.c:1019
void * cb_data
Definition: services.h:153
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition: agents.c:31
#define PCMK__OP_FMT
Definition: internal.h:168
gboolean services_action_async(svc_action_t *op, void(*action_callback)(svc_action_t *))
Definition: services.c:813
char * name
Definition: pcmk_fence.c:31
unsigned int timeout
Definition: pcmk_fence.c:32
char * provider
Definition: services.h:126
gboolean systemd_unit_exec(svc_action_t *op)
Definition: systemd.c:834
#define crm_info(fmt, args...)
Definition: logging.h:353
uint64_t flags
Definition: remote.c:149
bool services__lsb_agent_exists(const char *agent)
Definition: services_lsb.c:258
#define OCF_RA_PATH
Definition: config.h:469
char * stderr_data
Definition: services.h:144