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

          Line data    Source code
       1             : /*
       2             :  * Copyright 2012-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             : #include <crm/crm.h>
      12             : #include <crm/common/xml.h>
      13             : #include <crm/services.h>
      14             : #include <crm/services_internal.h>
      15             : #include <crm/common/mainloop.h>
      16             : 
      17             : #include <sys/stat.h>
      18             : #include <gio/gio.h>
      19             : #include <services_private.h>
      20             : #include <systemd.h>
      21             : #include <dbus/dbus.h>
      22             : #include <pcmk-dbus.h>
      23             : 
      24             : static void invoke_unit_by_path(svc_action_t *op, const char *unit);
      25             : 
      26             : #define BUS_NAME         "org.freedesktop.systemd1"
      27             : #define BUS_NAME_MANAGER BUS_NAME ".Manager"
      28             : #define BUS_NAME_UNIT    BUS_NAME ".Unit"
      29             : #define BUS_PATH         "/org/freedesktop/systemd1"
      30             : 
      31             : /*!
      32             :  * \internal
      33             :  * \brief Prepare a systemd action
      34             :  *
      35             :  * \param[in,out] op  Action to prepare
      36             :  *
      37             :  * \return Standard Pacemaker return code
      38             :  */
      39             : int
      40           0 : services__systemd_prepare(svc_action_t *op)
      41             : {
      42           0 :     op->opaque->exec = strdup("systemd-dbus");
      43           0 :     if (op->opaque->exec == NULL) {
      44           0 :         return ENOMEM;
      45             :     }
      46           0 :     return pcmk_rc_ok;
      47             : }
      48             : 
      49             : /*!
      50             :  * \internal
      51             :  * \brief Map a systemd result to a standard OCF result
      52             :  *
      53             :  * \param[in] exit_status  Systemd result
      54             :  *
      55             :  * \return Standard OCF result
      56             :  */
      57             : enum ocf_exitcode
      58           0 : services__systemd2ocf(int exit_status)
      59             : {
      60             :     // This library uses OCF codes for systemd actions
      61           0 :     return (enum ocf_exitcode) exit_status;
      62             : }
      63             : 
      64             : static inline DBusMessage *
      65           0 : systemd_new_method(const char *method)
      66             : {
      67           0 :     crm_trace("Calling: %s on " BUS_NAME_MANAGER, method);
      68           0 :     return dbus_message_new_method_call(BUS_NAME, BUS_PATH, BUS_NAME_MANAGER,
      69             :                                         method);
      70             : }
      71             : 
      72             : /*
      73             :  * Functions to manage a static DBus connection
      74             :  */
      75             : 
      76             : static DBusConnection* systemd_proxy = NULL;
      77             : 
      78             : static inline DBusPendingCall *
      79           0 : systemd_send(DBusMessage *msg,
      80             :              void(*done)(DBusPendingCall *pending, void *user_data),
      81             :              void *user_data, int timeout)
      82             : {
      83           0 :     return pcmk_dbus_send(msg, systemd_proxy, done, user_data, timeout);
      84             : }
      85             : 
      86             : static inline DBusMessage *
      87           0 : systemd_send_recv(DBusMessage *msg, DBusError *error, int timeout)
      88             : {
      89           0 :     return pcmk_dbus_send_recv(msg, systemd_proxy, error, timeout);
      90             : }
      91             : 
      92             : /*!
      93             :  * \internal
      94             :  * \brief Send a method to systemd without arguments, and wait for reply
      95             :  *
      96             :  * \param[in] method  Method to send
      97             :  *
      98             :  * \return Systemd reply on success, NULL (and error will be logged) otherwise
      99             :  *
     100             :  * \note The caller must call dbus_message_unref() on the reply after
     101             :  *       handling it.
     102             :  */
     103             : static DBusMessage *
     104           0 : systemd_call_simple_method(const char *method)
     105             : {
     106           0 :     DBusMessage *msg = systemd_new_method(method);
     107           0 :     DBusMessage *reply = NULL;
     108             :     DBusError error;
     109             : 
     110             :     /* Don't call systemd_init() here, because that calls this */
     111           0 :     CRM_CHECK(systemd_proxy, return NULL);
     112             : 
     113           0 :     if (msg == NULL) {
     114           0 :         crm_err("Could not create message to send %s to systemd", method);
     115           0 :         return NULL;
     116             :     }
     117             : 
     118           0 :     dbus_error_init(&error);
     119           0 :     reply = systemd_send_recv(msg, &error, DBUS_TIMEOUT_USE_DEFAULT);
     120           0 :     dbus_message_unref(msg);
     121             : 
     122           0 :     if (dbus_error_is_set(&error)) {
     123           0 :         crm_err("Could not send %s to systemd: %s (%s)",
     124             :                 method, error.message, error.name);
     125           0 :         dbus_error_free(&error);
     126           0 :         return NULL;
     127             : 
     128           0 :     } else if (reply == NULL) {
     129           0 :         crm_err("Could not send %s to systemd: no reply received", method);
     130           0 :         return NULL;
     131             :     }
     132             : 
     133           0 :     return reply;
     134             : }
     135             : 
     136             : static gboolean
     137           0 : systemd_init(void)
     138             : {
     139             :     static int need_init = 1;
     140             :     // https://dbus.freedesktop.org/doc/api/html/group__DBusConnection.html
     141             : 
     142           0 :     if (systemd_proxy
     143           0 :         && dbus_connection_get_is_connected(systemd_proxy) == FALSE) {
     144           0 :         crm_warn("Connection to System DBus is closed. Reconnecting...");
     145           0 :         pcmk_dbus_disconnect(systemd_proxy);
     146           0 :         systemd_proxy = NULL;
     147           0 :         need_init = 1;
     148             :     }
     149             : 
     150           0 :     if (need_init) {
     151           0 :         need_init = 0;
     152           0 :         systemd_proxy = pcmk_dbus_connect();
     153             :     }
     154           0 :     if (systemd_proxy == NULL) {
     155           0 :         return FALSE;
     156             :     }
     157           0 :     return TRUE;
     158             : }
     159             : 
     160             : static inline char *
     161           0 : systemd_get_property(const char *unit, const char *name,
     162             :                      void (*callback)(const char *name, const char *value, void *userdata),
     163             :                      void *userdata, DBusPendingCall **pending, int timeout)
     164             : {
     165           0 :     return systemd_proxy?
     166           0 :            pcmk_dbus_get_property(systemd_proxy, BUS_NAME, unit, BUS_NAME_UNIT,
     167             :                                   name, callback, userdata, pending, timeout)
     168           0 :            : NULL;
     169             : }
     170             : 
     171             : void
     172           0 : systemd_cleanup(void)
     173             : {
     174           0 :     if (systemd_proxy) {
     175           0 :         pcmk_dbus_disconnect(systemd_proxy);
     176           0 :         systemd_proxy = NULL;
     177             :     }
     178           0 : }
     179             : 
     180             : /*
     181             :  * end of systemd_proxy functions
     182             :  */
     183             : 
     184             : /*!
     185             :  * \internal
     186             :  * \brief Check whether a file name represents a manageable systemd unit
     187             :  *
     188             :  * \param[in] name  File name to check
     189             :  *
     190             :  * \return Pointer to "dot" before filename extension if so, NULL otherwise
     191             :  */
     192             : static const char *
     193           0 : systemd_unit_extension(const char *name)
     194             : {
     195           0 :     if (name) {
     196           0 :         const char *dot = strrchr(name, '.');
     197             : 
     198           0 :         if (dot && (!strcmp(dot, ".service")
     199           0 :                     || !strcmp(dot, ".socket")
     200           0 :                     || !strcmp(dot, ".mount")
     201           0 :                     || !strcmp(dot, ".timer")
     202           0 :                     || !strcmp(dot, ".path"))) {
     203           0 :             return dot;
     204             :         }
     205             :     }
     206           0 :     return NULL;
     207             : }
     208             : 
     209             : static char *
     210           0 : systemd_service_name(const char *name, bool add_instance_name)
     211             : {
     212           0 :     const char *dot = NULL;
     213             : 
     214           0 :     if (pcmk__str_empty(name)) {
     215           0 :         return NULL;
     216             :     }
     217             : 
     218             :     /* Services that end with an @ sign are systemd templates.  They expect an
     219             :      * instance name to follow the service name.  If no instance name was
     220             :      * provided, just add "pacemaker" to the string as the instance name.  It
     221             :      * doesn't seem to matter for purposes of looking up whether a service
     222             :      * exists or not.
     223             :      *
     224             :      * A template can be specified either with or without the unit extension,
     225             :      * so this block handles both cases.
     226             :      */
     227           0 :     dot = systemd_unit_extension(name);
     228             : 
     229           0 :     if (dot) {
     230           0 :         if (dot != name && *(dot-1) == '@') {
     231           0 :             char *s = NULL;
     232             : 
     233           0 :             if (asprintf(&s, "%.*spacemaker%s", (int) (dot-name), name, dot) == -1) {
     234             :                 /* If asprintf fails, just return name. */
     235           0 :                 return strdup(name);
     236             :             }
     237             : 
     238           0 :             return s;
     239             :         } else {
     240           0 :             return strdup(name);
     241             :         }
     242             : 
     243           0 :     } else if (add_instance_name && *(name+strlen(name)-1) == '@') {
     244           0 :         return crm_strdup_printf("%spacemaker.service", name);
     245             : 
     246             :     } else {
     247           0 :         return crm_strdup_printf("%s.service", name);
     248             :     }
     249             : }
     250             : 
     251             : static void
     252           0 : systemd_daemon_reload_complete(DBusPendingCall *pending, void *user_data)
     253             : {
     254             :     DBusError error;
     255           0 :     DBusMessage *reply = NULL;
     256           0 :     unsigned int reload_count = GPOINTER_TO_UINT(user_data);
     257             : 
     258           0 :     dbus_error_init(&error);
     259           0 :     if(pending) {
     260           0 :         reply = dbus_pending_call_steal_reply(pending);
     261             :     }
     262             : 
     263           0 :     if (pcmk_dbus_find_error(pending, reply, &error)) {
     264           0 :         crm_warn("Could not issue systemd reload %d: %s",
     265             :                  reload_count, error.message);
     266           0 :         dbus_error_free(&error);
     267             : 
     268             :     } else {
     269           0 :         crm_trace("Reload %d complete", reload_count);
     270             :     }
     271             : 
     272           0 :     if(pending) {
     273           0 :         dbus_pending_call_unref(pending);
     274             :     }
     275           0 :     if(reply) {
     276           0 :         dbus_message_unref(reply);
     277             :     }
     278           0 : }
     279             : 
     280             : static bool
     281           0 : systemd_daemon_reload(int timeout)
     282             : {
     283             :     static unsigned int reload_count = 0;
     284           0 :     DBusMessage *msg = systemd_new_method("Reload");
     285             : 
     286           0 :     reload_count++;
     287           0 :     CRM_ASSERT(msg != NULL);
     288           0 :     systemd_send(msg, systemd_daemon_reload_complete,
     289           0 :                  GUINT_TO_POINTER(reload_count), timeout);
     290           0 :     dbus_message_unref(msg);
     291             : 
     292           0 :     return TRUE;
     293             : }
     294             : 
     295             : /*!
     296             :  * \internal
     297             :  * \brief Set an action result based on a method error
     298             :  *
     299             :  * \param[in,out] op     Action to set result for
     300             :  * \param[in]     error  Method error
     301             :  */
     302             : static void
     303           0 : set_result_from_method_error(svc_action_t *op, const DBusError *error)
     304             : {
     305           0 :     services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
     306             :                          "Unable to invoke systemd DBus method");
     307             : 
     308           0 :     if (strstr(error->name, "org.freedesktop.systemd1.InvalidName")
     309           0 :         || strstr(error->name, "org.freedesktop.systemd1.LoadFailed")
     310           0 :         || strstr(error->name, "org.freedesktop.systemd1.NoSuchUnit")) {
     311             : 
     312           0 :         if (pcmk__str_eq(op->action, PCMK_ACTION_STOP, pcmk__str_casei)) {
     313           0 :             crm_trace("Masking systemd stop failure (%s) for %s "
     314             :                       "because unknown service can be considered stopped",
     315             :                       error->name, pcmk__s(op->rsc, "unknown resource"));
     316           0 :             services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
     317           0 :             return;
     318             :         }
     319             : 
     320           0 :         services__format_result(op, PCMK_OCF_NOT_INSTALLED,
     321             :                                PCMK_EXEC_NOT_INSTALLED,
     322             :                                "systemd unit %s not found", op->agent);
     323             :     }
     324             : 
     325           0 :     crm_info("DBus request for %s of systemd unit %s%s%s failed: %s",
     326             :              op->action, op->agent,
     327             :              ((op->rsc == NULL)? "" : " for resource "), pcmk__s(op->rsc, ""),
     328             :              error->message);
     329             : }
     330             : 
     331             : /*!
     332             :  * \internal
     333             :  * \brief Extract unit path from LoadUnit reply, and execute action
     334             :  *
     335             :  * \param[in]     reply  LoadUnit reply
     336             :  * \param[in,out] op     Action to execute (or NULL to just return path)
     337             :  *
     338             :  * \return DBus object path for specified unit if successful (only valid for
     339             :  *         lifetime of \p reply), otherwise NULL
     340             :  */
     341             : static const char *
     342           0 : execute_after_loadunit(DBusMessage *reply, svc_action_t *op)
     343             : {
     344           0 :     const char *path = NULL;
     345             :     DBusError error;
     346             : 
     347             :     /* path here is not used other than as a non-NULL flag to indicate that a
     348             :      * request was indeed sent
     349             :      */
     350           0 :     if (pcmk_dbus_find_error((void *) &path, reply, &error)) {
     351           0 :         if (op != NULL) {
     352           0 :             set_result_from_method_error(op, &error);
     353             :         }
     354           0 :         dbus_error_free(&error);
     355             : 
     356           0 :     } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
     357             :                                      __func__, __LINE__)) {
     358           0 :         if (op != NULL) {
     359           0 :             services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
     360             :                                  "systemd DBus method had unexpected reply");
     361           0 :             crm_info("Could not load systemd unit %s for %s: "
     362             :                      "DBus reply has unexpected type", op->agent, op->id);
     363             :         } else {
     364           0 :             crm_info("Could not load systemd unit: "
     365             :                      "DBus reply has unexpected type");
     366             :         }
     367             : 
     368             :     } else {
     369           0 :         dbus_message_get_args (reply, NULL,
     370             :                                DBUS_TYPE_OBJECT_PATH, &path,
     371             :                                DBUS_TYPE_INVALID);
     372             :     }
     373             : 
     374           0 :     if (op != NULL) {
     375           0 :         if (path != NULL) {
     376           0 :             invoke_unit_by_path(op, path);
     377             : 
     378           0 :         } else if (!(op->synchronous)) {
     379           0 :             services__format_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
     380             :                                     "No DBus object found for systemd unit %s",
     381             :                                     op->agent);
     382           0 :             services__finalize_async_op(op);
     383             :         }
     384             :     }
     385             : 
     386           0 :     return path;
     387             : }
     388             : 
     389             : /*!
     390             :  * \internal
     391             :  * \brief Execute a systemd action after its LoadUnit completes
     392             :  *
     393             :  * \param[in,out] pending    If not NULL, DBus call associated with LoadUnit
     394             :  * \param[in,out] user_data  Action to execute
     395             :  */
     396             : static void
     397           0 : loadunit_completed(DBusPendingCall *pending, void *user_data)
     398             : {
     399           0 :     DBusMessage *reply = NULL;
     400           0 :     svc_action_t *op = user_data;
     401             : 
     402           0 :     crm_trace("LoadUnit result for %s arrived", op->id);
     403             : 
     404             :     // Grab the reply
     405           0 :     if (pending != NULL) {
     406           0 :         reply = dbus_pending_call_steal_reply(pending);
     407             :     }
     408             : 
     409             :     // The call is no longer pending
     410           0 :     CRM_LOG_ASSERT(pending == op->opaque->pending);
     411           0 :     services_set_op_pending(op, NULL);
     412             : 
     413             :     // Execute the desired action based on the reply
     414           0 :     execute_after_loadunit(reply, user_data);
     415           0 :     if (reply != NULL) {
     416           0 :         dbus_message_unref(reply);
     417             :     }
     418           0 : }
     419             : 
     420             : /*!
     421             :  * \internal
     422             :  * \brief Execute a systemd action, given the unit name
     423             :  *
     424             :  * \param[in]     arg_name  Unit name (possibly without ".service" extension)
     425             :  * \param[in,out] op        Action to execute (if NULL, just get object path)
     426             :  * \param[out]    path      If non-NULL and \p op is NULL or synchronous, where
     427             :  *                          to store DBus object path for specified unit
     428             :  *
     429             :  * \return Standard Pacemaker return code (for NULL \p op, pcmk_rc_ok means unit
     430             :  *         was found; for synchronous actions, pcmk_rc_ok means unit was
     431             :  *         executed, with the actual result stored in \p op; for asynchronous
     432             :  *         actions, pcmk_rc_ok means action was initiated)
     433             :  * \note It is the caller's responsibility to free the path.
     434             :  */
     435             : static int
     436           0 : invoke_unit_by_name(const char *arg_name, svc_action_t *op, char **path)
     437             : {
     438             :     DBusMessage *msg;
     439           0 :     DBusMessage *reply = NULL;
     440           0 :     DBusPendingCall *pending = NULL;
     441           0 :     char *name = NULL;
     442             : 
     443           0 :     if (!systemd_init()) {
     444           0 :         if (op != NULL) {
     445           0 :             services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
     446             :                                  "No DBus connection");
     447             :         }
     448           0 :         return ENOTCONN;
     449             :     }
     450             : 
     451             :     /* Create a LoadUnit DBus method (equivalent to GetUnit if already loaded),
     452             :      * which makes the unit usable via further DBus methods.
     453             :      *
     454             :      * <method name="LoadUnit">
     455             :      *  <arg name="name" type="s" direction="in"/>
     456             :      *  <arg name="unit" type="o" direction="out"/>
     457             :      * </method>
     458             :      */
     459           0 :     msg = systemd_new_method("LoadUnit");
     460           0 :     CRM_ASSERT(msg != NULL);
     461             : 
     462             :     // Add the (expanded) unit name as the argument
     463           0 :     name = systemd_service_name(arg_name,
     464             :                                 (op == NULL)
     465           0 :                                 || pcmk__str_eq(op->action,
     466             :                                                 PCMK_ACTION_META_DATA,
     467             :                                                 pcmk__str_none));
     468           0 :     CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
     469             :                                             DBUS_TYPE_INVALID));
     470           0 :     free(name);
     471             : 
     472           0 :     if ((op == NULL) || op->synchronous) {
     473             :         // For synchronous ops, wait for a reply and extract the result
     474           0 :         const char *unit = NULL;
     475           0 :         int rc = pcmk_rc_ok;
     476             : 
     477           0 :         reply = systemd_send_recv(msg, NULL,
     478             :                                   (op? op->timeout : DBUS_TIMEOUT_USE_DEFAULT));
     479           0 :         dbus_message_unref(msg);
     480             : 
     481           0 :         unit = execute_after_loadunit(reply, op);
     482           0 :         if (unit == NULL) {
     483           0 :             rc = ENOENT;
     484           0 :             if (path != NULL) {
     485           0 :                 *path = NULL;
     486             :             }
     487           0 :         } else if (path != NULL) {
     488           0 :             *path = strdup(unit);
     489           0 :             if (*path == NULL) {
     490           0 :                 rc = ENOMEM;
     491             :             }
     492             :         }
     493             : 
     494           0 :         if (reply != NULL) {
     495           0 :             dbus_message_unref(reply);
     496             :         }
     497           0 :         return rc;
     498             :     }
     499             : 
     500             :     // For asynchronous ops, initiate the LoadUnit call and return
     501           0 :     pending = systemd_send(msg, loadunit_completed, op, op->timeout);
     502           0 :     if (pending == NULL) {
     503           0 :         services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
     504             :                              "Unable to send DBus message");
     505           0 :         dbus_message_unref(msg);
     506           0 :         return ECOMM;
     507             :     }
     508             : 
     509             :     // LoadUnit was successfully initiated
     510           0 :     services__set_result(op, PCMK_OCF_UNKNOWN, PCMK_EXEC_PENDING, NULL);
     511           0 :     services_set_op_pending(op, pending);
     512           0 :     dbus_message_unref(msg);
     513           0 :     return pcmk_rc_ok;
     514             : }
     515             : 
     516             : /*!
     517             :  * \internal
     518             :  * \brief Compare two strings alphabetically (case-insensitive)
     519             :  *
     520             :  * \param[in] a  First string to compare
     521             :  * \param[in] b  Second string to compare
     522             :  *
     523             :  * \return 0 if strings are equal, -1 if a < b, 1 if a > b
     524             :  *
     525             :  * \note Usable as a GCompareFunc with g_list_sort().
     526             :  *       NULL is considered less than non-NULL.
     527             :  */
     528             : static gint
     529           0 : sort_str(gconstpointer a, gconstpointer b)
     530             : {
     531           0 :     if (!a && !b) {
     532           0 :         return 0;
     533           0 :     } else if (!a) {
     534           0 :         return -1;
     535           0 :     } else if (!b) {
     536           0 :         return 1;
     537             :     }
     538           0 :     return strcasecmp(a, b);
     539             : }
     540             : 
     541             : GList *
     542           0 : systemd_unit_listall(void)
     543             : {
     544           0 :     int nfiles = 0;
     545           0 :     GList *units = NULL;
     546             :     DBusMessageIter args;
     547             :     DBusMessageIter unit;
     548             :     DBusMessageIter elem;
     549           0 :     DBusMessage *reply = NULL;
     550             : 
     551           0 :     if (systemd_init() == FALSE) {
     552           0 :         return NULL;
     553             :     }
     554             : 
     555             : /*
     556             :         "  <method name=\"ListUnitFiles\">\n"                               \
     557             :         "   <arg name=\"files\" type=\"a(ss)\" direction=\"out\"/>\n" \
     558             :         "  </method>\n"                                                 \
     559             : */
     560             : 
     561           0 :     reply = systemd_call_simple_method("ListUnitFiles");
     562           0 :     if (reply == NULL) {
     563           0 :         return NULL;
     564             :     }
     565           0 :     if (!dbus_message_iter_init(reply, &args)) {
     566           0 :         crm_err("Could not list systemd unit files: systemd reply has no arguments");
     567           0 :         dbus_message_unref(reply);
     568           0 :         return NULL;
     569             :     }
     570           0 :     if (!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY,
     571             :                               __func__, __LINE__)) {
     572           0 :         crm_err("Could not list systemd unit files: systemd reply has invalid arguments");
     573           0 :         dbus_message_unref(reply);
     574           0 :         return NULL;
     575             :     }
     576             : 
     577           0 :     dbus_message_iter_recurse(&args, &unit);
     578           0 :     for (; dbus_message_iter_get_arg_type(&unit) != DBUS_TYPE_INVALID;
     579           0 :         dbus_message_iter_next(&unit)) {
     580             : 
     581             :         DBusBasicValue value;
     582           0 :         const char *match = NULL;
     583           0 :         char *unit_name = NULL;
     584           0 :         char *basename = NULL;
     585             : 
     586           0 :         if(!pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_STRUCT, __func__, __LINE__)) {
     587           0 :             crm_warn("Skipping systemd reply argument with unexpected type");
     588           0 :             continue;
     589             :         }
     590             : 
     591           0 :         dbus_message_iter_recurse(&unit, &elem);
     592           0 :         if(!pcmk_dbus_type_check(reply, &elem, DBUS_TYPE_STRING, __func__, __LINE__)) {
     593           0 :             crm_warn("Skipping systemd reply argument with no string");
     594           0 :             continue;
     595             :         }
     596             : 
     597           0 :         dbus_message_iter_get_basic(&elem, &value);
     598           0 :         if (value.str == NULL) {
     599           0 :             crm_debug("ListUnitFiles reply did not provide a string");
     600           0 :             continue;
     601             :         }
     602           0 :         crm_trace("DBus ListUnitFiles listed: %s", value.str);
     603             : 
     604           0 :         match = systemd_unit_extension(value.str);
     605           0 :         if (match == NULL) {
     606             :             // This is not a unit file type we know how to manage
     607           0 :             crm_debug("ListUnitFiles entry '%s' is not supported as resource",
     608             :                       value.str);
     609           0 :             continue;
     610             :         }
     611             : 
     612             :         // ListUnitFiles returns full path names, we just want base name
     613           0 :         basename = strrchr(value.str, '/');
     614           0 :         if (basename) {
     615           0 :             basename = basename + 1;
     616             :         } else {
     617           0 :             basename = value.str;
     618             :         }
     619             : 
     620           0 :         if (!strcmp(match, ".service")) {
     621             :             // Service is the "default" unit type, so strip it
     622           0 :             unit_name = strndup(basename, match - basename);
     623             :         } else {
     624           0 :             unit_name = strdup(basename);
     625             :         }
     626             : 
     627           0 :         nfiles++;
     628           0 :         units = g_list_prepend(units, unit_name);
     629             :     }
     630             : 
     631           0 :     dbus_message_unref(reply);
     632             : 
     633           0 :     crm_trace("Found %d manageable systemd unit files", nfiles);
     634           0 :     units = g_list_sort(units, sort_str);
     635           0 :     return units;
     636             : }
     637             : 
     638             : gboolean
     639           0 : systemd_unit_exists(const char *name)
     640             : {
     641           0 :     char *path = NULL;
     642           0 :     char *state = NULL;
     643             : 
     644             :     /* Note: Makes a blocking dbus calls
     645             :      * Used by resources_find_service_class() when resource class=service
     646             :      */
     647           0 :     if ((invoke_unit_by_name(name, NULL, &path) != pcmk_rc_ok)
     648           0 :         || (path == NULL)) {
     649           0 :         return FALSE;
     650             :     }
     651             : 
     652             :     /* A successful LoadUnit is not sufficient to determine the unit's
     653             :      * existence; it merely means the LoadUnit request received a reply.
     654             :      * We must make another blocking call to check the LoadState property.
     655             :      */
     656           0 :     state = systemd_get_property(path, "LoadState", NULL, NULL, NULL,
     657             :                                  DBUS_TIMEOUT_USE_DEFAULT);
     658           0 :     free(path);
     659           0 :     if (pcmk__str_any_of(state, "loaded", "masked", NULL)) {
     660           0 :         free(state);
     661           0 :         return TRUE;
     662             :     }
     663           0 :     free(state);
     664           0 :     return FALSE;
     665             : }
     666             : 
     667             : // @TODO Use XML string constants and maybe a real XML object
     668             : #define METADATA_FORMAT                                                        \
     669             :     "<?xml " PCMK_XA_VERSION "=\"1.0\"?>\n"                                    \
     670             :     "<" PCMK_XE_RESOURCE_AGENT " "                                             \
     671             :         PCMK_XA_NAME "=\"%s\" "                                                \
     672             :         PCMK_XA_VERSION "=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n"               \
     673             :     "  <" PCMK_XE_VERSION ">1.1</" PCMK_XE_VERSION ">\n"                       \
     674             :     "  <" PCMK_XE_LONGDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">\n"       \
     675             :     "    %s\n"                                                                 \
     676             :     "  </" PCMK_XE_LONGDESC ">\n"                                              \
     677             :     "  <" PCMK_XE_SHORTDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">"        \
     678             :         "systemd unit file for %s"                                             \
     679             :       "</" PCMK_XE_SHORTDESC ">\n"                                             \
     680             :     "  <" PCMK_XE_PARAMETERS "/>\n"                                            \
     681             :     "  <" PCMK_XE_ACTIONS ">\n"                                                \
     682             :     "    <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_START "\""       \
     683             :                            " " PCMK_META_TIMEOUT "=\"100s\" />\n"              \
     684             :     "    <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STOP "\""        \
     685             :                            " " PCMK_META_TIMEOUT "=\"100s\" />\n"              \
     686             :     "    <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STATUS "\""      \
     687             :                            " " PCMK_META_TIMEOUT "=\"100s\" />\n"              \
     688             :     "    <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_MONITOR "\""     \
     689             :                            " " PCMK_META_TIMEOUT "=\"100s\""                   \
     690             :                            " " PCMK_META_INTERVAL "=\"60s\" />\n"              \
     691             :     "    <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_META_DATA "\""   \
     692             :                            " " PCMK_META_TIMEOUT "=\"5s\" />\n"                \
     693             :     "  </" PCMK_XE_ACTIONS ">\n"                                               \
     694             :     "  <" PCMK_XE_SPECIAL " " PCMK_XA_TAG "=\"systemd\"/>\n"                   \
     695             :     "</" PCMK_XE_RESOURCE_AGENT ">\n"
     696             : 
     697             : static char *
     698           0 : systemd_unit_metadata(const char *name, int timeout)
     699             : {
     700           0 :     char *meta = NULL;
     701           0 :     char *desc = NULL;
     702           0 :     char *path = NULL;
     703             : 
     704           0 :     if (invoke_unit_by_name(name, NULL, &path) == pcmk_rc_ok) {
     705             :         /* TODO: Worth a making blocking call for? Probably not. Possibly if cached. */
     706           0 :         desc = systemd_get_property(path, "Description", NULL, NULL, NULL,
     707             :                                     timeout);
     708             :     } else {
     709           0 :         desc = crm_strdup_printf("Systemd unit file for %s", name);
     710             :     }
     711             : 
     712           0 :     if (pcmk__xml_needs_escape(desc, pcmk__xml_escape_text)) {
     713           0 :         gchar *escaped = pcmk__xml_escape(desc, pcmk__xml_escape_text);
     714             : 
     715           0 :         meta = crm_strdup_printf(METADATA_FORMAT, name, escaped, name);
     716           0 :         g_free(escaped);
     717             : 
     718             :     } else {
     719           0 :         meta = crm_strdup_printf(METADATA_FORMAT, name, desc, name);
     720             :     }
     721             : 
     722           0 :     free(desc);
     723           0 :     free(path);
     724           0 :     return meta;
     725             : }
     726             : 
     727             : /*!
     728             :  * \internal
     729             :  * \brief Determine result of method from reply
     730             :  *
     731             :  * \param[in]     reply  Reply to start, stop, or restart request
     732             :  * \param[in,out] op     Action that was executed
     733             :  */
     734             : static void
     735           0 : process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
     736             : {
     737             :     DBusError error;
     738             : 
     739           0 :     dbus_error_init(&error);
     740             : 
     741             :     /* The first use of error here is not used other than as a non-NULL flag to
     742             :      * indicate that a request was indeed sent
     743             :      */
     744           0 :     if (pcmk_dbus_find_error((void *) &error, reply, &error)) {
     745           0 :         set_result_from_method_error(op, &error);
     746           0 :         dbus_error_free(&error);
     747             : 
     748           0 :     } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
     749             :                                      __func__, __LINE__)) {
     750           0 :         crm_info("DBus request for %s of %s succeeded but "
     751             :                  "return type was unexpected",
     752             :                  op->action, pcmk__s(op->rsc, "unknown resource"));
     753           0 :         services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE,
     754             :                              "systemd DBus method had unexpected reply");
     755             : 
     756             :     } else {
     757           0 :         const char *path = NULL;
     758             : 
     759           0 :         dbus_message_get_args(reply, NULL,
     760             :                               DBUS_TYPE_OBJECT_PATH, &path,
     761             :                               DBUS_TYPE_INVALID);
     762           0 :         crm_debug("DBus request for %s of %s using %s succeeded",
     763             :                   op->action, pcmk__s(op->rsc, "unknown resource"), path);
     764           0 :         services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
     765             :     }
     766           0 : }
     767             : 
     768             : /*!
     769             :  * \internal
     770             :  * \brief Process the completion of an asynchronous unit start, stop, or restart
     771             :  *
     772             :  * \param[in,out] pending    If not NULL, DBus call associated with request
     773             :  * \param[in,out] user_data  Action that was executed
     774             :  */
     775             : static void
     776           0 : unit_method_complete(DBusPendingCall *pending, void *user_data)
     777             : {
     778           0 :     DBusMessage *reply = NULL;
     779           0 :     svc_action_t *op = user_data;
     780             : 
     781           0 :     crm_trace("Result for %s arrived", op->id);
     782             : 
     783             :     // Grab the reply
     784           0 :     if (pending != NULL) {
     785           0 :         reply = dbus_pending_call_steal_reply(pending);
     786             :     }
     787             : 
     788             :     // The call is no longer pending
     789           0 :     CRM_LOG_ASSERT(pending == op->opaque->pending);
     790           0 :     services_set_op_pending(op, NULL);
     791             : 
     792             :     // Determine result and finalize action
     793           0 :     process_unit_method_reply(reply, op);
     794           0 :     services__finalize_async_op(op);
     795           0 :     if (reply != NULL) {
     796           0 :         dbus_message_unref(reply);
     797             :     }
     798           0 : }
     799             : 
     800             : #define SYSTEMD_OVERRIDE_ROOT "/run/systemd/system/"
     801             : 
     802             : /* When the cluster manages a systemd resource, we create a unit file override
     803             :  * to order the service "before" pacemaker. The "before" relationship won't
     804             :  * actually be used, since systemd won't ever start the resource -- we're
     805             :  * interested in the reverse shutdown ordering it creates, to ensure that
     806             :  * systemd doesn't stop the resource at shutdown while pacemaker is still
     807             :  * running.
     808             :  *
     809             :  * @TODO Add start timeout
     810             :  */
     811             : #define SYSTEMD_OVERRIDE_TEMPLATE                           \
     812             :     "[Unit]\n"                                              \
     813             :     "Description=Cluster Controlled %s\n"                   \
     814             :     "Before=pacemaker.service pacemaker_remote.service\n"   \
     815             :     "\n"                                                    \
     816             :     "[Service]\n"                                           \
     817             :     "Restart=no\n"
     818             : 
     819             : // Temporarily use rwxr-xr-x umask when opening a file for writing
     820             : static FILE *
     821           0 : create_world_readable(const char *filename)
     822             : {
     823           0 :     mode_t orig_umask = umask(S_IWGRP | S_IWOTH);
     824           0 :     FILE *fp = fopen(filename, "w");
     825             : 
     826           0 :     umask(orig_umask);
     827           0 :     return fp;
     828             : }
     829             : 
     830             : static void
     831           0 : create_override_dir(const char *agent)
     832             : {
     833           0 :     char *override_dir = crm_strdup_printf(SYSTEMD_OVERRIDE_ROOT
     834             :                                            "/%s.service.d", agent);
     835           0 :     int rc = pcmk__build_path(override_dir, 0755);
     836             : 
     837           0 :     if (rc != pcmk_rc_ok) {
     838           0 :         crm_warn("Could not create systemd override directory %s: %s",
     839             :                  override_dir, pcmk_rc_str(rc));
     840             :     }
     841           0 :     free(override_dir);
     842           0 : }
     843             : 
     844             : static char *
     845           0 : get_override_filename(const char *agent)
     846             : {
     847           0 :     return crm_strdup_printf(SYSTEMD_OVERRIDE_ROOT
     848             :                              "/%s.service.d/50-pacemaker.conf", agent);
     849             : }
     850             : 
     851             : static void
     852           0 : systemd_create_override(const char *agent, int timeout)
     853             : {
     854           0 :     FILE *file_strm = NULL;
     855           0 :     char *override_file = get_override_filename(agent);
     856             : 
     857           0 :     create_override_dir(agent);
     858             : 
     859             :     /* Ensure the override file is world-readable. This is not strictly
     860             :      * necessary, but it avoids a systemd warning in the logs.
     861             :      */
     862           0 :     file_strm = create_world_readable(override_file);
     863           0 :     if (file_strm == NULL) {
     864           0 :         crm_err("Cannot open systemd override file %s for writing",
     865             :                 override_file);
     866             :     } else {
     867           0 :         char *override = crm_strdup_printf(SYSTEMD_OVERRIDE_TEMPLATE, agent);
     868             : 
     869           0 :         int rc = fprintf(file_strm, "%s\n", override);
     870             : 
     871           0 :         free(override);
     872           0 :         if (rc < 0) {
     873           0 :             crm_perror(LOG_WARNING, "Cannot write to systemd override file %s",
     874             :                        override_file);
     875             :         }
     876           0 :         fflush(file_strm);
     877           0 :         fclose(file_strm);
     878           0 :         systemd_daemon_reload(timeout);
     879             :     }
     880             : 
     881           0 :     free(override_file);
     882           0 : }
     883             : 
     884             : static void
     885           0 : systemd_remove_override(const char *agent, int timeout)
     886             : {
     887           0 :     char *override_file = get_override_filename(agent);
     888           0 :     int rc = unlink(override_file);
     889             : 
     890           0 :     if (rc < 0) {
     891             :         // Stop may be called when already stopped, which is fine
     892           0 :         crm_perror(LOG_DEBUG, "Cannot remove systemd override file %s",
     893             :                    override_file);
     894             :     } else {
     895           0 :         systemd_daemon_reload(timeout);
     896             :     }
     897           0 :     free(override_file);
     898           0 : }
     899             : 
     900             : /*!
     901             :  * \internal
     902             :  * \brief Parse result of systemd status check
     903             :  *
     904             :  * Set a status action's exit status and execution status based on a DBus
     905             :  * property check result, and finalize the action if asynchronous.
     906             :  *
     907             :  * \param[in]     name      DBus interface name for property that was checked
     908             :  * \param[in]     state     Property value
     909             :  * \param[in,out] userdata  Status action that check was done for
     910             :  */
     911             : static void
     912           0 : parse_status_result(const char *name, const char *state, void *userdata)
     913             : {
     914           0 :     svc_action_t *op = userdata;
     915             : 
     916           0 :     crm_trace("Resource %s has %s='%s'",
     917             :               pcmk__s(op->rsc, "(unspecified)"), name,
     918             :               pcmk__s(state, "<null>"));
     919             : 
     920           0 :     if (pcmk__str_eq(state, "active", pcmk__str_none)) {
     921           0 :         services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
     922             : 
     923           0 :     } else if (pcmk__str_eq(state, "reloading", pcmk__str_none)) {
     924           0 :         services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
     925             : 
     926           0 :     } else if (pcmk__str_eq(state, "activating", pcmk__str_none)) {
     927           0 :         services__set_result(op, PCMK_OCF_UNKNOWN, PCMK_EXEC_PENDING, NULL);
     928             : 
     929           0 :     } else if (pcmk__str_eq(state, "deactivating", pcmk__str_none)) {
     930           0 :         services__set_result(op, PCMK_OCF_UNKNOWN, PCMK_EXEC_PENDING, NULL);
     931             : 
     932             :     } else {
     933           0 :         services__set_result(op, PCMK_OCF_NOT_RUNNING, PCMK_EXEC_DONE, state);
     934             :     }
     935             : 
     936           0 :     if (!(op->synchronous)) {
     937           0 :         services_set_op_pending(op, NULL);
     938           0 :         services__finalize_async_op(op);
     939             :     }
     940           0 : }
     941             : 
     942             : /*!
     943             :  * \internal
     944             :  * \brief Invoke a systemd unit, given its DBus object path
     945             :  *
     946             :  * \param[in,out] op    Action to execute
     947             :  * \param[in]     unit  DBus object path of systemd unit to invoke
     948             :  */
     949             : static void
     950           0 : invoke_unit_by_path(svc_action_t *op, const char *unit)
     951             : {
     952           0 :     const char *method = NULL;
     953           0 :     DBusMessage *msg = NULL;
     954           0 :     DBusMessage *reply = NULL;
     955             : 
     956           0 :     if (pcmk__str_any_of(op->action, PCMK_ACTION_MONITOR, PCMK_ACTION_STATUS,
     957             :                          NULL)) {
     958           0 :         DBusPendingCall *pending = NULL;
     959             :         char *state;
     960             : 
     961           0 :         state = systemd_get_property(unit, "ActiveState",
     962           0 :                                      (op->synchronous? NULL : parse_status_result),
     963           0 :                                      op, (op->synchronous? NULL : &pending),
     964             :                                      op->timeout);
     965           0 :         if (op->synchronous) {
     966           0 :             parse_status_result("ActiveState", state, op);
     967           0 :             free(state);
     968             : 
     969           0 :         } else if (pending == NULL) { // Could not get ActiveState property
     970           0 :             services__format_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
     971             :                                     "Could not get state for unit %s from DBus",
     972             :                                     op->agent);
     973           0 :             services__finalize_async_op(op);
     974             : 
     975             :         } else {
     976           0 :             services_set_op_pending(op, pending);
     977             :         }
     978           0 :         return;
     979             : 
     980           0 :     } else if (pcmk__str_eq(op->action, PCMK_ACTION_START, pcmk__str_none)) {
     981           0 :         method = "StartUnit";
     982           0 :         systemd_create_override(op->agent, op->timeout);
     983             : 
     984           0 :     } else if (pcmk__str_eq(op->action, PCMK_ACTION_STOP, pcmk__str_none)) {
     985           0 :         method = "StopUnit";
     986           0 :         systemd_remove_override(op->agent, op->timeout);
     987             : 
     988           0 :     } else if (pcmk__str_eq(op->action, "restart", pcmk__str_none)) {
     989           0 :         method = "RestartUnit";
     990             : 
     991             :     } else {
     992           0 :         services__format_result(op, PCMK_OCF_UNIMPLEMENT_FEATURE,
     993             :                                 PCMK_EXEC_ERROR,
     994             :                                 "Action %s not implemented "
     995             :                                 "for systemd resources",
     996           0 :                                 pcmk__s(op->action, "(unspecified)"));
     997           0 :         if (!(op->synchronous)) {
     998           0 :             services__finalize_async_op(op);
     999             :         }
    1000           0 :         return;
    1001             :     }
    1002             : 
    1003           0 :     crm_trace("Calling %s for unit path %s%s%s",
    1004             :               method, unit,
    1005             :               ((op->rsc == NULL)? "" : " for resource "), pcmk__s(op->rsc, ""));
    1006             : 
    1007           0 :     msg = systemd_new_method(method);
    1008           0 :     CRM_ASSERT(msg != NULL);
    1009             : 
    1010             :     /* (ss) */
    1011             :     {
    1012           0 :         const char *replace_s = "replace";
    1013           0 :         char *name = systemd_service_name(op->agent,
    1014           0 :                                           pcmk__str_eq(op->action,
    1015             :                                                        PCMK_ACTION_META_DATA,
    1016             :                                                        pcmk__str_none));
    1017             : 
    1018           0 :         CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID));
    1019           0 :         CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &replace_s, DBUS_TYPE_INVALID));
    1020             : 
    1021           0 :         free(name);
    1022             :     }
    1023             : 
    1024           0 :     if (op->synchronous) {
    1025           0 :         reply = systemd_send_recv(msg, NULL, op->timeout);
    1026           0 :         dbus_message_unref(msg);
    1027           0 :         process_unit_method_reply(reply, op);
    1028           0 :         if (reply != NULL) {
    1029           0 :             dbus_message_unref(reply);
    1030             :         }
    1031             : 
    1032             :     } else {
    1033           0 :         DBusPendingCall *pending = systemd_send(msg, unit_method_complete, op,
    1034             :                                                 op->timeout);
    1035             : 
    1036           0 :         dbus_message_unref(msg);
    1037           0 :         if (pending == NULL) {
    1038           0 :             services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
    1039             :                                  "Unable to send DBus message");
    1040           0 :             services__finalize_async_op(op);
    1041             : 
    1042             :         } else {
    1043           0 :             services_set_op_pending(op, pending);
    1044             :         }
    1045             :     }
    1046             : }
    1047             : 
    1048             : static gboolean
    1049           0 : systemd_timeout_callback(gpointer p)
    1050             : {
    1051           0 :     svc_action_t * op = p;
    1052             : 
    1053           0 :     op->opaque->timerid = 0;
    1054           0 :     crm_info("%s action for systemd unit %s named '%s' timed out",
    1055             :              op->action, op->agent, op->rsc);
    1056           0 :     services__format_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_TIMEOUT,
    1057             :                             "%s action for systemd unit %s "
    1058             :                             "did not complete in time", op->action, op->agent);
    1059           0 :     services__finalize_async_op(op);
    1060           0 :     return FALSE;
    1061             : }
    1062             : 
    1063             : /*!
    1064             :  * \internal
    1065             :  * \brief Execute a systemd action
    1066             :  *
    1067             :  * \param[in,out] op  Action to execute
    1068             :  *
    1069             :  * \return Standard Pacemaker return code
    1070             :  * \retval EBUSY          Recurring operation could not be initiated
    1071             :  * \retval pcmk_rc_error  Synchronous action failed
    1072             :  * \retval pcmk_rc_ok     Synchronous action succeeded, or asynchronous action
    1073             :  *                        should not be freed (because it's pending or because
    1074             :  *                        it failed to execute and was already freed)
    1075             :  *
    1076             :  * \note If the return value for an asynchronous action is not pcmk_rc_ok, the
    1077             :  *       caller is responsible for freeing the action.
    1078             :  */
    1079             : int
    1080           0 : services__execute_systemd(svc_action_t *op)
    1081             : {
    1082           0 :     CRM_ASSERT(op != NULL);
    1083             : 
    1084           0 :     if ((op->action == NULL) || (op->agent == NULL)) {
    1085           0 :         services__set_result(op, PCMK_OCF_NOT_CONFIGURED, PCMK_EXEC_ERROR_FATAL,
    1086             :                              "Bug in action caller");
    1087           0 :         goto done;
    1088             :     }
    1089             : 
    1090           0 :     if (!systemd_init()) {
    1091           0 :         services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
    1092             :                              "No DBus connection");
    1093           0 :         goto done;
    1094             :     }
    1095             : 
    1096           0 :     crm_debug("Performing %ssynchronous %s op on systemd unit %s%s%s",
    1097             :               (op->synchronous? "" : "a"), op->action, op->agent,
    1098             :               ((op->rsc == NULL)? "" : " for resource "), pcmk__s(op->rsc, ""));
    1099             : 
    1100           0 :     if (pcmk__str_eq(op->action, PCMK_ACTION_META_DATA, pcmk__str_casei)) {
    1101           0 :         op->stdout_data = systemd_unit_metadata(op->agent, op->timeout);
    1102           0 :         services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
    1103           0 :         goto done;
    1104             :     }
    1105             : 
    1106             :     /* invoke_unit_by_name() should always override these values, which are here
    1107             :      * just as a fail-safe in case there are any code paths that neglect to
    1108             :      */
    1109           0 :     services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
    1110             :                          "Bug in service library");
    1111             : 
    1112           0 :     if (invoke_unit_by_name(op->agent, op, NULL) == pcmk_rc_ok) {
    1113           0 :         op->opaque->timerid = g_timeout_add(op->timeout + 5000,
    1114             :                                             systemd_timeout_callback, op);
    1115           0 :         services_add_inflight_op(op);
    1116           0 :         return pcmk_rc_ok;
    1117             :     }
    1118             : 
    1119           0 : done:
    1120           0 :     if (op->synchronous) {
    1121           0 :         return (op->rc == PCMK_OCF_OK)? pcmk_rc_ok : pcmk_rc_error;
    1122             :     } else {
    1123           0 :         return services__finalize_async_op(op);
    1124             :     }
    1125             : }

Generated by: LCOV version 1.14