root/lib/services/services.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. services_action_create
  2. resources_find_service_class
  3. init_recurring_actions
  4. inflight_systemd_or_upstart
  5. expand_resource_class
  6. resources_action_create
  7. services_action_create_generic
  8. services_alert_create
  9. services_action_user
  10. set_alert_env
  11. unset_alert_env
  12. services_alert_async
  13. services_set_op_pending
  14. services_action_cleanup
  15. services_action_free
  16. cancel_recurring_action
  17. services_action_cancel
  18. services_action_kick
  19. handle_duplicate_recurring
  20. action_exec_helper
  21. services_add_inflight_op
  22. services_untrack_op
  23. services_action_async
  24. is_op_blocked
  25. handle_blocked_ops
  26. lsb_meta_helper_get_value
  27. lsb_get_metadata
  28. nagios_get_metadata
  29. heartbeat_get_metadata
  30. action_get_metadata
  31. services_action_sync
  32. get_directory_list
  33. services_list
  34. resources_os_list_hb_agents
  35. resources_list_standards
  36. resources_list_providers
  37. resources_list_agents

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

/* [previous][next][first][last][top][bottom][index][help] */