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