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

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