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