LCOV - code coverage report
Current view: top level - services - services.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 531 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 42 0.0 %

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

Generated by: LCOV version 1.14