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. dup_file_path
  6. services__create_resource_action
  7. resources_action_create
  8. services_action_create_generic
  9. services_alert_create
  10. services_action_user
  11. services_alert_async
  12. services_set_op_pending
  13. services_action_cleanup
  14. services_action_free
  15. cancel_recurring_action
  16. services_action_cancel
  17. services_action_kick
  18. handle_duplicate_recurring
  19. action_exec_helper
  20. services_add_inflight_op
  21. services_untrack_op
  22. services_action_async_fork_notify
  23. services_action_async
  24. is_op_blocked
  25. handle_blocked_ops
  26. action_get_metadata
  27. services_action_sync
  28. get_directory_list
  29. resources_list_standards
  30. resources_list_providers
  31. resources_list_agents
  32. resources_agent_exists

   1 /*
   2  * Copyright 2010-2021 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_lsb.h"
  32 
  33 #if SUPPORT_UPSTART
  34 #  include <upstart.h>
  35 #endif
  36 
  37 #if SUPPORT_SYSTEMD
  38 #  include <systemd.h>
  39 #endif
  40 
  41 #if SUPPORT_NAGIOS
  42 #  include <services_nagios.h>
  43 #endif
  44 
  45 /* TODO: Develop a rollover strategy */
  46 
  47 static int operations = 0;
  48 static GHashTable *recurring_actions = NULL;
  49 
  50 /* ops waiting to run async because of conflicting active
  51  * pending ops */
  52 static GList *blocked_ops = NULL;
  53 
  54 /* ops currently active (in-flight) */
  55 static GList *inflight_ops = NULL;
  56 
  57 static void handle_blocked_ops(void);
  58 
  59 /*!
  60  * \brief Find first service class that can provide a specified agent
  61  *
  62  * \param[in] agent  Name of agent to search for
  63  *
  64  * \return Service class if found, NULL otherwise
  65  *
  66  * \note The priority is LSB, then systemd, then upstart. It would be preferable
  67  *       to put systemd first, but LSB merely requires a file existence check,
  68  *       while systemd requires contacting D-Bus.
  69  */
  70 const char *
  71 resources_find_service_class(const char *agent)
     /* [previous][next][first][last][top][bottom][index][help] */
  72 {
  73     if (services__lsb_agent_exists(agent)) {
  74         return PCMK_RESOURCE_CLASS_LSB;
  75     }
  76 
  77 #if SUPPORT_SYSTEMD
  78     if (systemd_unit_exists(agent)) {
  79         return PCMK_RESOURCE_CLASS_SYSTEMD;
  80     }
  81 #endif
  82 
  83 #if SUPPORT_UPSTART
  84     if (upstart_job_exists(agent)) {
  85         return PCMK_RESOURCE_CLASS_UPSTART;
  86     }
  87 #endif
  88     return NULL;
  89 }
  90 
  91 static inline void
  92 init_recurring_actions(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  93 {
  94     if (recurring_actions == NULL) {
  95         recurring_actions = pcmk__strkey_table(NULL, NULL);
  96     }
  97 }
  98 
  99 /*!
 100  * \internal
 101  * \brief Check whether op is in-flight systemd or upstart op
 102  *
 103  * \param[in] op  Operation to check
 104  *
 105  * \return TRUE if op is in-flight systemd or upstart op
 106  */
 107 static inline gboolean
 108 inflight_systemd_or_upstart(svc_action_t *op)
     /* [previous][next][first][last][top][bottom][index][help] */
 109 {
 110     return pcmk__strcase_any_of(op->standard, PCMK_RESOURCE_CLASS_SYSTEMD,
 111                            PCMK_RESOURCE_CLASS_UPSTART, NULL) &&
 112            g_list_find(inflight_ops, op) != NULL;
 113 }
 114 
 115 /*!
 116  * \internal
 117  * \brief Expand "service" alias to an actual resource class
 118  *
 119  * \param[in] rsc       Resource name (for logging only)
 120  * \param[in] standard  Resource class as configured
 121  * \param[in] agent     Agent name to look for
 122  *
 123  * \return Newly allocated string with actual resource class
 124  *
 125  * \note The caller is responsible for calling free() on the result.
 126  */
 127 static char *
 128 expand_resource_class(const char *rsc, const char *standard, const char *agent)
     /* [previous][next][first][last][top][bottom][index][help] */
 129 {
 130     char *expanded_class = NULL;
 131 
 132     if (strcasecmp(standard, PCMK_RESOURCE_CLASS_SERVICE) == 0) {
 133         const char *found_class = resources_find_service_class(agent);
 134 
 135         if (found_class) {
 136             crm_debug("Found %s agent %s for %s", found_class, agent, rsc);
 137             expanded_class = strdup(found_class);
 138         } else {
 139             crm_info("Assuming resource class lsb for agent %s for %s",
 140                      agent, rsc);
 141             expanded_class = strdup(PCMK_RESOURCE_CLASS_LSB);
 142         }
 143     } else {
 144         expanded_class = strdup(standard);
 145     }
 146     CRM_ASSERT(expanded_class);
 147     return expanded_class;
 148 }
 149 
 150 #if SUPPORT_NAGIOS
 151 /*!
 152  * \brief Duplicate a file path, inserting a prefix if not absolute
 153  *
 154  * \param[in] filename  File path to duplicate
 155  * \param[in] dirname   If filename is not absolute, prefix to add
 156  *
 157  * \return Newly allocated memory with full path
 158  */
 159 static char *
 160 dup_file_path(const char *filename, const char *dirname)
     /* [previous][next][first][last][top][bottom][index][help] */
 161 {
 162     return (*filename == '/')? strdup(filename)
 163            : crm_strdup_printf("%s/%s", dirname, filename);
 164 }
 165 #endif
 166 
 167 svc_action_t *
 168 services__create_resource_action(const char *name, const char *standard,
     /* [previous][next][first][last][top][bottom][index][help] */
 169                         const char *provider, const char *agent,
 170                         const char *action, guint interval_ms, int timeout,
 171                         GHashTable *params, enum svc_action_flags flags)
 172 {
 173     svc_action_t *op = NULL;
 174     uint32_t ra_caps = 0;
 175 
 176     /*
 177      * Do some up front sanity checks before we go off and
 178      * build the svc_action_t instance.
 179      */
 180 
 181     if (pcmk__str_empty(name)) {
 182         crm_err("Cannot create operation without resource name");
 183         goto return_error;
 184     }
 185 
 186     if (pcmk__str_empty(standard)) {
 187         crm_err("Cannot create operation for %s without resource class", name);
 188         goto return_error;
 189     }
 190     ra_caps = pcmk_get_ra_caps(standard);
 191 
 192     if (pcmk_is_set(ra_caps, pcmk_ra_cap_provider)
 193         && pcmk__str_empty(provider)) {
 194         crm_err("Cannot create operation for %s without provider", name);
 195         goto return_error;
 196     }
 197 
 198     if (pcmk__str_empty(agent)) {
 199         crm_err("Cannot create operation for %s without agent name", name);
 200         goto return_error;
 201     }
 202 
 203     if (pcmk__str_empty(action)) {
 204         crm_err("Cannot create operation for %s without operation name", name);
 205         goto return_error;
 206     }
 207 
 208     /*
 209      * Sanity checks passed, proceed!
 210      */
 211 
 212     op = calloc(1, sizeof(svc_action_t));
 213     op->opaque = calloc(1, sizeof(svc_action_private_t));
 214     op->rsc = strdup(name);
 215     op->interval_ms = interval_ms;
 216     op->timeout = timeout;
 217     op->standard = expand_resource_class(name, standard, agent);
 218     op->agent = strdup(agent);
 219     op->sequence = ++operations;
 220     op->flags = flags;
 221     op->id = pcmk__op_key(name, action, interval_ms);
 222 
 223     if (pcmk_is_set(ra_caps, pcmk_ra_cap_status)
 224         && pcmk__str_eq(action, "monitor", pcmk__str_casei)) {
 225 
 226         op->action = strdup("status");
 227     } else {
 228         op->action = strdup(action);
 229     }
 230 
 231     if (pcmk_is_set(ra_caps, pcmk_ra_cap_provider)) {
 232         op->provider = strdup(provider);
 233     }
 234 
 235     if (pcmk_is_set(ra_caps, pcmk_ra_cap_params)) {
 236         op->params = params;
 237         params = NULL; // so we don't free them in this function
 238     }
 239 
 240     if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
 241         char *dirs = strdup(OCF_RA_PATH);
 242         char *dir = NULL;
 243         char *buf = NULL;
 244         struct stat st;
 245 
 246         if (pcmk__str_empty(dirs)) {
 247             free(dirs);
 248             services__handle_exec_error(op, ENOMEM);
 249             return op;
 250         }
 251 
 252         for (dir = strtok(dirs, ":"); dir != NULL; dir = strtok(NULL, ":")) {
 253             buf = crm_strdup_printf("%s/%s/%s", dir, provider, agent);
 254             if (stat(buf, &st) == 0) {
 255                 break;
 256             }
 257             free(buf);
 258             buf = NULL;
 259         }
 260 
 261         free(dirs);
 262 
 263         if (buf) {
 264             op->opaque->exec = buf;
 265         } else {
 266             services__handle_exec_error(op, ENOENT);
 267             return op;
 268         }
 269 
 270         op->opaque->args[0] = strdup(op->opaque->exec);
 271         op->opaque->args[1] = strdup(op->action);
 272 
 273     } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_LSB) == 0) {
 274         op->opaque->exec = services__lsb_agent_path(op->agent);
 275         op->opaque->args[0] = strdup(op->opaque->exec);
 276         op->opaque->args[1] = strdup(op->action);
 277 
 278 #if SUPPORT_SYSTEMD
 279     } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
 280         op->opaque->exec = strdup("systemd-dbus");
 281 #endif
 282 #if SUPPORT_UPSTART
 283     } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_UPSTART) == 0) {
 284         op->opaque->exec = strdup("upstart-dbus");
 285 #endif
 286 #if SUPPORT_NAGIOS
 287     } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_NAGIOS) == 0) {
 288         op->opaque->exec = dup_file_path(op->agent, NAGIOS_PLUGIN_DIR);
 289         op->opaque->args[0] = strdup(op->opaque->exec);
 290 
 291         if (pcmk__str_eq(op->action, "monitor", pcmk__str_casei) && (op->interval_ms == 0)) {
 292             /* Invoke --version for a nagios probe */
 293             op->opaque->args[1] = strdup("--version");
 294 
 295         } else if (op->params) {
 296             GHashTableIter iter;
 297             char *key = NULL;
 298             char *value = NULL;
 299             int index = 1;
 300             static int args_size = sizeof(op->opaque->args) / sizeof(char *);
 301 
 302             g_hash_table_iter_init(&iter, op->params);
 303 
 304             while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value) &&
 305                    index <= args_size - 3) {
 306 
 307                 if (pcmk__str_eq(key, XML_ATTR_CRM_VERSION, pcmk__str_casei) || strstr(key, CRM_META "_")) {
 308                     continue;
 309                 }
 310                 op->opaque->args[index++] = crm_strdup_printf("--%s", key);
 311                 op->opaque->args[index++] = strdup(value);
 312             }
 313         }
 314 
 315         // Nagios actions don't need to keep the parameters
 316         if (op->params != NULL) {
 317             g_hash_table_destroy(op->params);
 318             op->params = NULL;
 319         }
 320 #endif
 321     } else {
 322         crm_err("Unknown resource standard: %s", op->standard);
 323         services__handle_exec_error(op, ENOENT);
 324     }
 325 
 326   return_error:
 327     if(params) {
 328         g_hash_table_destroy(params);
 329     }
 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         return op;
 348     }
 349 }
 350 
 351 svc_action_t *
 352 services_action_create_generic(const char *exec, const char *args[])
     /* [previous][next][first][last][top][bottom][index][help] */
 353 {
 354     svc_action_t *op;
 355     unsigned int cur_arg;
 356 
 357     op = calloc(1, sizeof(*op));
 358     op->opaque = calloc(1, sizeof(svc_action_private_t));
 359 
 360     op->opaque->exec = strdup(exec);
 361     op->opaque->args[0] = strdup(exec);
 362 
 363     for (cur_arg = 1; args && args[cur_arg - 1]; cur_arg++) {
 364         op->opaque->args[cur_arg] = strdup(args[cur_arg - 1]);
 365 
 366         if (cur_arg == PCMK__NELEM(op->opaque->args) - 1) {
 367             crm_err("svc_action_t args list not long enough for '%s' execution request.", exec);
 368             break;
 369         }
 370     }
 371 
 372     return op;
 373 }
 374 
 375 /*!
 376  * \brief Create an alert agent action
 377  *
 378  * \param[in] id        Alert ID
 379  * \param[in] exec      Path to alert agent executable
 380  * \param[in] timeout   Action timeout
 381  * \param[in] params    Parameters to use with action
 382  * \param[in] sequence  Action sequence number
 383  * \param[in] cb_data   Data to pass to callback function
 384  *
 385  * \return New action on success, NULL on error
 386  * \note It is the caller's responsibility to free cb_data.
 387  *       The caller should not free params explicitly.
 388  */
 389 svc_action_t *
 390 services_alert_create(const char *id, const char *exec, int timeout,
     /* [previous][next][first][last][top][bottom][index][help] */
 391                       GHashTable *params, int sequence, void *cb_data)
 392 {
 393     svc_action_t *action = services_action_create_generic(exec, NULL);
 394 
 395     CRM_ASSERT(action);
 396     action->timeout = timeout;
 397     action->id = strdup(id);
 398     action->params = params;
 399     action->sequence = sequence;
 400     action->cb_data = cb_data;
 401     return action;
 402 }
 403 
 404 /*!
 405  * \brief Set the user and group that an action will execute as
 406  *
 407  * \param[in,out] action  Action to modify
 408  * \param[in]     user    Name of user to execute action as
 409  * \param[in]     group   Name of group to execute action as
 410  *
 411  * \return pcmk_ok on success, -errno otherwise
 412  *
 413  * \note This will have no effect unless the process executing the action runs
 414  *       as root, and the action is not a systemd or upstart action.
 415  *       We could implement this for systemd by adding User= and Group= to
 416  *       [Service] in the override file, but that seems more likely to cause
 417  *       problems than be useful.
 418  */
 419 int
 420 services_action_user(svc_action_t *op, const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 421 {
 422     CRM_CHECK((op != NULL) && (user != NULL), return -EINVAL);
 423     return crm_user_lookup(user, &(op->opaque->uid), &(op->opaque->gid));
 424 }
 425 
 426 /*!
 427  * \brief Execute an alert agent action
 428  *
 429  * \param[in] action  Action to execute
 430  * \param[in] cb      Function to call when action completes
 431  *
 432  * \return TRUE if the library will free action, FALSE otherwise
 433  *
 434  * \note If this function returns FALSE, it is the caller's responsibility to
 435  *       free the action with services_action_free().
 436  */
 437 gboolean
 438 services_alert_async(svc_action_t *action, void (*cb)(svc_action_t *op))
     /* [previous][next][first][last][top][bottom][index][help] */
 439 {
 440     action->synchronous = false;
 441     action->opaque->callback = cb;
 442     return services_os_action_execute(action);
 443 }
 444 
 445 #if SUPPORT_DBUS
 446 /*!
 447  * \internal
 448  * \brief Update operation's pending DBus call, unreferencing old one if needed
 449  *
 450  * \param[in,out] op       Operation to modify
 451  * \param[in]     pending  Pending call to set
 452  */
 453 void
 454 services_set_op_pending(svc_action_t *op, DBusPendingCall *pending)
     /* [previous][next][first][last][top][bottom][index][help] */
 455 {
 456     if (op->opaque->pending && (op->opaque->pending != pending)) {
 457         if (pending) {
 458             crm_info("Lost pending %s DBus call (%p)", op->id, op->opaque->pending);
 459         } else {
 460             crm_trace("Done with pending %s DBus call (%p)", op->id, op->opaque->pending);
 461         }
 462         dbus_pending_call_unref(op->opaque->pending);
 463     }
 464     op->opaque->pending = pending;
 465     if (pending) {
 466         crm_trace("Updated pending %s DBus call (%p)", op->id, pending);
 467     } else {
 468         crm_trace("Cleared pending %s DBus call", op->id);
 469     }
 470 }
 471 #endif
 472 
 473 void
 474 services_action_cleanup(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 475 {
 476     if ((op == NULL) || (op->opaque == NULL)) {
 477         return;
 478     }
 479 
 480 #if SUPPORT_DBUS
 481     if(op->opaque->timerid != 0) {
 482         crm_trace("Removing timer for call %s to %s", op->action, op->rsc);
 483         g_source_remove(op->opaque->timerid);
 484         op->opaque->timerid = 0;
 485     }
 486 
 487     if(op->opaque->pending) {
 488         if (dbus_pending_call_get_completed(op->opaque->pending)) {
 489             // This should never be the case
 490             crm_warn("Result of %s op %s was unhandled",
 491                      op->standard, op->id);
 492         } else {
 493             crm_debug("Will ignore any result of canceled %s op %s",
 494                       op->standard, op->id);
 495         }
 496         dbus_pending_call_cancel(op->opaque->pending);
 497         services_set_op_pending(op, NULL);
 498     }
 499 #endif
 500 
 501     if (op->opaque->stderr_gsource) {
 502         mainloop_del_fd(op->opaque->stderr_gsource);
 503         op->opaque->stderr_gsource = NULL;
 504     }
 505 
 506     if (op->opaque->stdout_gsource) {
 507         mainloop_del_fd(op->opaque->stdout_gsource);
 508         op->opaque->stdout_gsource = NULL;
 509     }
 510 }
 511 
 512 void
 513 services_action_free(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 514 {
 515     unsigned int i;
 516 
 517     if (op == NULL) {
 518         return;
 519     }
 520 
 521     /* The operation should be removed from all tracking lists by this point.
 522      * If it's not, we have a bug somewhere, so bail. That may lead to a
 523      * memory leak, but it's better than a use-after-free segmentation fault.
 524      */
 525     CRM_CHECK(g_list_find(inflight_ops, op) == NULL, return);
 526     CRM_CHECK(g_list_find(blocked_ops, op) == NULL, return);
 527     CRM_CHECK((recurring_actions == NULL)
 528               || (g_hash_table_lookup(recurring_actions, op->id) == NULL),
 529               return);
 530 
 531     services_action_cleanup(op);
 532 
 533     if (op->opaque->repeat_timer) {
 534         g_source_remove(op->opaque->repeat_timer);
 535         op->opaque->repeat_timer = 0;
 536     }
 537 
 538     free(op->id);
 539     free(op->opaque->exec);
 540 
 541     for (i = 0; i < PCMK__NELEM(op->opaque->args); i++) {
 542         free(op->opaque->args[i]);
 543     }
 544 
 545     free(op->opaque);
 546     free(op->rsc);
 547     free(op->action);
 548 
 549     free(op->standard);
 550     free(op->agent);
 551     free(op->provider);
 552 
 553     free(op->stdout_data);
 554     free(op->stderr_data);
 555 
 556     if (op->params) {
 557         g_hash_table_destroy(op->params);
 558         op->params = NULL;
 559     }
 560 
 561     free(op);
 562 }
 563 
 564 gboolean
 565 cancel_recurring_action(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 566 {
 567     crm_info("Cancelling %s operation %s", op->standard, op->id);
 568 
 569     if (recurring_actions) {
 570         g_hash_table_remove(recurring_actions, op->id);
 571     }
 572 
 573     if (op->opaque->repeat_timer) {
 574         g_source_remove(op->opaque->repeat_timer);
 575         op->opaque->repeat_timer = 0;
 576     }
 577 
 578     return TRUE;
 579 }
 580 
 581 /*!
 582  * \brief Cancel a recurring action
 583  *
 584  * \param[in] name         Name of resource that operation is for
 585  * \param[in] action       Name of operation to cancel
 586  * \param[in] interval_ms  Interval of operation to cancel
 587  *
 588  * \return TRUE if action was successfully cancelled, FALSE otherwise
 589  */
 590 gboolean
 591 services_action_cancel(const char *name, const char *action, guint interval_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 592 {
 593     gboolean cancelled = FALSE;
 594     char *id = pcmk__op_key(name, action, interval_ms);
 595     svc_action_t *op = NULL;
 596 
 597     /* We can only cancel a recurring action */
 598     init_recurring_actions();
 599     op = g_hash_table_lookup(recurring_actions, id);
 600     if (op == NULL) {
 601         goto done;
 602     }
 603 
 604     /* Tell operation_finalize() not to reschedule the operation */
 605     op->cancel = TRUE;
 606 
 607     /* Stop tracking it as a recurring operation, and stop its repeat timer */
 608     cancel_recurring_action(op);
 609 
 610     /* If the op has a PID, it's an in-flight child process, so kill it.
 611      *
 612      * Whether the kill succeeds or fails, the main loop will send the op to
 613      * operation_finished() (and thus operation_finalize()) when the process
 614      * goes away.
 615      */
 616     if (op->pid != 0) {
 617         crm_info("Terminating in-flight op %s[%d] early because it was cancelled",
 618                  id, op->pid);
 619         cancelled = mainloop_child_kill(op->pid);
 620         if (cancelled == FALSE) {
 621             crm_err("Termination of %s[%d] failed", id, op->pid);
 622         }
 623         goto done;
 624     }
 625 
 626 #if SUPPORT_DBUS
 627     // In-flight systemd and upstart ops don't have a pid
 628     if (inflight_systemd_or_upstart(op)) {
 629         inflight_ops = g_list_remove(inflight_ops, op);
 630 
 631         /* This will cause any result that comes in later to be discarded, so we
 632          * don't call the callback and free the operation twice.
 633          */
 634         services_action_cleanup(op);
 635     }
 636 #endif
 637 
 638     // The rest of this is essentially equivalent to operation_finalize(),
 639     // except without calling handle_blocked_ops()
 640 
 641     // Report operation as cancelled
 642     op->status = PCMK_LRM_OP_CANCELLED;
 643     if (op->opaque->callback) {
 644         op->opaque->callback(op);
 645     }
 646 
 647     blocked_ops = g_list_remove(blocked_ops, op);
 648     services_action_free(op);
 649     cancelled = TRUE;
 650     // @TODO Initiate handle_blocked_ops() asynchronously
 651 
 652 done:
 653     free(id);
 654     return cancelled;
 655 }
 656 
 657 gboolean
 658 services_action_kick(const char *name, const char *action, guint interval_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 659 {
 660     svc_action_t * op = NULL;
 661     char *id = pcmk__op_key(name, action, interval_ms);
 662 
 663     init_recurring_actions();
 664     op = g_hash_table_lookup(recurring_actions, id);
 665     free(id);
 666 
 667     if (op == NULL) {
 668         return FALSE;
 669     }
 670 
 671 
 672     if (op->pid || inflight_systemd_or_upstart(op)) {
 673         return TRUE;
 674     } else {
 675         if (op->opaque->repeat_timer) {
 676             g_source_remove(op->opaque->repeat_timer);
 677             op->opaque->repeat_timer = 0;
 678         }
 679         recurring_action_timer(op);
 680         return TRUE;
 681     }
 682 
 683 }
 684 
 685 /*!
 686  * \internal
 687  * \brief Add a new recurring operation, checking for duplicates
 688  *
 689  * \param[in] op               Operation to add
 690  *
 691  * \return TRUE if duplicate found (and reschedule), FALSE otherwise
 692  */
 693 static gboolean
 694 handle_duplicate_recurring(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 695 {
 696     svc_action_t * dup = NULL;
 697 
 698     /* check for duplicates */
 699     dup = g_hash_table_lookup(recurring_actions, op->id);
 700 
 701     if (dup && (dup != op)) {
 702         /* update user data */
 703         if (op->opaque->callback) {
 704             dup->opaque->callback = op->opaque->callback;
 705             dup->cb_data = op->cb_data;
 706             op->cb_data = NULL;
 707         }
 708         /* immediately execute the next interval */
 709         if (dup->pid != 0) {
 710             if (op->opaque->repeat_timer) {
 711                 g_source_remove(op->opaque->repeat_timer);
 712                 op->opaque->repeat_timer = 0;
 713             }
 714             recurring_action_timer(dup);
 715         }
 716         /* free the duplicate */
 717         services_action_free(op);
 718         return TRUE;
 719     }
 720 
 721     return FALSE;
 722 }
 723 
 724 inline static gboolean
 725 action_exec_helper(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 726 {
 727     /* Whether a/synchronous must be decided (op->synchronous) beforehand. */
 728     if (op->standard
 729         && (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_UPSTART) == 0)) {
 730 #if SUPPORT_UPSTART
 731         return upstart_job_exec(op);
 732 #endif
 733     } else if (op->standard && strcasecmp(op->standard,
 734                                           PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
 735 #if SUPPORT_SYSTEMD
 736         return systemd_unit_exec(op);
 737 #endif
 738     } else {
 739         return services_os_action_execute(op);
 740     }
 741     /* The 'op' has probably been freed if the execution functions return TRUE
 742        for the asynchronous 'op'. */
 743     /* Avoid using the 'op' in here. */
 744 
 745     return FALSE;
 746 }
 747 
 748 void
 749 services_add_inflight_op(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 750 {
 751     if (op == NULL) {
 752         return;
 753     }
 754 
 755     CRM_ASSERT(op->synchronous == FALSE);
 756 
 757     /* keep track of ops that are in-flight to avoid collisions in the same namespace */
 758     if (op->rsc) {
 759         inflight_ops = g_list_append(inflight_ops, op);
 760     }
 761 }
 762 
 763 /*!
 764  * \internal
 765  * \brief Stop tracking an operation that completed
 766  *
 767  * \param[in] op  Operation to stop tracking
 768  */
 769 void
 770 services_untrack_op(svc_action_t *op)
     /* [previous][next][first][last][top][bottom][index][help] */
 771 {
 772     /* Op is no longer in-flight or blocked */
 773     inflight_ops = g_list_remove(inflight_ops, op);
 774     blocked_ops = g_list_remove(blocked_ops, op);
 775 
 776     /* Op is no longer blocking other ops, so check if any need to run */
 777     handle_blocked_ops();
 778 }
 779 
 780 gboolean
 781 services_action_async_fork_notify(svc_action_t * op,
     /* [previous][next][first][last][top][bottom][index][help] */
 782                                   void (*action_callback) (svc_action_t *),
 783                                   void (*action_fork_callback) (svc_action_t *))
 784 {
 785     op->synchronous = false;
 786     if (action_callback) {
 787         op->opaque->callback = action_callback;
 788     }
 789     if (action_fork_callback) {
 790         op->opaque->fork_callback = action_fork_callback;
 791     }
 792 
 793     if (op->interval_ms > 0) {
 794         init_recurring_actions();
 795         if (handle_duplicate_recurring(op) == TRUE) {
 796             /* entry rescheduled, dup freed */
 797             /* exit early */
 798             return TRUE;
 799         }
 800         g_hash_table_replace(recurring_actions, op->id, op);
 801     }
 802 
 803     if (!pcmk_is_set(op->flags, SVC_ACTION_NON_BLOCKED)
 804         && op->rsc && is_op_blocked(op->rsc)) {
 805         blocked_ops = g_list_append(blocked_ops, op);
 806         return TRUE;
 807     }
 808 
 809     return action_exec_helper(op);
 810 }
 811 
 812 gboolean
 813 services_action_async(svc_action_t * op,
     /* [previous][next][first][last][top][bottom][index][help] */
 814                       void (*action_callback) (svc_action_t *))
 815 {
 816     return services_action_async_fork_notify(op, action_callback, NULL);
 817 }
 818 
 819 static gboolean processing_blocked_ops = FALSE;
 820 
 821 gboolean
 822 is_op_blocked(const char *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 823 {
 824     GList *gIter = NULL;
 825     svc_action_t *op = NULL;
 826 
 827     for (gIter = inflight_ops; gIter != NULL; gIter = gIter->next) {
 828         op = gIter->data;
 829         if (pcmk__str_eq(op->rsc, rsc, pcmk__str_casei)) {
 830             return TRUE;
 831         }
 832     }
 833 
 834     return FALSE;
 835 }
 836 
 837 static void
 838 handle_blocked_ops(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 839 {
 840     GList *executed_ops = NULL;
 841     GList *gIter = NULL;
 842     svc_action_t *op = NULL;
 843     gboolean res = FALSE;
 844 
 845     if (processing_blocked_ops) {
 846         /* avoid nested calling of this function */
 847         return;
 848     }
 849 
 850     processing_blocked_ops = TRUE;
 851 
 852     /* n^2 operation here, but blocked ops are incredibly rare. this list
 853      * will be empty 99% of the time. */
 854     for (gIter = blocked_ops; gIter != NULL; gIter = gIter->next) {
 855         op = gIter->data;
 856         if (is_op_blocked(op->rsc)) {
 857             continue;
 858         }
 859         executed_ops = g_list_append(executed_ops, op);
 860         res = action_exec_helper(op);
 861         if (res == FALSE) {
 862             op->status = PCMK_LRM_OP_ERROR;
 863             /* this can cause this function to be called recursively
 864              * which is why we have processing_blocked_ops static variable */
 865             operation_finalize(op);
 866         }
 867     }
 868 
 869     for (gIter = executed_ops; gIter != NULL; gIter = gIter->next) {
 870         op = gIter->data;
 871         blocked_ops = g_list_remove(blocked_ops, op);
 872     }
 873     g_list_free(executed_ops);
 874 
 875     processing_blocked_ops = FALSE;
 876 }
 877 
 878 static gboolean
 879 action_get_metadata(svc_action_t *op)
     /* [previous][next][first][last][top][bottom][index][help] */
 880 {
 881     const char *class = op->standard;
 882 
 883     if (op->agent == NULL) {
 884         crm_err("meta-data requested without specifying agent");
 885         return FALSE;
 886     }
 887 
 888     if (class == NULL) {
 889         crm_err("meta-data requested for agent %s without specifying class",
 890                 op->agent);
 891         return FALSE;
 892     }
 893 
 894     if (!strcmp(class, PCMK_RESOURCE_CLASS_SERVICE)) {
 895         class = resources_find_service_class(op->agent);
 896     }
 897 
 898     if (class == NULL) {
 899         crm_err("meta-data requested for %s, but could not determine class",
 900                 op->agent);
 901         return FALSE;
 902     }
 903 
 904     if (pcmk__str_eq(class, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei)) {
 905         return (services__get_lsb_metadata(op->agent, &op->stdout_data) >= 0);
 906     }
 907 
 908 #if SUPPORT_NAGIOS
 909     if (pcmk__str_eq(class, PCMK_RESOURCE_CLASS_NAGIOS, pcmk__str_casei)) {
 910         return services__get_nagios_metadata(op->agent, &op->stdout_data) >= 0;
 911     }
 912 #endif
 913 
 914     return action_exec_helper(op);
 915 }
 916 
 917 gboolean
 918 services_action_sync(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 919 {
 920     gboolean rc = TRUE;
 921 
 922     if (op == NULL) {
 923         crm_trace("No operation to execute");
 924         return FALSE;
 925     }
 926 
 927     op->synchronous = true;
 928 
 929     if (pcmk__str_eq(op->action, "meta-data", pcmk__str_casei)) {
 930         /* Synchronous meta-data operations are handled specially. Since most
 931          * resource classes don't provide any meta-data, it has to be
 932          * synthesized from available information about the agent.
 933          *
 934          * services_action_async() doesn't treat meta-data actions specially, so
 935          * it will result in an error for classes that don't support the action.
 936          */
 937         rc = action_get_metadata(op);
 938     } else {
 939         rc = action_exec_helper(op);
 940     }
 941     crm_trace(" > " PCMK__OP_FMT ": %s = %d",
 942               op->rsc, op->action, op->interval_ms, op->opaque->exec, op->rc);
 943     if (op->stdout_data) {
 944         crm_trace(" >  stdout: %s", op->stdout_data);
 945     }
 946     if (op->stderr_data) {
 947         crm_trace(" >  stderr: %s", op->stderr_data);
 948     }
 949     return rc;
 950 }
 951 
 952 GList *
 953 get_directory_list(const char *root, gboolean files, gboolean executable)
     /* [previous][next][first][last][top][bottom][index][help] */
 954 {
 955     return services_os_get_directory_list(root, files, executable);
 956 }
 957 
 958 GList *
 959 resources_list_standards(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 960 {
 961     GList *standards = NULL;
 962     GList *agents = NULL;
 963 
 964     standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_OCF));
 965     standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_LSB));
 966     standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_SERVICE));
 967 
 968 #if SUPPORT_SYSTEMD
 969     agents = systemd_unit_listall();
 970     if (agents) {
 971         standards = g_list_append(standards,
 972                                   strdup(PCMK_RESOURCE_CLASS_SYSTEMD));
 973         g_list_free_full(agents, free);
 974     }
 975 #endif
 976 
 977 #if SUPPORT_UPSTART
 978     agents = upstart_job_listall();
 979     if (agents) {
 980         standards = g_list_append(standards,
 981                                   strdup(PCMK_RESOURCE_CLASS_UPSTART));
 982         g_list_free_full(agents, free);
 983     }
 984 #endif
 985 
 986 #if SUPPORT_NAGIOS
 987     agents = services__list_nagios_agents();
 988     if (agents) {
 989         standards = g_list_append(standards,
 990                                   strdup(PCMK_RESOURCE_CLASS_NAGIOS));
 991         g_list_free_full(agents, free);
 992     }
 993 #endif
 994 
 995     return standards;
 996 }
 997 
 998 GList *
 999 resources_list_providers(const char *standard)
     /* [previous][next][first][last][top][bottom][index][help] */
1000 {
1001     if (pcmk_is_set(pcmk_get_ra_caps(standard), pcmk_ra_cap_provider)) {
1002         return resources_os_list_ocf_providers();
1003     }
1004 
1005     return NULL;
1006 }
1007 
1008 GList *
1009 resources_list_agents(const char *standard, const char *provider)
     /* [previous][next][first][last][top][bottom][index][help] */
1010 {
1011     if ((standard == NULL)
1012         || (strcasecmp(standard, PCMK_RESOURCE_CLASS_SERVICE) == 0)) {
1013 
1014         GList *tmp1;
1015         GList *tmp2;
1016         GList *result = services__list_lsb_agents();
1017 
1018         if (standard == NULL) {
1019             tmp1 = result;
1020             tmp2 = resources_os_list_ocf_agents(NULL);
1021             if (tmp2) {
1022                 result = g_list_concat(tmp1, tmp2);
1023             }
1024         }
1025 #if SUPPORT_SYSTEMD
1026         tmp1 = result;
1027         tmp2 = systemd_unit_listall();
1028         if (tmp2) {
1029             result = g_list_concat(tmp1, tmp2);
1030         }
1031 #endif
1032 
1033 #if SUPPORT_UPSTART
1034         tmp1 = result;
1035         tmp2 = upstart_job_listall();
1036         if (tmp2) {
1037             result = g_list_concat(tmp1, tmp2);
1038         }
1039 #endif
1040 
1041         return result;
1042 
1043     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
1044         return resources_os_list_ocf_agents(provider);
1045     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_LSB) == 0) {
1046         return services__list_lsb_agents();
1047 #if SUPPORT_SYSTEMD
1048     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
1049         return systemd_unit_listall();
1050 #endif
1051 #if SUPPORT_UPSTART
1052     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_UPSTART) == 0) {
1053         return upstart_job_listall();
1054 #endif
1055 #if SUPPORT_NAGIOS
1056     } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_NAGIOS) == 0) {
1057         return services__list_nagios_agents();
1058 #endif
1059     }
1060 
1061     return NULL;
1062 }
1063 
1064 gboolean
1065 resources_agent_exists(const char *standard, const char *provider, const char *agent)
     /* [previous][next][first][last][top][bottom][index][help] */
1066 {
1067     GList *standards = NULL;
1068     GList *providers = NULL;
1069     GList *iter = NULL;
1070     gboolean rc = FALSE;
1071     gboolean has_providers = FALSE;
1072 
1073     standards = resources_list_standards();
1074     for (iter = standards; iter != NULL; iter = iter->next) {
1075         if (pcmk__str_eq(iter->data, standard, pcmk__str_none)) {
1076             rc = TRUE;
1077             break;
1078         }
1079     }
1080 
1081     if (rc == FALSE) {
1082         goto done;
1083     }
1084 
1085     rc = FALSE;
1086 
1087     has_providers = pcmk_is_set(pcmk_get_ra_caps(standard), pcmk_ra_cap_provider);
1088     if (has_providers == TRUE && provider != NULL) {
1089         providers = resources_list_providers(standard);
1090         for (iter = providers; iter != NULL; iter = iter->next) {
1091             if (pcmk__str_eq(iter->data, provider, pcmk__str_none)) {
1092                 rc = TRUE;
1093                 break;
1094             }
1095         }
1096     } else if (has_providers == FALSE && provider == NULL) {
1097         rc = TRUE;
1098     }
1099 
1100     if (rc == FALSE) {
1101         goto done;
1102     }
1103 
1104     if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_SERVICE, pcmk__str_casei)) {
1105         if (services__lsb_agent_exists(agent)) {
1106             rc = TRUE;
1107 #if SUPPORT_SYSTEMD
1108         } else if (systemd_unit_exists(agent)) {
1109             rc = TRUE;
1110 #endif
1111 
1112 #if SUPPORT_UPSTART
1113         } else if (upstart_job_exists(agent)) {
1114             rc = TRUE;
1115 #endif
1116         } else {
1117             rc = FALSE;
1118         }
1119 
1120     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_OCF, pcmk__str_casei)) {
1121         rc = services__ocf_agent_exists(provider, agent);
1122 
1123     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei)) {
1124         rc = services__lsb_agent_exists(agent);
1125 
1126 #if SUPPORT_SYSTEMD
1127     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_SYSTEMD, pcmk__str_casei)) {
1128         rc = systemd_unit_exists(agent);
1129 #endif
1130 
1131 #if SUPPORT_UPSTART
1132     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_UPSTART, pcmk__str_casei)) {
1133         rc = upstart_job_exists(agent);
1134 #endif
1135 
1136 #if SUPPORT_NAGIOS
1137     } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_NAGIOS, pcmk__str_casei)) {
1138         rc = services__nagios_agent_exists(agent);
1139 #endif
1140 
1141     } else {
1142         rc = FALSE;
1143     }
1144 
1145 done:
1146     g_list_free(standards);
1147     g_list_free(providers);
1148     return rc;
1149 }

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