1 /*
2  * Copyright 2010-2019 Andrew Beekhof <andrew@beekhof.net>
3  *
4  * This source code is licensed under the GNU Lesser General Public License
5  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
6  */
7 
8 #include <crm_internal.h>
9 
10 #ifndef _GNU_SOURCE
11 #  define _GNU_SOURCE
12 #endif
13 
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <stdio.h>
17 
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/msg_xml.h>
27 #include "services_private.h"
28 
29 #if SUPPORT_UPSTART
30 #  include <upstart.h>
31 #endif
32 
33 #if SUPPORT_SYSTEMD
34 #  include <systemd.h>
35 #endif
36 
37 /* TODO: Develop a rollover strategy */
38 
39 static int operations = 0;
40 static GHashTable *recurring_actions = NULL;
41 
42 /* ops waiting to run async because of conflicting active
43  * pending ops */
44 static GList *blocked_ops = NULL;
45 
46 /* ops currently active (in-flight) */
47 static GList *inflight_ops = NULL;
48 
49 static void handle_blocked_ops(void);
50 
51 svc_action_t *
services_action_create(const char * name,const char * action,int interval,int timeout)52 services_action_create(const char *name, const char *action, int interval, int timeout)
53 {
54     return resources_action_create(name, PCMK_RESOURCE_CLASS_LSB, NULL, name,
55                                    action, interval, timeout, NULL, 0);
56 }
57 
58 static char *
services__lsb_agent_path(const char * agent)59 services__lsb_agent_path(const char *agent)
60 {
61     return (*agent == '/')? strdup(agent)
62         : crm_strdup_printf("%s/%s", LSB_ROOT_DIR, agent);
63 }
64 
65 static bool
services__lsb_agent_exists(const char * agent)66 services__lsb_agent_exists(const char *agent)
67 {
68     bool rc = FALSE;
69     struct stat st;
70     char *path = services__lsb_agent_path(agent);
71 
72     rc = (stat(path, &st) == 0);
73     free(path);
74     return rc;
75 }
76 
77 /*!
78  * \brief Find first service class that can provide a specified agent
79  *
80  * \param[in] agent  Name of agent to search for
81  *
82  * \return Service class if found, NULL otherwise
83  *
84  * \note The priority is LSB, then systemd, then upstart. It would be preferable
85  *       to put systemd first, but LSB merely requires a file existence check,
86  *       while systemd requires contacting D-Bus.
87  */
88 const char *
resources_find_service_class(const char * agent)89 resources_find_service_class(const char *agent)
90 {
91     /* Priority is:
92      * - lsb
93      * - systemd
94      * - upstart
95      */
96     if (services__lsb_agent_exists(agent)) {
97         return PCMK_RESOURCE_CLASS_LSB;
98     }
99 
100 #if SUPPORT_SYSTEMD
101     if (systemd_unit_exists(agent)) {
102         return PCMK_RESOURCE_CLASS_SYSTEMD;
103     }
104 #endif
105 
106 #if SUPPORT_UPSTART
107     if (upstart_job_exists(agent)) {
108         return PCMK_RESOURCE_CLASS_UPSTART;
109     }
110 #endif
111     return NULL;
112 }
113 
114 static inline void
init_recurring_actions(void)115 init_recurring_actions(void)
116 {
117     if (recurring_actions == NULL) {
118         recurring_actions = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
119                                                   NULL);
120     }
121 }
122 
123 /*!
124  * \internal
125  * \brief Check whether op is in-flight systemd or upstart op
126  *
127  * \param[in] op  Operation to check
128  *
129  * \return TRUE if op is in-flight systemd or upstart op
130  */
131 static inline gboolean
inflight_systemd_or_upstart(svc_action_t * op)132 inflight_systemd_or_upstart(svc_action_t *op)
133 {
134     return (safe_str_eq(op->standard, PCMK_RESOURCE_CLASS_SYSTEMD)
135             || safe_str_eq(op->standard, PCMK_RESOURCE_CLASS_UPSTART))
136             && (g_list_find(inflight_ops, op) != NULL);
137 }
138 
139 /*!
140  * \internal
141  * \brief Expand "service" alias to an actual resource class
142  *
143  * \param[in] rsc       Resource name (for logging only)
144  * \param[in] standard  Resource class as configured
145  * \param[in] agent     Agent name to look for
146  *
147  * \return Newly allocated string with actual resource class
148  *
149  * \note The caller is responsible for calling free() on the result.
150  */
151 static char *
expand_resource_class(const char * rsc,const char * standard,const char * agent)152 expand_resource_class(const char *rsc, const char *standard, const char *agent)
153 {
154     char *expanded_class = NULL;
155 
156     if (strcasecmp(standard, PCMK_RESOURCE_CLASS_SERVICE) == 0) {
157         const char *found_class = resources_find_service_class(agent);
158 
159         if (found_class) {
160             crm_debug("Found %s agent %s for %s", found_class, agent, rsc);
161             expanded_class = strdup(found_class);
162         } else {
163             crm_info("Assuming resource class lsb for agent %s for %s",
164                      agent, rsc);
165             expanded_class = strdup(PCMK_RESOURCE_CLASS_LSB);
166         }
167     } else {
168         expanded_class = strdup(standard);
169     }
170     CRM_ASSERT(expanded_class);
171     return expanded_class;
172 }
173 
174 svc_action_t *
resources_action_create(const char * name,const char * standard,const char * provider,const char * agent,const char * action,int interval,int timeout,GHashTable * params,enum svc_action_flags flags)175 resources_action_create(const char *name, const char *standard, const char *provider,
176                         const char *agent, const char *action, int interval, int timeout,
177                         GHashTable * params, enum svc_action_flags flags)
178 {
179     svc_action_t *op = NULL;
180     uint32_t ra_caps = 0;
181 
182     /*
183      * Do some up front sanity checks before we go off and
184      * build the svc_action_t instance.
185      */
186 
187     if (crm_strlen_zero(name)) {
188         crm_err("Cannot create operation without resource name");
189         goto return_error;
190     }
191 
192     if (crm_strlen_zero(standard)) {
193         crm_err("Cannot create operation for %s without resource class", name);
194         goto return_error;
195     }
196     ra_caps = pcmk_get_ra_caps(standard);
197 
198     if (is_set(ra_caps, pcmk_ra_cap_provider) && crm_strlen_zero(provider)) {
199         crm_err("Cannot create operation for %s without provider", name);
200         goto return_error;
201     }
202 
203     if (crm_strlen_zero(agent)) {
204         crm_err("Cannot create operation for %s without agent name", name);
205         goto return_error;
206     }
207 
208     if (crm_strlen_zero(action)) {
209         crm_err("Cannot create operation for %s without operation name", name);
210         goto return_error;
211     }
212 
213     /*
214      * Sanity checks passed, proceed!
215      */
216 
217     op = calloc(1, sizeof(svc_action_t));
218     op->opaque = calloc(1, sizeof(svc_action_private_t));
219     op->rsc = strdup(name);
220     op->interval = interval;
221     op->timeout = timeout;
222     op->standard = expand_resource_class(name, standard, agent);
223     op->agent = strdup(agent);
224     op->sequence = ++operations;
225     op->flags = flags;
226     op->id = generate_op_key(name, action, interval);
227 
228     if (is_set(ra_caps, pcmk_ra_cap_status) && safe_str_eq(action, "monitor")) {
229         op->action = strdup("status");
230     } else {
231         op->action = strdup(action);
232     }
233 
234     if (is_set(ra_caps, pcmk_ra_cap_provider)) {
235         op->provider = strdup(provider);
236     }
237 
238     if (is_set(ra_caps, pcmk_ra_cap_params)) {
239         op->params = params;
240         params = NULL; // so we don't free them in this function
241     }
242 
243     if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
244         if (asprintf(&op->opaque->exec, "%s/resource.d/%s/%s", OCF_ROOT_DIR, provider, agent) == -1) {
245             crm_err("Internal error: cannot create agent path");
246             goto return_error;
247         }
248         op->opaque->args[0] = strdup(op->opaque->exec);
249         op->opaque->args[1] = strdup(op->action);
250 
251     } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_LSB) == 0) {
252         op->opaque->exec = services__lsb_agent_path(op->agent);
253         op->opaque->args[0] = strdup(op->opaque->exec);
254         op->opaque->args[1] = strdup(op->action);
255 
256 #if SUPPORT_HEARTBEAT
257     } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_HB) == 0) {
258         int index;
259         int param_num;
260         char buf_tmp[20];
261         void *value_tmp;
262 
263         if (op->agent[0] == '/') {
264             /* if given an absolute path, use that instead
265              * of tacking on the HB_RA_DIR path to the front */
266             op->opaque->exec = strdup(op->agent);
267         } else if (asprintf(&op->opaque->exec, "%s/%s", HB_RA_DIR, op->agent) == -1) {
268             crm_err("Internal error: cannot create agent path");
269             goto return_error;
270         }
271         op->opaque->args[0] = strdup(op->opaque->exec);
272 
273         /* The "heartbeat" agent class only has positional arguments,
274          * which we keyed by their decimal position number. */
275         param_num = 1;
276         if (op->params) {
277             for (index = 1; index <= MAX_ARGC - 3; index++ ) {
278                 snprintf(buf_tmp, sizeof(buf_tmp), "%d", index);
279                 value_tmp = g_hash_table_lookup(op->params, buf_tmp);
280                 if (value_tmp == NULL) {
281                     /* maybe: strdup("") ??
282                      * But the old lrmd did simply continue as well. */
283                     continue;
284                 }
285                 op->opaque->args[param_num++] = strdup(value_tmp);
286             }
287 
288             // Heartbeat actions don't need to keep the parameters
289             g_hash_table_destroy(op->params);
290             op->params = NULL;
291         }
292 
293         /* Add operation code as the last argument, */
294         /* and the terminating NULL pointer */
295         op->opaque->args[param_num++] = strdup(op->action);
296         op->opaque->args[param_num] = NULL;
297 #endif
298 #if SUPPORT_SYSTEMD
299     } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
300         op->opaque->exec = strdup("systemd-dbus");
301 #endif
302 #if SUPPORT_UPSTART
303     } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_UPSTART) == 0) {
304         op->opaque->exec = strdup("upstart-dbus");
305 #endif
306 #if SUPPORT_NAGIOS
307     } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_NAGIOS) == 0) {
308         if (op->agent[0] == '/') {
309             /* if given an absolute path, use that instead
310              * of tacking on the NAGIOS_PLUGIN_DIR path to the front */
311             op->opaque->exec = strdup(op->agent);
312 
313         } else if (asprintf(&op->opaque->exec, "%s/%s", NAGIOS_PLUGIN_DIR, op->agent) == -1) {
314             crm_err("Internal error: cannot create agent path");
315             goto return_error;
316         }
317 
318         op->opaque->args[0] = strdup(op->opaque->exec);
319 
320         if (safe_str_eq(op->action, "monitor") && op->interval == 0) {
321             /* Invoke --version for a nagios probe */
322             op->opaque->args[1] = strdup("--version");
323 
324         } else if (op->params) {
325             GHashTableIter iter;
326             char *key = NULL;
327             char *value = NULL;
328             int index = 1;
329             static int args_size = sizeof(op->opaque->args) / sizeof(char *);
330 
331             g_hash_table_iter_init(&iter, op->params);
332 
333             while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value) &&
334                    index <= args_size - 3) {
335                 int len = 3;
336                 char *long_opt = NULL;
337 
338                 if (safe_str_eq(key, XML_ATTR_CRM_VERSION) || strstr(key, CRM_META "_")) {
339                     continue;
340                 }
341 
342                 len += strlen(key);
343                 long_opt = calloc(1, len);
344                 sprintf(long_opt, "--%s", key);
345                 long_opt[len - 1] = 0;
346 
347                 op->opaque->args[index] = long_opt;
348                 op->opaque->args[index + 1] = strdup(value);
349                 index += 2;
350             }
351         }
352 
353         // Nagios actions don't need to keep the parameters
354         if (op->params != NULL) {
355             g_hash_table_destroy(op->params);
356             op->params = NULL;
357         }
358 #endif
359     } else {
360         crm_err("Unknown resource standard: %s", op->standard);
361         goto return_error;
362     }
363 
364     if(params) {
365         g_hash_table_destroy(params);
366     }
367     return op;
368 
369   return_error:
370     if(params) {
371         g_hash_table_destroy(params);
372     }
373     services_action_free(op);
374 
375     return NULL;
376 }
377 
378 svc_action_t *
services_action_create_generic(const char * exec,const char * args[])379 services_action_create_generic(const char *exec, const char *args[])
380 {
381     svc_action_t *op;
382     unsigned int cur_arg;
383 
384     op = calloc(1, sizeof(*op));
385     op->opaque = calloc(1, sizeof(svc_action_private_t));
386 
387     op->opaque->exec = strdup(exec);
388     op->opaque->args[0] = strdup(exec);
389 
390     for (cur_arg = 1; args && args[cur_arg - 1]; cur_arg++) {
391         op->opaque->args[cur_arg] = strdup(args[cur_arg - 1]);
392 
393         if (cur_arg == DIMOF(op->opaque->args) - 1) {
394             crm_err("svc_action_t args list not long enough for '%s' execution request.", exec);
395             break;
396         }
397     }
398 
399     return op;
400 }
401 
402 /*!
403  * \brief Create an alert agent action
404  *
405  * \param[in] id        Alert ID
406  * \param[in] exec      Path to alert agent executable
407  * \param[in] timeout   Action timeout
408  * \param[in] params    Parameters to use with action
409  * \param[in] sequence  Action sequence number
410  * \param[in] cb_data   Data to pass to callback function
411  *
412  * \return New action on success, NULL on error
413  * \note It is the caller's responsibility to free cb_data.
414  *       The caller should not free params explicitly.
415  */
416 svc_action_t *
services_alert_create(const char * id,const char * exec,int timeout,GHashTable * params,int sequence,void * cb_data)417 services_alert_create(const char *id, const char *exec, int timeout,
418                       GHashTable *params, int sequence, void *cb_data)
419 {
420     svc_action_t *action = services_action_create_generic(exec, NULL);
421 
422     CRM_ASSERT(action);
423     action->timeout = timeout;
424     action->id = strdup(id);
425     action->params = params;
426     action->sequence = sequence;
427     action->cb_data = cb_data;
428     return action;
429 }
430 
431 /*!
432  * \brief Set the user and group that an action will execute as
433  *
434  * \param[in,out] action  Action to modify
435  * \param[in]     user    Name of user to execute action as
436  * \param[in]     group   Name of group to execute action as
437  *
438  * \return pcmk_ok on success, -errno otherwise
439  *
440  * \note This will have no effect unless the process executing the action runs
441  *       as root, and the action is not a systemd or upstart action.
442  *       We could implement this for systemd by adding User= and Group= to
443  *       [Service] in the override file, but that seems more likely to cause
444  *       problems than be useful.
445  */
446 int
services_action_user(svc_action_t * op,const char * user)447 services_action_user(svc_action_t *op, const char *user)
448 {
449     CRM_CHECK((op != NULL) && (user != NULL), return -EINVAL);
450     return crm_user_lookup(user, &(op->opaque->uid), &(op->opaque->gid));
451 }
452 
453 /*!
454  * \brief Execute an alert agent action
455  *
456  * \param[in] action  Action to execute
457  * \param[in] cb      Function to call when action completes
458  *
459  * \return TRUE if the library will free action, FALSE otherwise
460  *
461  * \note If this function returns FALSE, it is the caller's responsibility to
462  *       free the action with services_action_free().
463  */
464 gboolean
services_alert_async(svc_action_t * action,void (* cb)(svc_action_t * op))465 services_alert_async(svc_action_t *action, void (*cb)(svc_action_t *op))
466 {
467     action->synchronous = false;
468     action->opaque->callback = cb;
469     return services_os_action_execute(action);
470 }
471 
472 #if SUPPORT_DBUS
473 /*!
474  * \internal
475  * \brief Update operation's pending DBus call, unreferencing old one if needed
476  *
477  * \param[in,out] op       Operation to modify
478  * \param[in]     pending  Pending call to set
479  */
480 void
services_set_op_pending(svc_action_t * op,DBusPendingCall * pending)481 services_set_op_pending(svc_action_t *op, DBusPendingCall *pending)
482 {
483     if (op->opaque->pending && (op->opaque->pending != pending)) {
484         if (pending) {
485             crm_info("Lost pending %s DBus call (%p)", op->id, op->opaque->pending);
486         } else {
487             crm_trace("Done with pending %s DBus call (%p)", op->id, op->opaque->pending);
488         }
489         dbus_pending_call_unref(op->opaque->pending);
490     }
491     op->opaque->pending = pending;
492     if (pending) {
493         crm_trace("Updated pending %s DBus call (%p)", op->id, pending);
494     } else {
495         crm_trace("Cleared pending %s DBus call", op->id);
496     }
497 }
498 #endif
499 
500 void
services_action_cleanup(svc_action_t * op)501 services_action_cleanup(svc_action_t * op)
502 {
503     if ((op == NULL) || (op->opaque == NULL)) {
504         return;
505     }
506 
507 #if SUPPORT_DBUS
508     if(op->opaque->timerid != 0) {
509         crm_trace("Removing timer for call %s to %s", op->action, op->rsc);
510         g_source_remove(op->opaque->timerid);
511         op->opaque->timerid = 0;
512     }
513 
514     if(op->opaque->pending) {
515         if (dbus_pending_call_get_completed(op->opaque->pending)) {
516             // This should never be the case
517             crm_warn("Result of %s op %s was unhandled",
518                      op->standard, op->id);
519         } else {
520             crm_debug("Will ignore any result of canceled %s op %s",
521                       op->standard, op->id);
522         }
523         dbus_pending_call_cancel(op->opaque->pending);
524         services_set_op_pending(op, NULL);
525     }
526 #endif
527 
528     if (op->opaque->stderr_gsource) {
529         mainloop_del_fd(op->opaque->stderr_gsource);
530         op->opaque->stderr_gsource = NULL;
531     }
532 
533     if (op->opaque->stdout_gsource) {
534         mainloop_del_fd(op->opaque->stdout_gsource);
535         op->opaque->stdout_gsource = NULL;
536     }
537 }
538 
539 void
services_action_free(svc_action_t * op)540 services_action_free(svc_action_t * op)
541 {
542     unsigned int i;
543 
544     if (op == NULL) {
545         return;
546     }
547 
548     /* The operation should be removed from all tracking lists by this point.
549      * If it's not, we have a bug somewhere, so bail. That may lead to a
550      * memory leak, but it's better than a use-after-free segmentation fault.
551      */
552     CRM_CHECK(g_list_find(inflight_ops, op) == NULL, return);
553     CRM_CHECK(g_list_find(blocked_ops, op) == NULL, return);
554     CRM_CHECK((recurring_actions == NULL)
555               || (g_hash_table_lookup(recurring_actions, op->id) == NULL),
556               return);
557 
558     services_action_cleanup(op);
559 
560     if (op->opaque->repeat_timer) {
561         g_source_remove(op->opaque->repeat_timer);
562         op->opaque->repeat_timer = 0;
563     }
564 
565     free(op->id);
566     free(op->opaque->exec);
567 
568     for (i = 0; i < DIMOF(op->opaque->args); i++) {
569         free(op->opaque->args[i]);
570     }
571 
572     free(op->opaque);
573     free(op->rsc);
574     free(op->action);
575 
576     free(op->standard);
577     free(op->agent);
578     free(op->provider);
579 
580     free(op->stdout_data);
581     free(op->stderr_data);
582 
583     if (op->params) {
584         g_hash_table_destroy(op->params);
585         op->params = NULL;
586     }
587 
588     free(op);
589 }
590 
591 gboolean
cancel_recurring_action(svc_action_t * op)592 cancel_recurring_action(svc_action_t * op)
593 {
594     crm_info("Cancelling %s operation %s", op->standard, op->id);
595 
596     if (recurring_actions) {
597         g_hash_table_remove(recurring_actions, op->id);
598     }
599 
600     if (op->opaque->repeat_timer) {
601         g_source_remove(op->opaque->repeat_timer);
602         op->opaque->repeat_timer = 0;
603     }
604 
605     return TRUE;
606 }
607 
608 /*!
609  * \brief Cancel a recurring action
610  *
611  * \param[in] name      Name of resource that operation is for
612  * \param[in] action    Name of operation to cancel
613  * \param[in] interval  Interval of operation to cancel
614  *
615  * \return TRUE if action was successfully cancelled, FALSE otherwise
616  */
617 gboolean
services_action_cancel(const char * name,const char * action,int interval)618 services_action_cancel(const char *name, const char *action, int interval)
619 {
620     gboolean cancelled = FALSE;
621     char *id = generate_op_key(name, action, interval);
622     svc_action_t *op = NULL;
623 
624     /* We can only cancel a recurring action */
625     init_recurring_actions();
626     op = g_hash_table_lookup(recurring_actions, id);
627     if (op == NULL) {
628         goto done;
629     }
630 
631     /* Tell operation_finalize() not to reschedule the operation */
632     op->cancel = TRUE;
633 
634     /* Stop tracking it as a recurring operation, and stop its repeat timer */
635     cancel_recurring_action(op);
636 
637     /* If the op has a PID, it's an in-flight child process, so kill it.
638      *
639      * Whether the kill succeeds or fails, the main loop will send the op to
640      * operation_finished() (and thus operation_finalize()) when the process
641      * goes away.
642      */
643     if (op->pid != 0) {
644         crm_info("Terminating in-flight op %s (pid %d) early because it was cancelled",
645                  id, op->pid);
646         cancelled = mainloop_child_kill(op->pid);
647         if (cancelled == FALSE) {
648             crm_err("Termination of %s (pid %d) failed", id, op->pid);
649         }
650         goto done;
651     }
652 
653 #if SUPPORT_DBUS
654     // In-flight systemd and upstart ops don't have a pid
655     if (inflight_systemd_or_upstart(op)) {
656         inflight_ops = g_list_remove(inflight_ops, op);
657 
658         /* This will cause any result that comes in later to be discarded, so we
659          * don't call the callback and free the operation twice.
660          */
661         services_action_cleanup(op);
662     }
663 #endif
664 
665     // The rest of this is essentially equivalent to operation_finalize(),
666     // except without calling handle_blocked_ops()
667 
668     // Report operation as cancelled
669     op->status = PCMK_LRM_OP_CANCELLED;
670     if (op->opaque->callback) {
671         op->opaque->callback(op);
672     }
673 
674     blocked_ops = g_list_remove(blocked_ops, op);
675     services_action_free(op);
676     cancelled = TRUE;
677     // @TODO Initiate handle_blocked_ops() asynchronously
678 
679 done:
680     free(id);
681     return cancelled;
682 }
683 
684 gboolean
services_action_kick(const char * name,const char * action,int interval)685 services_action_kick(const char *name, const char *action, int interval /* ms */)
686 {
687     svc_action_t * op = NULL;
688     char *id = generate_op_key(name, action, interval);
689 
690     init_recurring_actions();
691     op = g_hash_table_lookup(recurring_actions, id);
692     free(id);
693 
694     if (op == NULL) {
695         return FALSE;
696     }
697 
698 
699     if (op->pid || inflight_systemd_or_upstart(op)) {
700         return TRUE;
701     } else {
702         if (op->opaque->repeat_timer) {
703             g_source_remove(op->opaque->repeat_timer);
704             op->opaque->repeat_timer = 0;
705         }
706         recurring_action_timer(op);
707         return TRUE;
708     }
709 
710 }
711 
712 /*!
713  * \internal
714  * \brief Add a new recurring operation, checking for duplicates
715  *
716  * \param[in] op               Operation to add
717  *
718  * \return TRUE if duplicate found (and reschedule), FALSE otherwise
719  */
720 static gboolean
handle_duplicate_recurring(svc_action_t * op)721 handle_duplicate_recurring(svc_action_t * op)
722 {
723     svc_action_t * dup = NULL;
724 
725     /* check for duplicates */
726     dup = g_hash_table_lookup(recurring_actions, op->id);
727 
728     if (dup && (dup != op)) {
729         /* update user data */
730         if (op->opaque->callback) {
731             dup->opaque->callback = op->opaque->callback;
732             dup->cb_data = op->cb_data;
733             op->cb_data = NULL;
734         }
735         /* immediately execute the next interval */
736         if (dup->pid != 0) {
737             if (op->opaque->repeat_timer) {
738                 g_source_remove(op->opaque->repeat_timer);
739                 op->opaque->repeat_timer = 0;
740             }
741             recurring_action_timer(dup);
742         }
743         /* free the duplicate */
744         services_action_free(op);
745         return TRUE;
746     }
747 
748     return FALSE;
749 }
750 
751 inline static gboolean
action_exec_helper(svc_action_t * op)752 action_exec_helper(svc_action_t * op)
753 {
754     /* Whether a/synchronous must be decided (op->synchronous) beforehand. */
755     if (op->standard
756         && (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_UPSTART) == 0)) {
757 #if SUPPORT_UPSTART
758         return upstart_job_exec(op);
759 #endif
760     } else if (op->standard && strcasecmp(op->standard,
761                                           PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
762 #if SUPPORT_SYSTEMD
763         return systemd_unit_exec(op);
764 #endif
765     } else {
766         return services_os_action_execute(op);
767     }
768     /* The 'op' has probably been freed if the execution functions return TRUE
769        for the asynchronous 'op'. */
770     /* Avoid using the 'op' in here. */
771 
772     return FALSE;
773 }
774 
775 void
services_add_inflight_op(svc_action_t * op)776 services_add_inflight_op(svc_action_t * op)
777 {
778     if (op == NULL) {
779         return;
780     }
781 
782     CRM_ASSERT(op->synchronous == FALSE);
783 
784     /* keep track of ops that are in-flight to avoid collisions in the same namespace */
785     if (op->rsc) {
786         inflight_ops = g_list_append(inflight_ops, op);
787     }
788 }
789 
790 /*!
791  * \internal
792  * \brief Stop tracking an operation that completed
793  *
794  * \param[in] op  Operation to stop tracking
795  */
796 void
services_untrack_op(svc_action_t * op)797 services_untrack_op(svc_action_t *op)
798 {
799     /* Op is no longer in-flight or blocked */
800     inflight_ops = g_list_remove(inflight_ops, op);
801     blocked_ops = g_list_remove(blocked_ops, op);
802 
803     /* Op is no longer blocking other ops, so check if any need to run */
804     handle_blocked_ops();
805 }
806 
807 gboolean
services_action_async_fork_notify(svc_action_t * op,void (* action_callback)(svc_action_t *),void (* action_fork_callback)(svc_action_t *))808 services_action_async_fork_notify(svc_action_t * op,
809                                   void (*action_callback) (svc_action_t *),
810                                   void (*action_fork_callback) (svc_action_t *))
811 {
812     op->synchronous = false;
813     if (action_callback) {
814         op->opaque->callback = action_callback;
815     }
816     if (action_fork_callback) {
817         op->opaque->fork_callback = action_fork_callback;
818     }
819 
820     if (op->interval > 0) {
821         init_recurring_actions();
822         if (handle_duplicate_recurring(op) == TRUE) {
823             /* entry rescheduled, dup freed */
824             /* exit early */
825             return TRUE;
826         }
827         g_hash_table_replace(recurring_actions, op->id, op);
828     }
829 
830     if (is_not_set(op->flags, SVC_ACTION_NON_BLOCKED)
831         && op->rsc && is_op_blocked(op->rsc)) {
832         blocked_ops = g_list_append(blocked_ops, op);
833         return TRUE;
834     }
835 
836     return action_exec_helper(op);
837 }
838 
839 gboolean
services_action_async(svc_action_t * op,void (* action_callback)(svc_action_t *))840 services_action_async(svc_action_t * op,
841                       void (*action_callback) (svc_action_t *))
842 {
843     return services_action_async_fork_notify(op, action_callback, NULL);
844 }
845 
846 static gboolean processing_blocked_ops = FALSE;
847 
848 gboolean
is_op_blocked(const char * rsc)849 is_op_blocked(const char *rsc)
850 {
851     GList *gIter = NULL;
852     svc_action_t *op = NULL;
853 
854     for (gIter = inflight_ops; gIter != NULL; gIter = gIter->next) {
855         op = gIter->data;
856         if (safe_str_eq(op->rsc, rsc)) {
857             return TRUE;
858         }
859     }
860 
861     return FALSE;
862 }
863 
864 static void
handle_blocked_ops(void)865 handle_blocked_ops(void)
866 {
867     GList *executed_ops = NULL;
868     GList *gIter = NULL;
869     svc_action_t *op = NULL;
870     gboolean res = FALSE;
871 
872     if (processing_blocked_ops) {
873         /* avoid nested calling of this function */
874         return;
875     }
876 
877     processing_blocked_ops = TRUE;
878 
879     /* n^2 operation here, but blocked ops are incredibly rare. this list
880      * will be empty 99% of the time. */
881     for (gIter = blocked_ops; gIter != NULL; gIter = gIter->next) {
882         op = gIter->data;
883         if (is_op_blocked(op->rsc)) {
884             continue;
885         }
886         executed_ops = g_list_append(executed_ops, op);
887         res = action_exec_helper(op);
888         if (res == FALSE) {
889             op->status = PCMK_LRM_OP_ERROR;
890             /* this can cause this function to be called recursively
891              * which is why we have processing_blocked_ops static variable */
892             operation_finalize(op);
893         }
894     }
895 
896     for (gIter = executed_ops; gIter != NULL; gIter = gIter->next) {
897         op = gIter->data;
898         blocked_ops = g_list_remove(blocked_ops, op);
899     }
900     g_list_free(executed_ops);
901 
902     processing_blocked_ops = FALSE;
903 }
904 
905 #define lsb_metadata_template  \
906     "<?xml version='1.0'?>\n"                                           \
907     "<!DOCTYPE resource-agent SYSTEM 'ra-api-1.dtd'>\n"                 \
908     "<resource-agent name='%s' version='" PCMK_DEFAULT_AGENT_VERSION "'>\n" \
909     "  <version>1.0</version>\n"                                        \
910     "  <longdesc lang='en'>\n"                                          \
911     "%s"                                                                \
912     "  </longdesc>\n"                                                   \
913     "  <shortdesc lang='en'>%s</shortdesc>\n"                           \
914     "  <parameters>\n"                                                  \
915     "  </parameters>\n"                                                 \
916     "  <actions>\n"                                                     \
917     "    <action name='meta-data'    timeout='5' />\n"                  \
918     "    <action name='start'        timeout='15' />\n"                 \
919     "    <action name='stop'         timeout='15' />\n"                 \
920     "    <action name='status'       timeout='15' />\n"                 \
921     "    <action name='restart'      timeout='15' />\n"                 \
922     "    <action name='force-reload' timeout='15' />\n"                 \
923     "    <action name='monitor'      timeout='15' interval='15' />\n"   \
924     "  </actions>\n"                                                    \
925     "  <special tag='LSB'>\n"                                           \
926     "    <Provides>%s</Provides>\n"                                     \
927     "    <Required-Start>%s</Required-Start>\n"                         \
928     "    <Required-Stop>%s</Required-Stop>\n"                           \
929     "    <Should-Start>%s</Should-Start>\n"                             \
930     "    <Should-Stop>%s</Should-Stop>\n"                               \
931     "    <Default-Start>%s</Default-Start>\n"                           \
932     "    <Default-Stop>%s</Default-Stop>\n"                             \
933     "  </special>\n"                                                    \
934     "</resource-agent>\n"
935 
936 /* See "Comment Conventions for Init Scripts" in the LSB core specification at:
937  * http://refspecs.linuxfoundation.org/lsb.shtml
938  */
939 #define LSB_INITSCRIPT_INFOBEGIN_TAG "### BEGIN INIT INFO"
940 #define LSB_INITSCRIPT_INFOEND_TAG "### END INIT INFO"
941 #define PROVIDES    "# Provides:"
942 #define REQ_START   "# Required-Start:"
943 #define REQ_STOP    "# Required-Stop:"
944 #define SHLD_START  "# Should-Start:"
945 #define SHLD_STOP   "# Should-Stop:"
946 #define DFLT_START  "# Default-Start:"
947 #define DFLT_STOP   "# Default-Stop:"
948 #define SHORT_DSCR  "# Short-Description:"
949 #define DESCRIPTION "# Description:"
950 
951 #define lsb_meta_helper_free_value(m)           \
952     do {                                        \
953         if ((m) != NULL) {                      \
954             xmlFree(m);                         \
955             (m) = NULL;                         \
956         }                                       \
957     } while(0)
958 
959 /*!
960  * \internal
961  * \brief Grab an LSB header value
962  *
963  * \param[in]     line    Line read from LSB init script
964  * \param[in,out] value   If not set, will be set to XML-safe copy of value
965  * \param[in]     prefix  Set value if line starts with this pattern
966  *
967  * \return TRUE if value was set, FALSE otherwise
968  */
969 static inline gboolean
lsb_meta_helper_get_value(const char * line,char ** value,const char * prefix)970 lsb_meta_helper_get_value(const char *line, char **value, const char *prefix)
971 {
972     if (!*value && crm_starts_with(line, prefix)) {
973         *value = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST line+strlen(prefix));
974         return TRUE;
975     }
976     return FALSE;
977 }
978 
979 #define DESC_MAX 2048
980 
981 static int
lsb_get_metadata(const char * type,char ** output)982 lsb_get_metadata(const char *type, char **output)
983 {
984     char ra_pathname[PATH_MAX] = { 0, };
985     FILE *fp = NULL;
986     char buffer[1024] = { 0, };
987     char *provides = NULL;
988     char *req_start = NULL;
989     char *req_stop = NULL;
990     char *shld_start = NULL;
991     char *shld_stop = NULL;
992     char *dflt_start = NULL;
993     char *dflt_stop = NULL;
994     char *s_dscrpt = NULL;
995     char *xml_l_dscrpt = NULL;
996     int offset = 0;
997     bool in_header = FALSE;
998     char description[DESC_MAX] = { 0, };
999 
1000     if (type[0] == '/') {
1001         snprintf(ra_pathname, sizeof(ra_pathname), "%s", type);
1002     } else {
1003         snprintf(ra_pathname, sizeof(ra_pathname), "%s/%s",
1004                  LSB_ROOT_DIR, type);
1005     }
1006 
1007     crm_trace("Looking into %s", ra_pathname);
1008     fp = fopen(ra_pathname, "r");
1009     if (fp == NULL) {
1010         return -errno;
1011     }
1012 
1013     /* Enter into the LSB-compliant comment block */
1014     while (fgets(buffer, sizeof(buffer), fp)) {
1015 
1016         // Ignore lines up to and including the block delimiter
1017         if (crm_starts_with(buffer, LSB_INITSCRIPT_INFOBEGIN_TAG)) {
1018             in_header = TRUE;
1019             continue;
1020         }
1021         if (!in_header) {
1022             continue;
1023         }
1024 
1025         /* Assume each of the following eight arguments contain one line */
1026         if (lsb_meta_helper_get_value(buffer, &provides, PROVIDES)) {
1027             continue;
1028         }
1029         if (lsb_meta_helper_get_value(buffer, &req_start, REQ_START)) {
1030             continue;
1031         }
1032         if (lsb_meta_helper_get_value(buffer, &req_stop, REQ_STOP)) {
1033             continue;
1034         }
1035         if (lsb_meta_helper_get_value(buffer, &shld_start, SHLD_START)) {
1036             continue;
1037         }
1038         if (lsb_meta_helper_get_value(buffer, &shld_stop, SHLD_STOP)) {
1039             continue;
1040         }
1041         if (lsb_meta_helper_get_value(buffer, &dflt_start, DFLT_START)) {
1042             continue;
1043         }
1044         if (lsb_meta_helper_get_value(buffer, &dflt_stop, DFLT_STOP)) {
1045             continue;
1046         }
1047         if (lsb_meta_helper_get_value(buffer, &s_dscrpt, SHORT_DSCR)) {
1048             continue;
1049         }
1050 
1051         /* Long description may cross multiple lines */
1052         if ((offset == 0) // haven't already found long description
1053             && crm_starts_with(buffer, DESCRIPTION)) {
1054             bool processed_line = TRUE;
1055 
1056             // Get remainder of description line itself
1057             offset += snprintf(description, DESC_MAX, "%s",
1058                                buffer + strlen(DESCRIPTION));
1059 
1060             // Read any continuation lines of the description
1061             buffer[0] = '\0';
1062             while (fgets(buffer, sizeof(buffer), fp)) {
1063                 if (crm_starts_with(buffer, "#  ")
1064                     || crm_starts_with(buffer, "#\t")) {
1065                     /* '#' followed by a tab or more than one space indicates a
1066                      * continuation of the long description.
1067                      */
1068                     offset += snprintf(description + offset, DESC_MAX - offset,
1069                                        "%s", buffer + 1);
1070                 } else {
1071                     /* This line is not part of the long description,
1072                      * so continue with normal processing.
1073                      */
1074                     processed_line = FALSE;
1075                     break;
1076                 }
1077             }
1078 
1079             // Make long description safe to use in XML
1080             xml_l_dscrpt = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST(description));
1081 
1082             if (processed_line) {
1083                 // We grabbed the line into the long description
1084                 continue;
1085             }
1086         }
1087 
1088         // Stop if we leave the header block
1089         if (crm_starts_with(buffer, LSB_INITSCRIPT_INFOEND_TAG)) {
1090             break;
1091         }
1092         if (buffer[0] != '#') {
1093             break;
1094         }
1095     }
1096     fclose(fp);
1097 
1098     *output = crm_strdup_printf(lsb_metadata_template, type,
1099                                 (xml_l_dscrpt? xml_l_dscrpt : type),
1100                                 (s_dscrpt? s_dscrpt : type),
1101                                 (provides? provides : ""),
1102                                 (req_start? req_start : ""),
1103                                 (req_stop? req_stop : ""),
1104                                 (shld_start? shld_start : ""),
1105                                 (shld_stop? shld_stop : ""),
1106                                 (dflt_start? dflt_start : ""),
1107                                 (dflt_stop? dflt_stop : ""));
1108 
1109     lsb_meta_helper_free_value(xml_l_dscrpt);
1110     lsb_meta_helper_free_value(s_dscrpt);
1111     lsb_meta_helper_free_value(provides);
1112     lsb_meta_helper_free_value(req_start);
1113     lsb_meta_helper_free_value(req_stop);
1114     lsb_meta_helper_free_value(shld_start);
1115     lsb_meta_helper_free_value(shld_stop);
1116     lsb_meta_helper_free_value(dflt_start);
1117     lsb_meta_helper_free_value(dflt_stop);
1118 
1119     crm_trace("Created fake metadata: %llu",
1120               (unsigned long long) strlen(*output));
1121     return pcmk_ok;
1122 }
1123 
1124 #if SUPPORT_NAGIOS
1125 static int
nagios_get_metadata(const char * type,char ** output)1126 nagios_get_metadata(const char *type, char **output)
1127 {
1128     int rc = pcmk_ok;
1129     FILE *file_strm = NULL;
1130     int start = 0, length = 0, read_len = 0;
1131     char *metadata_file = crm_strdup_printf("%s/%s.xml",
1132                                             NAGIOS_METADATA_DIR, type);
1133 
1134     file_strm = fopen(metadata_file, "r");
1135     if (file_strm == NULL) {
1136         crm_err("Metadata file %s does not exist", metadata_file);
1137         free(metadata_file);
1138         return -EIO;
1139     }
1140 
1141     /* see how big the file is */
1142     start = ftell(file_strm);
1143     fseek(file_strm, 0L, SEEK_END);
1144     length = ftell(file_strm);
1145     fseek(file_strm, 0L, start);
1146 
1147     CRM_ASSERT(length >= 0);
1148     CRM_ASSERT(start == ftell(file_strm));
1149 
1150     if (length <= 0) {
1151         crm_info("%s was not valid", metadata_file);
1152         free(*output);
1153         *output = NULL;
1154         rc = -EIO;
1155 
1156     } else {
1157         crm_trace("Reading %d bytes from file", length);
1158         *output = calloc(1, (length + 1));
1159         read_len = fread(*output, 1, length, file_strm);
1160         if (read_len != length) {
1161             crm_err("Calculated and read bytes differ: %d vs. %d",
1162                     length, read_len);
1163             free(*output);
1164             *output = NULL;
1165             rc = -EIO;
1166         }
1167     }
1168 
1169     fclose(file_strm);
1170     free(metadata_file);
1171     return rc;
1172 }
1173 #endif
1174 
1175 #if SUPPORT_HEARTBEAT
1176 /* strictly speaking, support for class=heartbeat style scripts
1177  * does not require "heartbeat support" to be enabled.
1178  * But since those scripts are part of the "heartbeat" package usually,
1179  * and are very unlikely to be present in any other deployment,
1180  * I leave it inside this ifdef.
1181  *
1182  * Yes, I know, these are legacy and should die,
1183  * or at least be rewritten to be a proper OCF style agent.
1184  * But they exist, and custom scripts following these rules do, too.
1185  *
1186  * Taken from the old "glue" lrmd, see
1187  * http://hg.linux-ha.org/glue/file/0a7add1d9996/lib/plugins/lrm/raexechb.c#l49
1188  * http://hg.linux-ha.org/glue/file/0a7add1d9996/lib/plugins/lrm/raexechb.c#l393
1189  */
1190 
1191 static const char hb_metadata_template[] =
1192     "<?xml version='1.0'?>\n"
1193     "<!DOCTYPE resource-agent SYSTEM 'ra-api-1.dtd'>\n"
1194     "<resource-agent name='%s' version='" PCMK_DEFAULT_AGENT_VERSION "'>\n"
1195     "<version>1.0</version>\n"
1196     "<longdesc lang='en'>\n"
1197     "%s"
1198     "</longdesc>\n"
1199     "<shortdesc lang='en'>%s</shortdesc>\n"
1200     "<parameters>\n"
1201     "<parameter name='1' unique='1' required='0'>\n"
1202     "<longdesc lang='en'>\n"
1203     "This argument will be passed as the first argument to the "
1204     "heartbeat resource agent (assuming it supports one)\n"
1205     "</longdesc>\n"
1206     "<shortdesc lang='en'>argv[1]</shortdesc>\n"
1207     "<content type='string' default=' ' />\n"
1208     "</parameter>\n"
1209     "<parameter name='2' unique='1' required='0'>\n"
1210     "<longdesc lang='en'>\n"
1211     "This argument will be passed as the second argument to the "
1212     "heartbeat resource agent (assuming it supports one)\n"
1213     "</longdesc>\n"
1214     "<shortdesc lang='en'>argv[2]</shortdesc>\n"
1215     "<content type='string' default=' ' />\n"
1216     "</parameter>\n"
1217     "<parameter name='3' unique='1' required='0'>\n"
1218     "<longdesc lang='en'>\n"
1219     "This argument will be passed as the third argument to the "
1220     "heartbeat resource agent (assuming it supports one)\n"
1221     "</longdesc>\n"
1222     "<shortdesc lang='en'>argv[3]</shortdesc>\n"
1223     "<content type='string' default=' ' />\n"
1224     "</parameter>\n"
1225     "<parameter name='4' unique='1' required='0'>\n"
1226     "<longdesc lang='en'>\n"
1227     "This argument will be passed as the fourth argument to the "
1228     "heartbeat resource agent (assuming it supports one)\n"
1229     "</longdesc>\n"
1230     "<shortdesc lang='en'>argv[4]</shortdesc>\n"
1231     "<content type='string' default=' ' />\n"
1232     "</parameter>\n"
1233     "<parameter name='5' unique='1' required='0'>\n"
1234     "<longdesc lang='en'>\n"
1235     "This argument will be passed as the fifth argument to the "
1236     "heartbeat resource agent (assuming it supports one)\n"
1237     "</longdesc>\n"
1238     "<shortdesc lang='en'>argv[5]</shortdesc>\n"
1239     "<content type='string' default=' ' />\n"
1240     "</parameter>\n"
1241     "</parameters>\n"
1242     "<actions>\n"
1243     "<action name='start'   timeout='15' />\n"
1244     "<action name='stop'    timeout='15' />\n"
1245     "<action name='status'  timeout='15' />\n"
1246     "<action name='monitor' timeout='15' interval='15' start-delay='15' />\n"
1247     "<action name='meta-data'  timeout='5' />\n"
1248     "</actions>\n"
1249     "<special tag='heartbeat'>\n"
1250     "</special>\n"
1251     "</resource-agent>\n";
1252 
1253 static int
heartbeat_get_metadata(const char * type,char ** output)1254 heartbeat_get_metadata(const char *type, char **output)
1255 {
1256     *output = crm_strdup_printf(hb_metadata_template, type, type, type);
1257     crm_trace("Created fake metadata: %llu",
1258               (unsigned long long) strlen(*output));
1259     return pcmk_ok;
1260 }
1261 #endif
1262 
1263 static gboolean
action_get_metadata(svc_action_t * op)1264 action_get_metadata(svc_action_t *op)
1265 {
1266     const char *class = op->standard;
1267 
1268     if (op->agent == NULL) {
1269         crm_err("meta-data requested without specifying agent");
1270         return FALSE;
1271     }
1272 
1273     if (class == NULL) {
1274         crm_err("meta-data requested for agent %s without specifying class",
1275                 op->agent);
1276         return FALSE;
1277     }
1278 
1279     if (!strcmp(class, PCMK_RESOURCE_CLASS_SERVICE)) {
1280         class = resources_find_service_class(op->agent);
1281     }
1282 
1283     if (class == NULL) {
1284         crm_err("meta-data requested for %s, but could not determine class",
1285                 op->agent);
1286         return FALSE;
1287     }
1288 
1289     if (safe_str_eq(class, PCMK_RESOURCE_CLASS_LSB)) {
1290         return (lsb_get_metadata(op->agent, &op->stdout_data) >= 0);
1291     }
1292 
1293 #if SUPPORT_NAGIOS
1294     if (safe_str_eq(class, PCMK_RESOURCE_CLASS_NAGIOS)) {
1295         return (nagios_get_metadata(op->agent, &op->stdout_data) >= 0);
1296     }
1297 #endif
1298 
1299 #if SUPPORT_HEARTBEAT
1300     if (safe_str_eq(class, PCMK_RESOURCE_CLASS_HB)) {
1301         return (heartbeat_get_metadata(op->agent, &op->stdout_data) >= 0);
1302     }
1303 #endif
1304 
1305     return action_exec_helper(op);
1306 }
1307 
1308 gboolean
services_action_sync(svc_action_t * op)1309 services_action_sync(svc_action_t * op)
1310 {
1311     gboolean rc = TRUE;
1312 
1313     if (op == NULL) {
1314         crm_trace("No operation to execute");
1315         return FALSE;
1316     }
1317 
1318     op->synchronous = true;
1319 
1320     if (safe_str_eq(op->action, "meta-data")) {
1321         /* Synchronous meta-data operations are handled specially. Since most
1322          * resource classes don't provide any meta-data, it has to be
1323          * synthesized from available information about the agent.
1324          *
1325          * services_action_async() doesn't treat meta-data actions specially, so
1326          * it will result in an error for classes that don't support the action.
1327          */
1328         rc = action_get_metadata(op);
1329     } else {
1330         rc = action_exec_helper(op);
1331     }
1332     crm_trace(" > %s_%s_%d: %s = %d",
1333               op->rsc, op->action, op->interval, op->opaque->exec, op->rc);
1334     if (op->stdout_data) {
1335         crm_trace(" >  stdout: %s", op->stdout_data);
1336     }
1337     if (op->stderr_data) {
1338         crm_trace(" >  stderr: %s", op->stderr_data);
1339     }
1340     return rc;
1341 }
1342 
1343 GList *
get_directory_list(const char * root,gboolean files,gboolean executable)1344 get_directory_list(const char *root, gboolean files, gboolean executable)
1345 {
1346     return services_os_get_directory_list(root, files, executable);
1347 }
1348 
1349 GList *
services_list(void)1350 services_list(void)
1351 {
1352     return resources_list_agents(PCMK_RESOURCE_CLASS_LSB, NULL);
1353 }
1354 
1355 #if SUPPORT_HEARTBEAT
1356 static GList *
resources_os_list_hb_agents(void)1357 resources_os_list_hb_agents(void)
1358 {
1359     return services_os_get_directory_list(HB_RA_DIR, TRUE, TRUE);
1360 }
1361 #endif
1362 
1363 GList *
resources_list_standards(void)1364 resources_list_standards(void)
1365 {
1366     GList *standards = NULL;
1367     GList *agents = NULL;
1368 
1369     standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_OCF));
1370     standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_LSB));
1371     standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_SERVICE));
1372 
1373 #if SUPPORT_SYSTEMD
1374     agents = systemd_unit_listall();
1375     if (agents) {
1376         standards = g_list_append(standards,
1377                                   strdup(PCMK_RESOURCE_CLASS_SYSTEMD));
1378         g_list_free_full(agents, free);
1379     }
1380 #endif
1381 
1382 #if SUPPORT_UPSTART
1383     agents = upstart_job_listall();
1384     if (agents) {
1385         standards = g_list_append(standards,
1386                                   strdup(PCMK_RESOURCE_CLASS_UPSTART));
1387         g_list_free_full(agents, free);
1388     }
1389 #endif
1390 
1391 #if SUPPORT_NAGIOS
1392     agents = resources_os_list_nagios_agents();
1393     if (agents) {
1394         standards = g_list_append(standards,
1395                                   strdup(PCMK_RESOURCE_CLASS_NAGIOS));
1396         g_list_free_full(agents, free);
1397     }
1398 #endif
1399 
1400 #if SUPPORT_HEARTBEAT
1401     standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_HB));
1402 #endif
1403 
1404     return standards;
1405 }
1406 
1407 GList *
resources_list_providers(const char * standard)1408 resources_list_providers(const char *standard)
1409 {
1410     if (is_set(pcmk_get_ra_caps(standard), pcmk_ra_cap_provider)) {
1411         return resources_os_list_ocf_providers();
1412     }
1413 
1414     return NULL;
1415 }
1416 
1417 GList *
resources_list_agents(const char * standard,const char * provider)1418 resources_list_agents(const char *standard, const char *provider)
1419 {
1420     if ((standard == NULL)
1421         || (strcasecmp(standard, PCMK_RESOURCE_CLASS_SERVICE) == 0)) {
1422 
1423         GList *tmp1;
1424         GList *tmp2;
1425         GList *result = resources_os_list_lsb_agents();
1426 
1427         if (standard == NULL) {
1428             tmp1 = result;
1429             tmp2 = resources_os_list_ocf_agents(NULL);
1430             if (tmp2) {
1431                 result = g_list_concat(tmp1, tmp2);
1432             }
1433         }
1434 #if SUPPORT_SYSTEMD
1435         tmp1 = result;
1436         tmp2 = systemd_unit_listall();
1437         if (tmp2) {
1438             result = g_list_concat(tmp1, tmp2);
1439         }
1440 #endif
1441 
1442 #if SUPPORT_UPSTART
1443         tmp1 = result;
1444         tmp2 = upstart_job_listall();
1445         if (tmp2) {
1446             result = g_list_concat(tmp1, tmp2);
1447         }
1448 #endif
1449 
1450         return result;
1451 
1452     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
1453         return resources_os_list_ocf_agents(provider);
1454     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_LSB) == 0) {
1455         return resources_os_list_lsb_agents();
1456 #if SUPPORT_HEARTBEAT
1457     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_HB) == 0) {
1458         return resources_os_list_hb_agents();
1459 #endif
1460 #if SUPPORT_SYSTEMD
1461     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
1462         return systemd_unit_listall();
1463 #endif
1464 #if SUPPORT_UPSTART
1465     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_UPSTART) == 0) {
1466         return upstart_job_listall();
1467 #endif
1468 #if SUPPORT_NAGIOS
1469     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_NAGIOS) == 0) {
1470         return resources_os_list_nagios_agents();
1471 #endif
1472     }
1473 
1474     return NULL;
1475 }
1476