root/lib/services/services.c

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

DEFINITIONS

This source file includes following definitions.
  1. resources_find_service_class
  2. init_recurring_actions
  3. inflight_systemd_or_upstart
  4. expand_resource_class
  5. new_action
  6. required_argument_missing
  7. copy_action_arguments
  8. services__create_resource_action
  9. resources_action_create
  10. services_action_create_generic
  11. services_alert_create
  12. services_action_user
  13. services_alert_async
  14. services_set_op_pending
  15. services_action_cleanup
  16. services_result2ocf
  17. services_action_free
  18. cancel_recurring_action
  19. services_action_cancel
  20. services_action_kick
  21. handle_duplicate_recurring
  22. execute_action
  23. services_add_inflight_op
  24. services_untrack_op
  25. services_action_async_fork_notify
  26. services_action_async
  27. is_op_blocked
  28. handle_blocked_ops
  29. execute_metadata_action
  30. services_action_sync
  31. get_directory_list
  32. resources_list_standards
  33. resources_list_providers
  34. resources_list_agents
  35. resources_agent_exists
  36. services__set_result
  37. services__format_result
  38. services__set_cancelled
  39. services__action_kind
  40. services__exit_reason
  41. services__grab_stdout
  42. services__grab_stderr

   1 /*
   2  * Copyright 2010-2022 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #ifndef _GNU_SOURCE
  13 #  define _GNU_SOURCE
  14 #endif
  15 
  16 #include <sys/types.h>
  17 #include <sys/stat.h>
  18 #include <stdio.h>
  19 #include <errno.h>
  20 #include <unistd.h>
  21 #include <dirent.h>
  22 #include <fcntl.h>
  23 
  24 #include <crm/crm.h>
  25 #include <crm/common/mainloop.h>
  26 #include <crm/services.h>
  27 #include <crm/services_internal.h>
  28 #include <crm/stonith-ng.h>
  29 #include <crm/msg_xml.h>
  30 #include "services_private.h"
  31 #include "services_ocf.h"
  32 #include "services_lsb.h"
  33 
  34 #if SUPPORT_UPSTART
  35 #  include <upstart.h>
  36 #endif
  37 
  38 #if SUPPORT_SYSTEMD
  39 #  include <systemd.h>
  40 #endif
  41 
  42 #if SUPPORT_NAGIOS
  43 #  include <services_nagios.h>
  44 #endif
  45 
  46 /* TODO: Develop a rollover strategy */
  47 
  48 static int operations = 0;
  49 static GHashTable *recurring_actions = NULL;
  50 
  51 /* ops waiting to run async because of conflicting active
  52  * pending ops */
  53 static GList *blocked_ops = NULL;
  54 
  55 /* ops currently active (in-flight) */
  56 static GList *inflight_ops = NULL;
  57 
  58 static void handle_blocked_ops(void);
  59 
  60 /*!
  61  * \brief Find first service class that can provide a specified agent
  62  *
  63  * \param[in] agent  Name of agent to search for
  64  *
  65  * \return Service class if found, NULL otherwise
  66  *
  67  * \note The priority is LSB, then systemd, then upstart. It would be preferable
  68  *       to put systemd first, but LSB merely requires a file existence check,
  69  *       while systemd requires contacting D-Bus.
  70  */
  71 const char *
  72 resources_find_service_class(const char *agent)
     /* [previous][next][first][last][top][bottom][index][help] */
  73 {
  74     if (services__lsb_agent_exists(agent)) {
  75         return PCMK_RESOURCE_CLASS_LSB;
  76     }
  77 
  78 #if SUPPORT_SYSTEMD
  79     if (systemd_unit_exists(agent)) {
  80         return PCMK_RESOURCE_CLASS_SYSTEMD;
  81     }
  82 #endif
  83 
  84 #if SUPPORT_UPSTART
  85     if (upstart_job_exists(agent)) {
  86         return PCMK_RESOURCE_CLASS_UPSTART;
  87     }
  88 #endif
  89     return NULL;
  90 }
  91 
  92 static inline void
  93 init_recurring_actions(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  94 {
  95     if (recurring_actions == NULL) {
  96         recurring_actions = pcmk__strkey_table(NULL, NULL);
  97     }
  98 }
  99 
 100 /*!
 101  * \internal
 102  * \brief Check whether op is in-flight systemd or upstart op
 103  *
 104  * \param[in] op  Operation to check
 105  *
 106  * \return TRUE if op is in-flight systemd or upstart op
 107  */
 108 static inline gboolean
 109 inflight_systemd_or_upstart(svc_action_t *op)
     /* [previous][next][first][last][top][bottom][index][help] */
 110 {
 111     return pcmk__strcase_any_of(op->standard, PCMK_RESOURCE_CLASS_SYSTEMD,
 112                            PCMK_RESOURCE_CLASS_UPSTART, NULL) &&
 113            g_list_find(inflight_ops, op) != NULL;
 114 }
 115 
 116 /*!
 117  * \internal
 118  * \brief Expand "service" alias to an actual resource class
 119  *
 120  * \param[in] rsc       Resource name (for logging only)
 121  * \param[in] standard  Resource class as configured
 122  * \param[in] agent     Agent name to look for
 123  *
 124  * \return Newly allocated string with actual resource class
 125  *
 126  * \note The caller is responsible for calling free() on the result.
 127  */
 128 static char *
 129 expand_resource_class(const char *rsc, const char *standard, const char *agent)
     /* [previous][next][first][last][top][bottom][index][help] */
 130 {
 131     char *expanded_class = NULL;
 132 
 133     if (strcasecmp(standard, PCMK_RESOURCE_CLASS_SERVICE) == 0) {
 134         const char *found_class = resources_find_service_class(agent);
 135 
 136         if (found_class) {
 137             crm_debug("Found %s agent %s for %s", found_class, agent, rsc);
 138             expanded_class = strdup(found_class);
 139         } else {
 140             crm_info("Assuming resource class lsb for agent %s for %s",
 141                      agent, rsc);
 142             expanded_class = strdup(PCMK_RESOURCE_CLASS_LSB);
 143         }
 144     } else {
 145         expanded_class = strdup(standard);
 146     }
 147     CRM_ASSERT(expanded_class);
 148     return expanded_class;
 149 }
 150 
 151 /*!
 152  * \internal
 153  * \brief Create a simple svc_action_t instance
 154  *
 155  * \return Newly allocated instance (or NULL if not enough memory)
 156  */
 157 static svc_action_t *
 158 new_action(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 159 {
 160     svc_action_t *op = calloc(1, sizeof(svc_action_t));
 161 
 162     if (op == NULL) {
 163         return NULL;
 164     }
 165 
 166     op->opaque = calloc(1, sizeof(svc_action_private_t));
 167     if (op->opaque == NULL) {
 168         free(op);
 169         return NULL;
 170     }
 171 
 172     // Initialize result
 173     services__set_result(op, PCMK_OCF_UNKNOWN, PCMK_EXEC_UNKNOWN, NULL);
 174     return op;
 175 }
 176 
 177 static bool
 178 required_argument_missing(uint32_t ra_caps, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 179                           const char *standard, const char *provider,
 180                           const char *agent, const char *action)
 181 {
 182     if (pcmk__str_empty(name)) {
 183         crm_info("Cannot create operation without resource name (bug?)");
 184         return true;
 185     }
 186 
 187     if (pcmk__str_empty(standard)) {
 188         crm_info("Cannot create operation for %s without resource class (bug?)",
 189                  name);
 190         return true;
 191     }
 192 
 193     if (pcmk_is_set(ra_caps, pcmk_ra_cap_provider)
 194         && pcmk__str_empty(provider)) {
 195         crm_info("Cannot create operation for %s resource %s "
 196                  "without provider (bug?)", standard, name);
 197         return true;
 198     }
 199 
 200     if (pcmk__str_empty(agent)) {
 201         crm_info("Cannot create operation for %s without agent name (bug?)",
 202                  name);
 203         return true;
 204     }
 205 
 206     if (pcmk__str_empty(action)) {
 207         crm_info("Cannot create operation for %s without action name (bug?)",
 208                  name);
 209         return true;
 210     }
 211     return false;
 212 }
 213 
 214 // \return Standard Pacemaker return code (pcmk_rc_ok or ENOMEM)
 215 static int
 216 copy_action_arguments(svc_action_t *op, uint32_t ra_caps, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 217                       const char *standard, const char *provider,
 218                       const char *agent, const char *action)
 219 {
 220     op->rsc = strdup(name);
 221     if (op->rsc == NULL) {
 222         return ENOMEM;
 223     }
 224 
 225     op->agent = strdup(agent);
 226     if (op->agent == NULL) {
 227         return ENOMEM;
 228     }
 229 
 230     op->standard = expand_resource_class(name, standard, agent);
 231     if (op->standard == NULL) {
 232         return ENOMEM;
 233     }
 234 
 235     if (pcmk_is_set(ra_caps, pcmk_ra_cap_status)
 236         && pcmk__str_eq(action, "monitor", pcmk__str_casei)) {
 237         action = "status";
 238     }
 239     op->action = strdup(action);
 240     if (op->action == NULL) {
 241         return ENOMEM;
 242     }
 243 
 244     if (pcmk_is_set(ra_caps, pcmk_ra_cap_provider)) {
 245         op->provider = strdup(provider);
 246         if (op->provider == NULL) {
 247             return ENOMEM;
 248         }
 249     }
 250     return pcmk_rc_ok;
 251 }
 252 
 253 svc_action_t *
 254 services__create_resource_action(const char *name, const char *standard,
     /* [previous][next][first][last][top][bottom][index][help] */
 255                         const char *provider, const char *agent,
 256                         const char *action, guint interval_ms, int timeout,
 257                         GHashTable *params, enum svc_action_flags flags)
 258 {
 259     svc_action_t *op = NULL;
 260     uint32_t ra_caps = pcmk_get_ra_caps(standard);
 261     int rc = pcmk_rc_ok;
 262 
 263     op = new_action();
 264     if (op == NULL) {
 265         crm_crit("Cannot prepare action: %s", strerror(ENOMEM));
 266         if (params != NULL) {
 267             g_hash_table_destroy(params);
 268         }
 269         return NULL;
 270     }
 271 
 272     op->interval_ms = interval_ms;
 273     op->timeout = timeout;
 274     op->flags = flags;
 275     op->sequence = ++operations;
 276 
 277     // Take ownership of params
 278     if (pcmk_is_set(ra_caps, pcmk_ra_cap_params)) {
 279         op->params = params;
 280     } else if (params != NULL) {
 281         g_hash_table_destroy(params);
 282         params = NULL;
 283     }
 284 
 285     if (required_argument_missing(ra_caps, name, standard, provider, agent,
 286                                   action)) {
 287         services__set_result(op, services__generic_error(op),
 288                              PCMK_EXEC_ERROR_FATAL,
 289                              "Required agent or action information missing");
 290         return op;
 291     }
 292 
 293     op->id = pcmk__op_key(name, action, interval_ms);
 294 
 295     if (copy_action_arguments(op, ra_caps, name, standard, provider, agent,
 296                               action) != pcmk_rc_ok) {
 297         crm_crit("Cannot prepare %s action for %s: %s",
 298                  action, name, strerror(ENOMEM));
 299         services__handle_exec_error(op, ENOMEM);
 300         return op;
 301     }
 302 
 303     if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
 304         rc = services__ocf_prepare(op);
 305 
 306     } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_LSB) == 0) {
 307         rc = services__lsb_prepare(op);
 308 
 309 #if SUPPORT_SYSTEMD
 310     } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
 311         rc = services__systemd_prepare(op);
 312 #endif
 313 #if SUPPORT_UPSTART
 314     } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_UPSTART) == 0) {
 315         rc = services__upstart_prepare(op);
 316 #endif
 317 #if SUPPORT_NAGIOS
 318     } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_NAGIOS) == 0) {
 319         rc = services__nagios_prepare(op);
 320 #endif
 321     } else {
 322         crm_info("Unknown resource standard: %s", op->standard);
 323         rc = ENOENT;
 324     }
 325 
 326     if (rc != pcmk_rc_ok) {
 327         crm_info("Cannot prepare %s operation for %s: %s",
 328                  action, name, strerror(rc));
 329         services__handle_exec_error(op, rc);
 330     }
 331     return op;
 332 }
 333 
 334 svc_action_t *
 335 resources_action_create(const char *name, const char *standard,
     /* [previous][next][first][last][top][bottom][index][help] */
 336                         const char *provider, const char *agent,
 337                         const char *action, guint interval_ms, int timeout,
 338                         GHashTable *params, enum svc_action_flags flags)
 339 {
 340     svc_action_t *op = services__create_resource_action(name, standard,
 341                             provider, agent, action, interval_ms, timeout,
 342                             params, flags);
 343     if (op == NULL || op->rc != 0) {
 344         services_action_free(op);
 345         return NULL;
 346     } else {
 347         // Preserve public API backward compatibility
 348         op->rc = PCMK_OCF_OK;
 349         op->status = PCMK_EXEC_DONE;
 350 
 351         return op;
 352     }
 353 }
 354 
 355 svc_action_t *
 356 services_action_create_generic(const char *exec, const char *args[])
     /* [previous][next][first][last][top][bottom][index][help] */
 357 {
 358     svc_action_t *op = new_action();
 359 
 360     CRM_ASSERT(op != NULL);
 361 
 362     op->opaque->exec = strdup(exec);
 363     op->opaque->args[0] = strdup(exec);
 364     if ((op->opaque->exec == NULL) || (op->opaque->args[0] == NULL)) {
 365         crm_crit("Cannot prepare action for '%s': %s", exec, strerror(ENOMEM));
 366         services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
 367                              strerror(ENOMEM));
 368         return op;
 369     }
 370 
 371     if (args == NULL) {
 372         return op;
 373     }
 374 
 375     for (int cur_arg = 1; args[cur_arg - 1] != NULL; cur_arg++) {
 376 
 377         if (cur_arg == PCMK__NELEM(op->opaque->args)) {
 378             crm_info("Cannot prepare action for '%s': Too many arguments",
 379                      exec);
 380             services__set_result(op, PCMK_OCF_UNKNOWN_ERROR,
 381                                  PCMK_EXEC_ERROR_HARD, "Too many arguments");
 382             break;
 383         }
 384 
 385         op->opaque->args[cur_arg] = strdup(args[cur_arg - 1]);
 386         if (op->opaque->args[cur_arg] == NULL) {
 387             crm_crit("Cannot prepare action for '%s': %s",
 388                      exec, strerror(ENOMEM));
 389             services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
 390                                  strerror(ENOMEM));
 391             break;
 392         }
 393     }
 394 
 395     return op;
 396 }
 397 
 398 /*!
 399  * \brief Create an alert agent action
 400  *
 401  * \param[in] id        Alert ID
 402  * \param[in] exec      Path to alert agent executable
 403  * \param[in] timeout   Action timeout
 404  * \param[in] params    Parameters to use with action
 405  * \param[in] sequence  Action sequence number
 406  * \param[in] cb_data   Data to pass to callback function
 407  *
 408  * \return New action on success, NULL on error
 409  * \note It is the caller's responsibility to free cb_data.
 410  *       The caller should not free params explicitly.
 411  */
 412 svc_action_t *
 413 services_alert_create(const char *id, const char *exec, int timeout,
     /* [previous][next][first][last][top][bottom][index][help] */
 414                       GHashTable *params, int sequence, void *cb_data)
 415 {
 416     svc_action_t *action = services_action_create_generic(exec, NULL);
 417 
 418     action->id = strdup(id);
 419     action->standard = strdup(PCMK_RESOURCE_CLASS_ALERT);
 420     CRM_ASSERT((action->id != NULL) && (action->standard != NULL));
 421 
 422     action->timeout = timeout;
 423     action->params = params;
 424     action->sequence = sequence;
 425     action->cb_data = cb_data;
 426     return action;
 427 }
 428 
 429 /*!
 430  * \brief Set the user and group that an action will execute as
 431  *
 432  * \param[in,out] action  Action to modify
 433  * \param[in]     user    Name of user to execute action as
 434  * \param[in]     group   Name of group to execute action as
 435  *
 436  * \return pcmk_ok on success, -errno otherwise
 437  *
 438  * \note This will have no effect unless the process executing the action runs
 439  *       as root, and the action is not a systemd or upstart action.
 440  *       We could implement this for systemd by adding User= and Group= to
 441  *       [Service] in the override file, but that seems more likely to cause
 442  *       problems than be useful.
 443  */
 444 int
 445 services_action_user(svc_action_t *op, const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 446 {
 447     CRM_CHECK((op != NULL) && (user != NULL), return -EINVAL);
 448     return crm_user_lookup(user, &(op->opaque->uid), &(op->opaque->gid));
 449 }
 450 
 451 /*!
 452  * \brief Execute an alert agent action
 453  *
 454  * \param[in] action  Action to execute
 455  * \param[in] cb      Function to call when action completes
 456  *
 457  * \return TRUE if the library will free action, FALSE otherwise
 458  *
 459  * \note If this function returns FALSE, it is the caller's responsibility to
 460  *       free the action with services_action_free(). However, unless someone
 461  *       intentionally creates a recurring alert action, this will never return
 462  *       FALSE.
 463  */
 464 gboolean
 465 services_alert_async(svc_action_t *action, void (*cb)(svc_action_t *op))
     /* [previous][next][first][last][top][bottom][index][help] */
 466 {
 467     action->synchronous = false;
 468     action->opaque->callback = cb;
 469     return services__execute_file(action) == pcmk_rc_ok;
 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
 481 services_set_op_pending(svc_action_t *op, DBusPendingCall *pending)
     /* [previous][next][first][last][top][bottom][index][help] */
 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
 501 services_action_cleanup(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 /*!
 540  * \internal
 541  * \brief Map an actual resource action result to a standard OCF result
 542  *
 543  * \param[in] standard     Agent standard (must not be "service")
 544  * \param[in] action       Action that result is for
 545  * \param[in] exit_status  Actual agent exit status
 546  *
 547  * \return Standard OCF result
 548  */
 549 enum ocf_exitcode
 550 services_result2ocf(const char *standard, const char *action, int exit_status)
     /* [previous][next][first][last][top][bottom][index][help] */
 551 {
 552     if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_OCF, pcmk__str_casei)) {
 553         return services__ocf2ocf(exit_status);
 554 
 555 #if SUPPORT_SYSTEMD
 556     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_SYSTEMD,
 557                             pcmk__str_casei)) {
 558         return services__systemd2ocf(exit_status);
 559 #endif
 560 
 561 #if SUPPORT_UPSTART
 562     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_UPSTART,
 563                             pcmk__str_casei)) {
 564         return services__upstart2ocf(exit_status);
 565 #endif
 566 
 567 #if SUPPORT_NAGIOS
 568     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_NAGIOS,
 569                             pcmk__str_casei)) {
 570         return services__nagios2ocf(exit_status);
 571 #endif
 572 
 573     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_LSB,
 574                             pcmk__str_casei)) {
 575         return services__lsb2ocf(action, exit_status);
 576 
 577     } else {
 578         crm_warn("Treating result from unknown standard '%s' as OCF",
 579                  ((standard == NULL)? "unspecified" : standard));
 580         return services__ocf2ocf(exit_status);
 581     }
 582 }
 583 
 584 void
 585 services_action_free(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 586 {
 587     unsigned int i;
 588 
 589     if (op == NULL) {
 590         return;
 591     }
 592 
 593     /* The operation should be removed from all tracking lists by this point.
 594      * If it's not, we have a bug somewhere, so bail. That may lead to a
 595      * memory leak, but it's better than a use-after-free segmentation fault.
 596      */
 597     CRM_CHECK(g_list_find(inflight_ops, op) == NULL, return);
 598     CRM_CHECK(g_list_find(blocked_ops, op) == NULL, return);
 599     CRM_CHECK((recurring_actions == NULL)
 600               || (g_hash_table_lookup(recurring_actions, op->id) == NULL),
 601               return);
 602 
 603     services_action_cleanup(op);
 604 
 605     if (op->opaque->repeat_timer) {
 606         g_source_remove(op->opaque->repeat_timer);
 607         op->opaque->repeat_timer = 0;
 608     }
 609 
 610     free(op->id);
 611     free(op->opaque->exec);
 612 
 613     for (i = 0; i < PCMK__NELEM(op->opaque->args); i++) {
 614         free(op->opaque->args[i]);
 615     }
 616 
 617     free(op->opaque->exit_reason);
 618     free(op->opaque);
 619     free(op->rsc);
 620     free(op->action);
 621 
 622     free(op->standard);
 623     free(op->agent);
 624     free(op->provider);
 625 
 626     free(op->stdout_data);
 627     free(op->stderr_data);
 628 
 629     if (op->params) {
 630         g_hash_table_destroy(op->params);
 631         op->params = NULL;
 632     }
 633 
 634     free(op);
 635 }
 636 
 637 gboolean
 638 cancel_recurring_action(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 639 {
 640     crm_info("Cancelling %s operation %s", op->standard, op->id);
 641 
 642     if (recurring_actions) {
 643         g_hash_table_remove(recurring_actions, op->id);
 644     }
 645 
 646     if (op->opaque->repeat_timer) {
 647         g_source_remove(op->opaque->repeat_timer);
 648         op->opaque->repeat_timer = 0;
 649     }
 650 
 651     return TRUE;
 652 }
 653 
 654 /*!
 655  * \brief Cancel a recurring action
 656  *
 657  * \param[in] name         Name of resource that operation is for
 658  * \param[in] action       Name of operation to cancel
 659  * \param[in] interval_ms  Interval of operation to cancel
 660  *
 661  * \return TRUE if action was successfully cancelled, FALSE otherwise
 662  */
 663 gboolean
 664 services_action_cancel(const char *name, const char *action, guint interval_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 665 {
 666     gboolean cancelled = FALSE;
 667     char *id = pcmk__op_key(name, action, interval_ms);
 668     svc_action_t *op = NULL;
 669 
 670     /* We can only cancel a recurring action */
 671     init_recurring_actions();
 672     op = g_hash_table_lookup(recurring_actions, id);
 673     if (op == NULL) {
 674         goto done;
 675     }
 676 
 677     // Tell services__finalize_async_op() not to reschedule the operation
 678     op->cancel = TRUE;
 679 
 680     /* Stop tracking it as a recurring operation, and stop its repeat timer */
 681     cancel_recurring_action(op);
 682 
 683     /* If the op has a PID, it's an in-flight child process, so kill it.
 684      *
 685      * Whether the kill succeeds or fails, the main loop will send the op to
 686      * async_action_complete() (and thus services__finalize_async_op()) when the
 687      * process goes away.
 688      */
 689     if (op->pid != 0) {
 690         crm_info("Terminating in-flight op %s[%d] early because it was cancelled",
 691                  id, op->pid);
 692         cancelled = mainloop_child_kill(op->pid);
 693         if (cancelled == FALSE) {
 694             crm_err("Termination of %s[%d] failed", id, op->pid);
 695         }
 696         goto done;
 697     }
 698 
 699 #if SUPPORT_DBUS
 700     // In-flight systemd and upstart ops don't have a pid
 701     if (inflight_systemd_or_upstart(op)) {
 702         inflight_ops = g_list_remove(inflight_ops, op);
 703 
 704         /* This will cause any result that comes in later to be discarded, so we
 705          * don't call the callback and free the operation twice.
 706          */
 707         services_action_cleanup(op);
 708     }
 709 #endif
 710 
 711     /* The rest of this is essentially equivalent to
 712      * services__finalize_async_op(), minus the handle_blocked_ops() call.
 713      */
 714 
 715     // Report operation as cancelled
 716     services__set_cancelled(op);
 717     if (op->opaque->callback) {
 718         op->opaque->callback(op);
 719     }
 720 
 721     blocked_ops = g_list_remove(blocked_ops, op);
 722     services_action_free(op);
 723     cancelled = TRUE;
 724     // @TODO Initiate handle_blocked_ops() asynchronously
 725 
 726 done:
 727     free(id);
 728     return cancelled;
 729 }
 730 
 731 gboolean
 732 services_action_kick(const char *name, const char *action, guint interval_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 733 {
 734     svc_action_t * op = NULL;
 735     char *id = pcmk__op_key(name, action, interval_ms);
 736 
 737     init_recurring_actions();
 738     op = g_hash_table_lookup(recurring_actions, id);
 739     free(id);
 740 
 741     if (op == NULL) {
 742         return FALSE;
 743     }
 744 
 745 
 746     if (op->pid || inflight_systemd_or_upstart(op)) {
 747         return TRUE;
 748     } else {
 749         if (op->opaque->repeat_timer) {
 750             g_source_remove(op->opaque->repeat_timer);
 751             op->opaque->repeat_timer = 0;
 752         }
 753         recurring_action_timer(op);
 754         return TRUE;
 755     }
 756 
 757 }
 758 
 759 /*!
 760  * \internal
 761  * \brief Add a new recurring operation, checking for duplicates
 762  *
 763  * \param[in] op               Operation to add
 764  *
 765  * \return TRUE if duplicate found (and reschedule), FALSE otherwise
 766  */
 767 static gboolean
 768 handle_duplicate_recurring(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 769 {
 770     svc_action_t * dup = NULL;
 771 
 772     /* check for duplicates */
 773     dup = g_hash_table_lookup(recurring_actions, op->id);
 774 
 775     if (dup && (dup != op)) {
 776         /* update user data */
 777         if (op->opaque->callback) {
 778             dup->opaque->callback = op->opaque->callback;
 779             dup->cb_data = op->cb_data;
 780             op->cb_data = NULL;
 781         }
 782         /* immediately execute the next interval */
 783         if (dup->pid != 0) {
 784             if (op->opaque->repeat_timer) {
 785                 g_source_remove(op->opaque->repeat_timer);
 786                 op->opaque->repeat_timer = 0;
 787             }
 788             recurring_action_timer(dup);
 789         }
 790         /* free the duplicate */
 791         services_action_free(op);
 792         return TRUE;
 793     }
 794 
 795     return FALSE;
 796 }
 797 
 798 /*!
 799  * \internal
 800  * \brief Execute an action appropriately according to its standard
 801  *
 802  * \param[in] op  Action to execute
 803  *
 804  * \return Standard Pacemaker return code
 805  * \retval EBUSY          Recurring operation could not be initiated
 806  * \retval pcmk_rc_error  Synchronous action failed
 807  * \retval pcmk_rc_ok     Synchronous action succeeded, or asynchronous action
 808  *                        should not be freed (because it's pending or because
 809  *                        it failed to execute and was already freed)
 810  *
 811  * \note If the return value for an asynchronous action is not pcmk_rc_ok, the
 812  *       caller is responsible for freeing the action.
 813  */
 814 static int
 815 execute_action(svc_action_t *op)
     /* [previous][next][first][last][top][bottom][index][help] */
 816 {
 817 #if SUPPORT_UPSTART
 818     if (pcmk__str_eq(op->standard, PCMK_RESOURCE_CLASS_UPSTART,
 819                      pcmk__str_casei)) {
 820         return services__execute_upstart(op);
 821     }
 822 #endif
 823 
 824 #if SUPPORT_SYSTEMD
 825     if (pcmk__str_eq(op->standard, PCMK_RESOURCE_CLASS_SYSTEMD,
 826                      pcmk__str_casei)) {
 827         return services__execute_systemd(op);
 828     }
 829 #endif
 830 
 831     return services__execute_file(op);
 832 }
 833 
 834 void
 835 services_add_inflight_op(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 836 {
 837     if (op == NULL) {
 838         return;
 839     }
 840 
 841     CRM_ASSERT(op->synchronous == FALSE);
 842 
 843     /* keep track of ops that are in-flight to avoid collisions in the same namespace */
 844     if (op->rsc) {
 845         inflight_ops = g_list_append(inflight_ops, op);
 846     }
 847 }
 848 
 849 /*!
 850  * \internal
 851  * \brief Stop tracking an operation that completed
 852  *
 853  * \param[in] op  Operation to stop tracking
 854  */
 855 void
 856 services_untrack_op(svc_action_t *op)
     /* [previous][next][first][last][top][bottom][index][help] */
 857 {
 858     /* Op is no longer in-flight or blocked */
 859     inflight_ops = g_list_remove(inflight_ops, op);
 860     blocked_ops = g_list_remove(blocked_ops, op);
 861 
 862     /* Op is no longer blocking other ops, so check if any need to run */
 863     handle_blocked_ops();
 864 }
 865 
 866 gboolean
 867 services_action_async_fork_notify(svc_action_t * op,
     /* [previous][next][first][last][top][bottom][index][help] */
 868                                   void (*action_callback) (svc_action_t *),
 869                                   void (*action_fork_callback) (svc_action_t *))
 870 {
 871     CRM_CHECK(op != NULL, return TRUE);
 872 
 873     op->synchronous = false;
 874     if (action_callback != NULL) {
 875         op->opaque->callback = action_callback;
 876     }
 877     if (action_fork_callback != NULL) {
 878         op->opaque->fork_callback = action_fork_callback;
 879     }
 880 
 881     if (op->interval_ms > 0) {
 882         init_recurring_actions();
 883         if (handle_duplicate_recurring(op)) {
 884             /* entry rescheduled, dup freed */
 885             /* exit early */
 886             return TRUE;
 887         }
 888         g_hash_table_replace(recurring_actions, op->id, op);
 889     }
 890 
 891     if (!pcmk_is_set(op->flags, SVC_ACTION_NON_BLOCKED)
 892         && op->rsc && is_op_blocked(op->rsc)) {
 893         blocked_ops = g_list_append(blocked_ops, op);
 894         return TRUE;
 895     }
 896 
 897     return execute_action(op) == pcmk_rc_ok;
 898 }
 899 
 900 gboolean
 901 services_action_async(svc_action_t * op,
     /* [previous][next][first][last][top][bottom][index][help] */
 902                       void (*action_callback) (svc_action_t *))
 903 {
 904     return services_action_async_fork_notify(op, action_callback, NULL);
 905 }
 906 
 907 static gboolean processing_blocked_ops = FALSE;
 908 
 909 gboolean
 910 is_op_blocked(const char *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 911 {
 912     GList *gIter = NULL;
 913     svc_action_t *op = NULL;
 914 
 915     for (gIter = inflight_ops; gIter != NULL; gIter = gIter->next) {
 916         op = gIter->data;
 917         if (pcmk__str_eq(op->rsc, rsc, pcmk__str_casei)) {
 918             return TRUE;
 919         }
 920     }
 921 
 922     return FALSE;
 923 }
 924 
 925 static void
 926 handle_blocked_ops(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 927 {
 928     GList *executed_ops = NULL;
 929     GList *gIter = NULL;
 930     svc_action_t *op = NULL;
 931 
 932     if (processing_blocked_ops) {
 933         /* avoid nested calling of this function */
 934         return;
 935     }
 936 
 937     processing_blocked_ops = TRUE;
 938 
 939     /* n^2 operation here, but blocked ops are incredibly rare. this list
 940      * will be empty 99% of the time. */
 941     for (gIter = blocked_ops; gIter != NULL; gIter = gIter->next) {
 942         op = gIter->data;
 943         if (is_op_blocked(op->rsc)) {
 944             continue;
 945         }
 946         executed_ops = g_list_append(executed_ops, op);
 947         if (execute_action(op) != pcmk_rc_ok) {
 948             /* this can cause this function to be called recursively
 949              * which is why we have processing_blocked_ops static variable */
 950             services__finalize_async_op(op);
 951         }
 952     }
 953 
 954     for (gIter = executed_ops; gIter != NULL; gIter = gIter->next) {
 955         op = gIter->data;
 956         blocked_ops = g_list_remove(blocked_ops, op);
 957     }
 958     g_list_free(executed_ops);
 959 
 960     processing_blocked_ops = FALSE;
 961 }
 962 
 963 /*!
 964  * \internal
 965  * \brief Execute a meta-data action appropriately to standard
 966  *
 967  * \param[in] op  Meta-data action to execute
 968  *
 969  * \return Standard Pacemaker return code
 970  */
 971 static int
 972 execute_metadata_action(svc_action_t *op)
     /* [previous][next][first][last][top][bottom][index][help] */
 973 {
 974     const char *class = op->standard;
 975 
 976     if (op->agent == NULL) {
 977         crm_info("Meta-data requested without specifying agent");
 978         services__set_result(op, services__generic_error(op),
 979                              PCMK_EXEC_ERROR_FATAL, "Agent not specified");
 980         return EINVAL;
 981     }
 982 
 983     if (class == NULL) {
 984         crm_info("Meta-data requested for agent %s without specifying class",
 985                 op->agent);
 986         services__set_result(op, services__generic_error(op),
 987                              PCMK_EXEC_ERROR_FATAL,
 988                              "Agent standard not specified");
 989         return EINVAL;
 990     }
 991 
 992     if (!strcmp(class, PCMK_RESOURCE_CLASS_SERVICE)) {
 993         class = resources_find_service_class(op->agent);
 994     }
 995     if (class == NULL) {
 996         crm_info("Meta-data requested for %s, but could not determine class",
 997                  op->agent);
 998         services__set_result(op, services__generic_error(op),
 999                              PCMK_EXEC_ERROR_HARD,
1000                              "Agent standard could not be determined");
1001         return EINVAL;
1002     }
1003 
1004     if (pcmk__str_eq(class, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei)) {
1005         return pcmk_legacy2rc(services__get_lsb_metadata(op->agent,
1006                                                          &op->stdout_data));
1007     }
1008 
1009 #if SUPPORT_NAGIOS
1010     if (pcmk__str_eq(class, PCMK_RESOURCE_CLASS_NAGIOS, pcmk__str_casei)) {
1011         return pcmk_legacy2rc(services__get_nagios_metadata(op->agent,
1012                                                             &op->stdout_data));
1013     }
1014 #endif
1015 
1016     return execute_action(op);
1017 }
1018 
1019 gboolean
1020 services_action_sync(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
1021 {
1022     gboolean rc = TRUE;
1023 
1024     if (op == NULL) {
1025         crm_trace("No operation to execute");
1026         return FALSE;
1027     }
1028 
1029     op->synchronous = true;
1030 
1031     if (pcmk__str_eq(op->action, "meta-data", pcmk__str_casei)) {
1032         /* Synchronous meta-data operations are handled specially. Since most
1033          * resource classes don't provide any meta-data, it has to be
1034          * synthesized from available information about the agent.
1035          *
1036          * services_action_async() doesn't treat meta-data actions specially, so
1037          * it will result in an error for classes that don't support the action.
1038          */
1039         rc = (execute_metadata_action(op) == pcmk_rc_ok);
1040     } else {
1041         rc = (execute_action(op) == pcmk_rc_ok);
1042     }
1043     crm_trace(" > " PCMK__OP_FMT ": %s = %d",
1044               op->rsc, op->action, op->interval_ms, op->opaque->exec, op->rc);
1045     if (op->stdout_data) {
1046         crm_trace(" >  stdout: %s", op->stdout_data);
1047     }
1048     if (op->stderr_data) {
1049         crm_trace(" >  stderr: %s", op->stderr_data);
1050     }
1051     return rc;
1052 }
1053 
1054 GList *
1055 get_directory_list(const char *root, gboolean files, gboolean executable)
     /* [previous][next][first][last][top][bottom][index][help] */
1056 {
1057     return services_os_get_directory_list(root, files, executable);
1058 }
1059 
1060 GList *
1061 resources_list_standards(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1062 {
1063     GList *standards = NULL;
1064 
1065     standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_OCF));
1066     standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_LSB));
1067     standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_SERVICE));
1068 
1069 #if SUPPORT_SYSTEMD
1070     {
1071         GList *agents = systemd_unit_listall();
1072 
1073         if (agents != NULL) {
1074             standards = g_list_append(standards,
1075                                       strdup(PCMK_RESOURCE_CLASS_SYSTEMD));
1076             g_list_free_full(agents, free);
1077         }
1078     }
1079 #endif
1080 
1081 #if SUPPORT_UPSTART
1082     {
1083         GList *agents = upstart_job_listall();
1084 
1085         if (agents != NULL) {
1086             standards = g_list_append(standards,
1087                                       strdup(PCMK_RESOURCE_CLASS_UPSTART));
1088             g_list_free_full(agents, free);
1089         }
1090     }
1091 #endif
1092 
1093 #if SUPPORT_NAGIOS
1094     {
1095         GList *agents = services__list_nagios_agents();
1096 
1097         if (agents != NULL) {
1098             standards = g_list_append(standards,
1099                                       strdup(PCMK_RESOURCE_CLASS_NAGIOS));
1100             g_list_free_full(agents, free);
1101         }
1102     }
1103 #endif
1104 
1105     return standards;
1106 }
1107 
1108 GList *
1109 resources_list_providers(const char *standard)
     /* [previous][next][first][last][top][bottom][index][help] */
1110 {
1111     if (pcmk_is_set(pcmk_get_ra_caps(standard), pcmk_ra_cap_provider)) {
1112         return resources_os_list_ocf_providers();
1113     }
1114 
1115     return NULL;
1116 }
1117 
1118 GList *
1119 resources_list_agents(const char *standard, const char *provider)
     /* [previous][next][first][last][top][bottom][index][help] */
1120 {
1121     if ((standard == NULL)
1122         || (strcasecmp(standard, PCMK_RESOURCE_CLASS_SERVICE) == 0)) {
1123 
1124         GList *tmp1;
1125         GList *tmp2;
1126         GList *result = services__list_lsb_agents();
1127 
1128         if (standard == NULL) {
1129             tmp1 = result;
1130             tmp2 = resources_os_list_ocf_agents(NULL);
1131             if (tmp2) {
1132                 result = g_list_concat(tmp1, tmp2);
1133             }
1134         }
1135 #if SUPPORT_SYSTEMD
1136         tmp1 = result;
1137         tmp2 = systemd_unit_listall();
1138         if (tmp2) {
1139             result = g_list_concat(tmp1, tmp2);
1140         }
1141 #endif
1142 
1143 #if SUPPORT_UPSTART
1144         tmp1 = result;
1145         tmp2 = upstart_job_listall();
1146         if (tmp2) {
1147             result = g_list_concat(tmp1, tmp2);
1148         }
1149 #endif
1150 
1151         return result;
1152 
1153     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
1154         return resources_os_list_ocf_agents(provider);
1155     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_LSB) == 0) {
1156         return services__list_lsb_agents();
1157 #if SUPPORT_SYSTEMD
1158     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
1159         return systemd_unit_listall();
1160 #endif
1161 #if SUPPORT_UPSTART
1162     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_UPSTART) == 0) {
1163         return upstart_job_listall();
1164 #endif
1165 #if SUPPORT_NAGIOS
1166     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_NAGIOS) == 0) {
1167         return services__list_nagios_agents();
1168 #endif
1169     }
1170 
1171     return NULL;
1172 }
1173 
1174 gboolean
1175 resources_agent_exists(const char *standard, const char *provider, const char *agent)
     /* [previous][next][first][last][top][bottom][index][help] */
1176 {
1177     GList *standards = NULL;
1178     GList *providers = NULL;
1179     GList *iter = NULL;
1180     gboolean rc = FALSE;
1181     gboolean has_providers = FALSE;
1182 
1183     standards = resources_list_standards();
1184     for (iter = standards; iter != NULL; iter = iter->next) {
1185         if (pcmk__str_eq(iter->data, standard, pcmk__str_none)) {
1186             rc = TRUE;
1187             break;
1188         }
1189     }
1190 
1191     if (rc == FALSE) {
1192         goto done;
1193     }
1194 
1195     rc = FALSE;
1196 
1197     has_providers = pcmk_is_set(pcmk_get_ra_caps(standard), pcmk_ra_cap_provider);
1198     if (has_providers == TRUE && provider != NULL) {
1199         providers = resources_list_providers(standard);
1200         for (iter = providers; iter != NULL; iter = iter->next) {
1201             if (pcmk__str_eq(iter->data, provider, pcmk__str_none)) {
1202                 rc = TRUE;
1203                 break;
1204             }
1205         }
1206     } else if (has_providers == FALSE && provider == NULL) {
1207         rc = TRUE;
1208     }
1209 
1210     if (rc == FALSE) {
1211         goto done;
1212     }
1213 
1214     if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_SERVICE, pcmk__str_casei)) {
1215         if (services__lsb_agent_exists(agent)) {
1216             rc = TRUE;
1217 #if SUPPORT_SYSTEMD
1218         } else if (systemd_unit_exists(agent)) {
1219             rc = TRUE;
1220 #endif
1221 
1222 #if SUPPORT_UPSTART
1223         } else if (upstart_job_exists(agent)) {
1224             rc = TRUE;
1225 #endif
1226         } else {
1227             rc = FALSE;
1228         }
1229 
1230     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_OCF, pcmk__str_casei)) {
1231         rc = services__ocf_agent_exists(provider, agent);
1232 
1233     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei)) {
1234         rc = services__lsb_agent_exists(agent);
1235 
1236 #if SUPPORT_SYSTEMD
1237     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_SYSTEMD, pcmk__str_casei)) {
1238         rc = systemd_unit_exists(agent);
1239 #endif
1240 
1241 #if SUPPORT_UPSTART
1242     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_UPSTART, pcmk__str_casei)) {
1243         rc = upstart_job_exists(agent);
1244 #endif
1245 
1246 #if SUPPORT_NAGIOS
1247     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_NAGIOS, pcmk__str_casei)) {
1248         rc = services__nagios_agent_exists(agent);
1249 #endif
1250 
1251     } else {
1252         rc = FALSE;
1253     }
1254 
1255 done:
1256     g_list_free(standards);
1257     g_list_free(providers);
1258     return rc;
1259 }
1260 
1261 /*!
1262  * \internal
1263  * \brief Set the result of an action
1264  *
1265  * \param[out] action        Where to set action result
1266  * \param[in]  agent_status  Exit status to set
1267  * \param[in]  exec_status   Execution status to set
1268  * \param[in]  reason        Human-friendly description of event to set
1269  */
1270 void
1271 services__set_result(svc_action_t *action, int agent_status,
     /* [previous][next][first][last][top][bottom][index][help] */
1272                      enum pcmk_exec_status exec_status, const char *reason)
1273 {
1274     if (action == NULL) {
1275         return;
1276     }
1277 
1278     action->rc = agent_status;
1279     action->status = exec_status;
1280 
1281     if (!pcmk__str_eq(action->opaque->exit_reason, reason,
1282                       pcmk__str_none)) {
1283         free(action->opaque->exit_reason);
1284         action->opaque->exit_reason = (reason == NULL)? NULL : strdup(reason);
1285     }
1286 }
1287 
1288 /*!
1289  * \internal
1290  * \brief Set the result of an action, with a formatted exit reason
1291  *
1292  * \param[out] action        Where to set action result
1293  * \param[in]  agent_status  Exit status to set
1294  * \param[in]  exec_status   Execution status to set
1295  * \param[in]  format        printf-style format for a human-friendly
1296  *                           description of reason for result
1297  * \param[in]  ...           arguments for \p format
1298  */
1299 void
1300 services__format_result(svc_action_t *action, int agent_status,
     /* [previous][next][first][last][top][bottom][index][help] */
1301                         enum pcmk_exec_status exec_status,
1302                         const char *format, ...)
1303 {
1304     va_list ap;
1305     int len = 0;
1306     char *reason = NULL;
1307 
1308     if (action == NULL) {
1309         return;
1310     }
1311 
1312     action->rc = agent_status;
1313     action->status = exec_status;
1314 
1315     if (format != NULL) {
1316         va_start(ap, format);
1317         len = vasprintf(&reason, format, ap);
1318         CRM_ASSERT(len > 0);
1319         va_end(ap);
1320     }
1321     free(action->opaque->exit_reason);
1322     action->opaque->exit_reason = reason;
1323 }
1324 
1325 /*!
1326  * \internal
1327  * \brief Set the result of an action to cancelled
1328  *
1329  * \param[out] action        Where to set action result
1330  *
1331  * \note This sets execution status but leaves the exit status unchanged
1332  */
1333 void
1334 services__set_cancelled(svc_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
1335 {
1336     if (action != NULL) {
1337         action->status = PCMK_EXEC_CANCELLED;
1338         free(action->opaque->exit_reason);
1339         action->opaque->exit_reason = NULL;
1340     }
1341 }
1342 
1343 /*!
1344  * \internal
1345  * \brief Get a readable description of what an action is for
1346  *
1347  * \param[in] action  Action to check
1348  *
1349  * \return Readable name for the kind of \p action
1350  */
1351 const char *
1352 services__action_kind(svc_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
1353 {
1354     if ((action == NULL) || (action->standard == NULL)) {
1355         return "Process";
1356     } else if (pcmk__str_eq(action->standard, PCMK_RESOURCE_CLASS_STONITH,
1357                             pcmk__str_none)) {
1358         return "Fence agent";
1359     } else if (pcmk__str_eq(action->standard, PCMK_RESOURCE_CLASS_ALERT,
1360                             pcmk__str_none)) {
1361         return "Alert agent";
1362     } else {
1363         return "Resource agent";
1364     }
1365 }
1366 
1367 /*!
1368  * \internal
1369  * \brief Get the exit reason of an action
1370  *
1371  * \param[in] action  Action to check
1372  *
1373  * \return Action's exit reason (or NULL if none)
1374  */
1375 const char *
1376 services__exit_reason(svc_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
1377 {
1378     return action->opaque->exit_reason;
1379 }
1380 
1381 /*!
1382  * \internal
1383  * \brief Steal stdout from an action
1384  *
1385  * \param[in] action  Action whose stdout is desired
1386  *
1387  * \return Action's stdout (which may be NULL)
1388  * \note Upon return, \p action will no longer track the output, so it is the
1389  *       caller's responsibility to free the return value.
1390  */
1391 char *
1392 services__grab_stdout(svc_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
1393 {
1394     char *output = action->stdout_data;
1395 
1396     action->stdout_data = NULL;
1397     return output;
1398 }
1399 
1400 /*!
1401  * \internal
1402  * \brief Steal stderr from an action
1403  *
1404  * \param[in] action  Action whose stderr is desired
1405  *
1406  * \return Action's stderr (which may be NULL)
1407  * \note Upon return, \p action will no longer track the output, so it is the
1408  *       caller's responsibility to free the return value.
1409  */
1410 char *
1411 services__grab_stderr(svc_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
1412 {
1413     char *output = action->stderr_data;
1414 
1415     action->stderr_data = NULL;
1416     return output;
1417 }

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