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

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

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