root/lib/services/systemd.c

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

DEFINITIONS

This source file includes following definitions.
  1. systemd_new_method
  2. systemd_send
  3. systemd_send_recv
  4. systemd_call_simple_method
  5. systemd_init
  6. systemd_get_property
  7. systemd_cleanup
  8. systemd_unit_extension
  9. systemd_service_name
  10. systemd_daemon_reload_complete
  11. systemd_daemon_reload
  12. systemd_mask_error
  13. systemd_loadunit_result
  14. systemd_loadunit_cb
  15. systemd_unit_by_name
  16. systemd_unit_listall
  17. systemd_unit_exists
  18. systemd_unit_metadata
  19. systemd_exec_result
  20. systemd_async_dispatch
  21. systemd_unit_check
  22. systemd_unit_exec_with_unit
  23. systemd_timeout_callback
  24. systemd_unit_exec

   1 /*
   2  * Copyright (C) 2012-2016 Andrew Beekhof <andrew@beekhof.net>
   3  *
   4  * This source code is licensed under the GNU Lesser General Public License
   5  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   6  */
   7 
   8 #include <crm_internal.h>
   9 #include <crm/crm.h>
  10 #include <crm/services.h>
  11 #include <crm/common/mainloop.h>
  12 
  13 #include <sys/stat.h>
  14 #include <gio/gio.h>
  15 #include <services_private.h>
  16 #include <systemd.h>
  17 #include <dbus/dbus.h>
  18 #include <pcmk-dbus.h>
  19 
  20 gboolean systemd_unit_exec_with_unit(svc_action_t * op, const char *unit);
  21 
  22 #define BUS_NAME         "org.freedesktop.systemd1"
  23 #define BUS_NAME_MANAGER BUS_NAME ".Manager"
  24 #define BUS_NAME_UNIT    BUS_NAME ".Unit"
  25 #define BUS_PATH         "/org/freedesktop/systemd1"
  26 
  27 static inline DBusMessage *
  28 systemd_new_method(const char *method)
     /* [previous][next][first][last][top][bottom][index][help] */
  29 {
  30     crm_trace("Calling: %s on " BUS_NAME_MANAGER, method);
  31     return dbus_message_new_method_call(BUS_NAME, BUS_PATH, BUS_NAME_MANAGER,
  32                                         method);
  33 }
  34 
  35 /*
  36  * Functions to manage a static DBus connection
  37  */
  38 
  39 static DBusConnection* systemd_proxy = NULL;
  40 
  41 static inline DBusPendingCall *
  42 systemd_send(DBusMessage *msg,
     /* [previous][next][first][last][top][bottom][index][help] */
  43              void(*done)(DBusPendingCall *pending, void *user_data),
  44              void *user_data, int timeout)
  45 {
  46     return pcmk_dbus_send(msg, systemd_proxy, done, user_data, timeout);
  47 }
  48 
  49 static inline DBusMessage *
  50 systemd_send_recv(DBusMessage *msg, DBusError *error, int timeout)
     /* [previous][next][first][last][top][bottom][index][help] */
  51 {
  52     return pcmk_dbus_send_recv(msg, systemd_proxy, error, timeout);
  53 }
  54 
  55 /*!
  56  * \internal
  57  * \brief Send a method to systemd without arguments, and wait for reply
  58  *
  59  * \param[in] method  Method to send
  60  *
  61  * \return Systemd reply on success, NULL (and error will be logged) otherwise
  62  *
  63  * \note The caller must call dbus_message_unref() on the reply after
  64  *       handling it.
  65  */
  66 static DBusMessage *
  67 systemd_call_simple_method(const char *method)
     /* [previous][next][first][last][top][bottom][index][help] */
  68 {
  69     DBusMessage *msg = systemd_new_method(method);
  70     DBusMessage *reply = NULL;
  71     DBusError error;
  72 
  73     /* Don't call systemd_init() here, because that calls this */
  74     CRM_CHECK(systemd_proxy, return NULL);
  75 
  76     if (msg == NULL) {
  77         crm_err("Could not create message to send %s to systemd", method);
  78         return NULL;
  79     }
  80 
  81     dbus_error_init(&error);
  82     reply = systemd_send_recv(msg, &error, DBUS_TIMEOUT_USE_DEFAULT);
  83     dbus_message_unref(msg);
  84 
  85     if (dbus_error_is_set(&error)) {
  86         crm_err("Could not send %s to systemd: %s (%s)",
  87                 method, error.message, error.name);
  88         dbus_error_free(&error);
  89         return NULL;
  90 
  91     } else if (reply == NULL) {
  92         crm_err("Could not send %s to systemd: no reply received", method);
  93         return NULL;
  94     }
  95 
  96     return reply;
  97 }
  98 
  99 static gboolean
 100 systemd_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 101 {
 102     static int need_init = 1;
 103     /* http://dbus.freedesktop.org/doc/api/html/group__DBusConnection.html */
 104 
 105     if (systemd_proxy
 106         && dbus_connection_get_is_connected(systemd_proxy) == FALSE) {
 107         crm_warn("Connection to System DBus is closed. Reconnecting...");
 108         pcmk_dbus_disconnect(systemd_proxy);
 109         systemd_proxy = NULL;
 110         need_init = 1;
 111     }
 112 
 113     if (need_init) {
 114         need_init = 0;
 115         systemd_proxy = pcmk_dbus_connect();
 116     }
 117     if (systemd_proxy == NULL) {
 118         return FALSE;
 119     }
 120     return TRUE;
 121 }
 122 
 123 static inline char *
 124 systemd_get_property(const char *unit, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 125                      void (*callback)(const char *name, const char *value, void *userdata),
 126                      void *userdata, DBusPendingCall **pending, int timeout)
 127 {
 128     return systemd_proxy?
 129            pcmk_dbus_get_property(systemd_proxy, BUS_NAME, unit, BUS_NAME_UNIT,
 130                                   name, callback, userdata, pending, timeout)
 131            : NULL;
 132 }
 133 
 134 void
 135 systemd_cleanup(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 136 {
 137     if (systemd_proxy) {
 138         pcmk_dbus_disconnect(systemd_proxy);
 139         systemd_proxy = NULL;
 140     }
 141 }
 142 
 143 /*
 144  * end of systemd_proxy functions
 145  */
 146 
 147 /*!
 148  * \internal
 149  * \brief Check whether a file name represents a systemd unit
 150  *
 151  * \param[in] name  File name to check
 152  *
 153  * \return Pointer to "dot" before filename extension if so, NULL otherwise
 154  */
 155 static const char *
 156 systemd_unit_extension(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 157 {
 158     if (name) {
 159         const char *dot = strrchr(name, '.');
 160 
 161         if (dot && (!strcmp(dot, ".service") || !strcmp(dot, ".socket"))) {
 162             return dot;
 163         }
 164     }
 165     return NULL;
 166 }
 167 
 168 static char *
 169 systemd_service_name(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 170 {
 171     if (name == NULL) {
 172         return NULL;
 173     }
 174 
 175     if (systemd_unit_extension(name)) {
 176         return strdup(name);
 177     }
 178 
 179     return crm_strdup_printf("%s.service", name);
 180 }
 181 
 182 static void
 183 systemd_daemon_reload_complete(DBusPendingCall *pending, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 184 {
 185     DBusError error;
 186     DBusMessage *reply = NULL;
 187     unsigned int reload_count = GPOINTER_TO_UINT(user_data);
 188 
 189     dbus_error_init(&error);
 190     if(pending) {
 191         reply = dbus_pending_call_steal_reply(pending);
 192     }
 193 
 194     if (pcmk_dbus_find_error(pending, reply, &error)) {
 195         crm_err("Could not issue systemd reload %d: %s", reload_count, error.message);
 196         dbus_error_free(&error);
 197 
 198     } else {
 199         crm_trace("Reload %d complete", reload_count);
 200     }
 201 
 202     if(pending) {
 203         dbus_pending_call_unref(pending);
 204     }
 205     if(reply) {
 206         dbus_message_unref(reply);
 207     }
 208 }
 209 
 210 static bool
 211 systemd_daemon_reload(int timeout)
     /* [previous][next][first][last][top][bottom][index][help] */
 212 {
 213     static unsigned int reload_count = 0;
 214     DBusMessage *msg = systemd_new_method("Reload");
 215 
 216     reload_count++;
 217     CRM_ASSERT(msg != NULL);
 218     systemd_send(msg, systemd_daemon_reload_complete,
 219                  GUINT_TO_POINTER(reload_count), timeout);
 220     dbus_message_unref(msg);
 221 
 222     return TRUE;
 223 }
 224 
 225 static bool
 226 systemd_mask_error(svc_action_t *op, const char *error)
     /* [previous][next][first][last][top][bottom][index][help] */
 227 {
 228     crm_trace("Could not issue %s for %s: %s", op->action, op->rsc, error);
 229     if(strstr(error, "org.freedesktop.systemd1.InvalidName")
 230        || strstr(error, "org.freedesktop.systemd1.LoadFailed")
 231        || strstr(error, "org.freedesktop.systemd1.NoSuchUnit")) {
 232 
 233         if (safe_str_eq(op->action, "stop")) {
 234             crm_trace("Masking %s failure for %s: unknown services are stopped", op->action, op->rsc);
 235             op->rc = PCMK_OCF_OK;
 236             return TRUE;
 237 
 238         } else {
 239             crm_trace("Mapping %s failure for %s: unknown services are not installed", op->action, op->rsc);
 240             op->rc = PCMK_OCF_NOT_INSTALLED;
 241             op->status = PCMK_LRM_OP_NOT_INSTALLED;
 242             return FALSE;
 243         }
 244     }
 245 
 246     return FALSE;
 247 }
 248 
 249 static const char *
 250 systemd_loadunit_result(DBusMessage *reply, svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 251 {
 252     const char *path = NULL;
 253     DBusError error;
 254 
 255     if (pcmk_dbus_find_error((void*)&path, reply, &error)) {
 256         if(op && !systemd_mask_error(op, error.name)) {
 257             crm_err("Could not load systemd unit %s for %s: %s",
 258                     op->agent, op->id, error.message);
 259         }
 260         dbus_error_free(&error);
 261 
 262     } else if(pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH, __FUNCTION__, __LINE__)) {
 263         dbus_message_get_args (reply, NULL,
 264                                DBUS_TYPE_OBJECT_PATH, &path,
 265                                DBUS_TYPE_INVALID);
 266     }
 267 
 268     if(op) {
 269         if (path) {
 270             systemd_unit_exec_with_unit(op, path);
 271 
 272         } else if (op->synchronous == FALSE) {
 273             operation_finalize(op);
 274         }
 275     }
 276 
 277     return path;
 278 }
 279 
 280 
 281 static void
 282 systemd_loadunit_cb(DBusPendingCall *pending, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 283 {
 284     DBusMessage *reply = NULL;
 285     svc_action_t * op = user_data;
 286 
 287     if(pending) {
 288         reply = dbus_pending_call_steal_reply(pending);
 289     }
 290 
 291     crm_trace("Got result: %p for %p / %p for %s", reply, pending, op->opaque->pending, op->id);
 292 
 293     CRM_LOG_ASSERT(pending == op->opaque->pending);
 294     services_set_op_pending(op, NULL);
 295 
 296     systemd_loadunit_result(reply, user_data);
 297 
 298     if(reply) {
 299         dbus_message_unref(reply);
 300     }
 301 }
 302 
 303 static char *
 304 systemd_unit_by_name(const gchar * arg_name, svc_action_t *op)
     /* [previous][next][first][last][top][bottom][index][help] */
 305 {
 306     DBusMessage *msg;
 307     DBusMessage *reply = NULL;
 308     DBusPendingCall* pending = NULL;
 309     char *name = NULL;
 310 
 311 /*
 312   Equivalent to GetUnit if it's already loaded
 313   <method name="LoadUnit">
 314    <arg name="name" type="s" direction="in"/>
 315    <arg name="unit" type="o" direction="out"/>
 316   </method>
 317  */
 318 
 319     if (systemd_init() == FALSE) {
 320         return FALSE;
 321     }
 322 
 323     msg = systemd_new_method("LoadUnit");
 324     CRM_ASSERT(msg != NULL);
 325 
 326     name = systemd_service_name(arg_name);
 327     CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID));
 328     free(name);
 329 
 330     if(op == NULL || op->synchronous) {
 331         const char *unit = NULL;
 332         char *munit = NULL;
 333 
 334         reply = systemd_send_recv(msg, NULL,
 335                                   (op? op->timeout : DBUS_TIMEOUT_USE_DEFAULT));
 336         dbus_message_unref(msg);
 337 
 338         unit = systemd_loadunit_result(reply, op);
 339         if(unit) {
 340             munit = strdup(unit);
 341         }
 342         if(reply) {
 343             dbus_message_unref(reply);
 344         }
 345         return munit;
 346     }
 347 
 348     pending = systemd_send(msg, systemd_loadunit_cb, op, op->timeout);
 349     if(pending) {
 350         services_set_op_pending(op, pending);
 351     }
 352 
 353     dbus_message_unref(msg);
 354     return NULL;
 355 }
 356 
 357 GList *
 358 systemd_unit_listall(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 359 {
 360     int nfiles = 0;
 361     GList *units = NULL;
 362     DBusMessageIter args;
 363     DBusMessageIter unit;
 364     DBusMessageIter elem;
 365     DBusMessage *reply = NULL;
 366 
 367     if (systemd_init() == FALSE) {
 368         return NULL;
 369     }
 370 
 371 /*
 372         "  <method name=\"ListUnitFiles\">\n"                               \
 373         "   <arg name=\"files\" type=\"a(ss)\" direction=\"out\"/>\n" \
 374         "  </method>\n"                                                 \
 375 */
 376 
 377     reply = systemd_call_simple_method("ListUnitFiles");
 378     if (reply == NULL) {
 379         return NULL;
 380     }
 381     if (!dbus_message_iter_init(reply, &args)) {
 382         crm_err("Could not list systemd unit files: systemd reply has no arguments");
 383         dbus_message_unref(reply);
 384         return NULL;
 385     }
 386     if (!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY,
 387                               __FUNCTION__, __LINE__)) {
 388         crm_err("Could not list systemd unit files: systemd reply has invalid arguments");
 389         dbus_message_unref(reply);
 390         return NULL;
 391     }
 392 
 393     dbus_message_iter_recurse(&args, &unit);
 394     for (; dbus_message_iter_get_arg_type(&unit) != DBUS_TYPE_INVALID;
 395         dbus_message_iter_next(&unit)) {
 396 
 397         DBusBasicValue value;
 398         const char *match = NULL;
 399         char *unit_name = NULL;
 400         char *basename = NULL;
 401 
 402         if(!pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_STRUCT, __FUNCTION__, __LINE__)) {
 403             crm_debug("ListUnitFiles reply has unexpected type");
 404             continue;
 405         }
 406 
 407         dbus_message_iter_recurse(&unit, &elem);
 408         if(!pcmk_dbus_type_check(reply, &elem, DBUS_TYPE_STRING, __FUNCTION__, __LINE__)) {
 409             crm_debug("ListUnitFiles reply does not contain a string");
 410             continue;
 411         }
 412 
 413         dbus_message_iter_get_basic(&elem, &value);
 414         if (value.str == NULL) {
 415             crm_debug("ListUnitFiles reply did not provide a string");
 416             continue;
 417         }
 418         crm_trace("DBus ListUnitFiles listed: %s", value.str);
 419 
 420         match = systemd_unit_extension(value.str);
 421         if (match == NULL) {
 422             // Unit files always have an extension, so skip if not present
 423             crm_debug("ListUnitFiles entry '%s' does not have an extension",
 424                       value.str);
 425             continue;
 426         }
 427 
 428         // ListUnitFiles returns full path names
 429         basename = strrchr(value.str, '/');
 430         if (basename) {
 431             basename = basename + 1;
 432         } else {
 433             basename = value.str;
 434         }
 435 
 436         /* Unit files will include types (such as .target) that we can't manage,
 437          * so filter the replies here.
 438          */
 439         if (!strcmp(match, ".service")) {
 440             // Service is the "default" unit type, so strip it
 441             unit_name = strndup(basename, match - basename);
 442 
 443         } else if (!strcmp(match, ".mount")
 444                    || !strcmp(match, ".socket")) {
 445             unit_name = strdup(basename);
 446         }
 447         if (unit_name == NULL) {
 448             crm_trace("ListUnitFiles entry '%s' is not manageable",
 449                       value.str);
 450             continue;
 451         }
 452 
 453         nfiles++;
 454         units = g_list_prepend(units, unit_name);
 455     }
 456 
 457     dbus_message_unref(reply);
 458 
 459     crm_trace("Found %d manageable systemd unit files", nfiles);
 460     units = g_list_sort(units, crm_alpha_sort);
 461     return units;
 462 }
 463 
 464 gboolean
 465 systemd_unit_exists(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 466 {
 467     char *unit = NULL;
 468 
 469     /* Note: Makes a blocking dbus calls
 470      * Used by resources_find_service_class() when resource class=service
 471      */
 472     unit = systemd_unit_by_name(name, NULL);
 473     if(unit) {
 474         free(unit);
 475         return TRUE;
 476     }
 477     return FALSE;
 478 }
 479 
 480 static char *
 481 systemd_unit_metadata(const char *name, int timeout)
     /* [previous][next][first][last][top][bottom][index][help] */
 482 {
 483     char *meta = NULL;
 484     char *desc = NULL;
 485     char *path = systemd_unit_by_name(name, NULL);
 486 
 487     if (path) {
 488         /* TODO: Worth a making blocking call for? Probably not. Possibly if cached. */
 489         desc = systemd_get_property(path, "Description", NULL, NULL, NULL,
 490                                     timeout);
 491     } else {
 492         desc = crm_strdup_printf("Systemd unit file for %s", name);
 493     }
 494 
 495     meta = crm_strdup_printf("<?xml version=\"1.0\"?>\n"
 496                            "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
 497                            "<resource-agent name=\"%s\" version=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n"
 498                            "  <version>1.0</version>\n"
 499                            "  <longdesc lang=\"en\">\n"
 500                            "    %s\n"
 501                            "  </longdesc>\n"
 502                            "  <shortdesc lang=\"en\">systemd unit file for %s</shortdesc>\n"
 503                            "  <parameters>\n"
 504                            "  </parameters>\n"
 505                            "  <actions>\n"
 506                            "    <action name=\"start\"   timeout=\"100\" />\n"
 507                            "    <action name=\"stop\"    timeout=\"100\" />\n"
 508                            "    <action name=\"status\"  timeout=\"100\" />\n"
 509                            "    <action name=\"monitor\" timeout=\"100\" interval=\"60\"/>\n"
 510                            "    <action name=\"meta-data\"  timeout=\"5\" />\n"
 511                            "  </actions>\n"
 512                            "  <special tag=\"systemd\">\n"
 513                            "  </special>\n" "</resource-agent>\n", name, desc, name);
 514     free(desc);
 515     free(path);
 516     return meta;
 517 }
 518 
 519 static void
 520 systemd_exec_result(DBusMessage *reply, svc_action_t *op)
     /* [previous][next][first][last][top][bottom][index][help] */
 521 {
 522     DBusError error;
 523 
 524     if (pcmk_dbus_find_error((void*)&error, reply, &error)) {
 525 
 526         /* ignore "already started" or "not running" errors */
 527         if (!systemd_mask_error(op, error.name)) {
 528             crm_err("Could not issue %s for %s: %s", op->action, op->rsc, error.message);
 529         }
 530         dbus_error_free(&error);
 531 
 532     } else {
 533         if(!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH, __FUNCTION__, __LINE__)) {
 534             crm_warn("Call to %s passed but return type was unexpected", op->action);
 535             op->rc = PCMK_OCF_OK;
 536 
 537         } else {
 538             const char *path = NULL;
 539 
 540             dbus_message_get_args (reply, NULL,
 541                                    DBUS_TYPE_OBJECT_PATH, &path,
 542                                    DBUS_TYPE_INVALID);
 543             crm_info("Call to %s passed: %s", op->action, path);
 544             op->rc = PCMK_OCF_OK;
 545         }
 546     }
 547 
 548     operation_finalize(op);
 549 }
 550 
 551 static void
 552 systemd_async_dispatch(DBusPendingCall *pending, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 553 {
 554     DBusMessage *reply = NULL;
 555     svc_action_t *op = user_data;
 556 
 557     if(pending) {
 558         reply = dbus_pending_call_steal_reply(pending);
 559     }
 560 
 561     crm_trace("Got result: %p for %p for %s, %s", reply, pending, op->rsc, op->action);
 562 
 563     CRM_LOG_ASSERT(pending == op->opaque->pending);
 564     services_set_op_pending(op, NULL);
 565     systemd_exec_result(reply, op);
 566 
 567     if(reply) {
 568         dbus_message_unref(reply);
 569     }
 570 }
 571 
 572 #define SYSTEMD_OVERRIDE_ROOT "/run/systemd/system/"
 573 
 574 static void
 575 systemd_unit_check(const char *name, const char *state, void *userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 576 {
 577     svc_action_t * op = userdata;
 578 
 579     crm_trace("Resource %s has %s='%s'", op->rsc, name, state);
 580 
 581     if(state == NULL) {
 582         op->rc = PCMK_OCF_NOT_RUNNING;
 583 
 584     } else if (g_strcmp0(state, "active") == 0) {
 585         op->rc = PCMK_OCF_OK;
 586     } else if (g_strcmp0(state, "reloading") == 0) {
 587         op->rc = PCMK_OCF_OK;
 588     } else if (g_strcmp0(state, "activating") == 0) {
 589         op->rc = PCMK_OCF_PENDING;
 590     } else if (g_strcmp0(state, "deactivating") == 0) {
 591         op->rc = PCMK_OCF_PENDING;
 592     } else {
 593         op->rc = PCMK_OCF_NOT_RUNNING;
 594     }
 595 
 596     if (op->synchronous == FALSE) {
 597         services_set_op_pending(op, NULL);
 598         operation_finalize(op);
 599     }
 600 }
 601 
 602 gboolean
 603 systemd_unit_exec_with_unit(svc_action_t * op, const char *unit)
     /* [previous][next][first][last][top][bottom][index][help] */
 604 {
 605     const char *method = op->action;
 606     DBusMessage *msg = NULL;
 607     DBusMessage *reply = NULL;
 608 
 609     CRM_ASSERT(unit);
 610 
 611     if (safe_str_eq(op->action, "monitor") || safe_str_eq(method, "status")) {
 612         DBusPendingCall *pending = NULL;
 613         char *state;
 614 
 615         state = systemd_get_property(unit, "ActiveState",
 616                                      (op->synchronous? NULL : systemd_unit_check),
 617                                      op, (op->synchronous? NULL : &pending),
 618                                      op->timeout);
 619         if (op->synchronous) {
 620             systemd_unit_check("ActiveState", state, op);
 621             free(state);
 622             return op->rc == PCMK_OCF_OK;
 623         } else if (pending) {
 624             services_set_op_pending(op, pending);
 625             return TRUE;
 626 
 627         } else {
 628             return operation_finalize(op);
 629         }
 630 
 631     } else if (g_strcmp0(method, "start") == 0) {
 632         FILE *file_strm = NULL;
 633         char *override_dir = crm_strdup_printf("%s/%s.service.d", SYSTEMD_OVERRIDE_ROOT, op->agent);
 634         char *override_file = crm_strdup_printf("%s/%s.service.d/50-pacemaker.conf", SYSTEMD_OVERRIDE_ROOT, op->agent);
 635         mode_t orig_umask;
 636 
 637         method = "StartUnit";
 638         crm_build_path(override_dir, 0755);
 639 
 640         /* Ensure the override file is world-readable. This is not strictly
 641          * necessary, but it avoids a systemd warning in the logs.
 642          */
 643         orig_umask = umask(S_IWGRP | S_IWOTH);
 644         file_strm = fopen(override_file, "w");
 645         umask(orig_umask);
 646 
 647         if (file_strm != NULL) {
 648             /* TODO: Insert the start timeout in too */
 649             char *override = crm_strdup_printf(
 650                 "[Unit]\n"
 651                 "Description=Cluster Controlled %s\n"
 652                 "Before=pacemaker.service\n"
 653                 "\n"
 654                 "[Service]\n"
 655                 "Restart=no\n",
 656                 op->agent);
 657 
 658             int rc = fprintf(file_strm, "%s\n", override);
 659 
 660             free(override);
 661             if (rc < 0) {
 662                 crm_perror(LOG_ERR, "Cannot write to systemd override file %s", override_file);
 663             }
 664 
 665         } else {
 666             crm_err("Cannot open systemd override file %s for writing", override_file);
 667         }
 668 
 669         if (file_strm != NULL) {
 670             fflush(file_strm);
 671             fclose(file_strm);
 672         }
 673         systemd_daemon_reload(op->timeout);
 674         free(override_file);
 675         free(override_dir);
 676 
 677     } else if (g_strcmp0(method, "stop") == 0) {
 678         char *override_file = crm_strdup_printf("%s/%s.service.d/50-pacemaker.conf", SYSTEMD_OVERRIDE_ROOT, op->agent);
 679 
 680         method = "StopUnit";
 681         unlink(override_file);
 682         free(override_file);
 683         systemd_daemon_reload(op->timeout);
 684 
 685     } else if (g_strcmp0(method, "restart") == 0) {
 686         method = "RestartUnit";
 687 
 688     } else {
 689         op->rc = PCMK_OCF_UNIMPLEMENT_FEATURE;
 690         goto cleanup;
 691     }
 692 
 693     crm_debug("Calling %s for %s: %s", method, op->rsc, unit);
 694 
 695     msg = systemd_new_method(method);
 696     CRM_ASSERT(msg != NULL);
 697 
 698     /* (ss) */
 699     {
 700         const char *replace_s = "replace";
 701         char *name = systemd_service_name(op->agent);
 702 
 703         CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID));
 704         CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &replace_s, DBUS_TYPE_INVALID));
 705 
 706         free(name);
 707     }
 708 
 709     if (op->synchronous == FALSE) {
 710         DBusPendingCall *pending = systemd_send(msg, systemd_async_dispatch,
 711                                                 op, op->timeout);
 712 
 713         dbus_message_unref(msg);
 714         if(pending) {
 715             services_set_op_pending(op, pending);
 716             return TRUE;
 717 
 718         } else {
 719             return operation_finalize(op);
 720         }
 721 
 722     } else {
 723         reply = systemd_send_recv(msg, NULL, op->timeout);
 724         dbus_message_unref(msg);
 725         systemd_exec_result(reply, op);
 726 
 727         if(reply) {
 728             dbus_message_unref(reply);
 729         }
 730         return FALSE;
 731     }
 732 
 733   cleanup:
 734     if (op->synchronous == FALSE) {
 735         return operation_finalize(op);
 736     }
 737 
 738     return op->rc == PCMK_OCF_OK;
 739 }
 740 
 741 static gboolean
 742 systemd_timeout_callback(gpointer p)
     /* [previous][next][first][last][top][bottom][index][help] */
 743 {
 744     svc_action_t * op = p;
 745 
 746     op->opaque->timerid = 0;
 747     crm_warn("%s operation on systemd unit %s named '%s' timed out", op->action, op->agent, op->rsc);
 748     operation_finalize(op);
 749 
 750     return FALSE;
 751 }
 752 
 753 /* For an asynchronous 'op', returns FALSE if 'op' should be free'd by the caller */
 754 /* For a synchronous 'op', returns FALSE if 'op' fails */
 755 gboolean
 756 systemd_unit_exec(svc_action_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 757 {
 758     char *unit = NULL;
 759 
 760     CRM_ASSERT(op);
 761     CRM_ASSERT(systemd_init());
 762     op->rc = PCMK_OCF_UNKNOWN_ERROR;
 763     crm_debug("Performing %ssynchronous %s op on systemd unit %s named '%s'",
 764               op->synchronous ? "" : "a", op->action, op->agent, op->rsc);
 765 
 766     if (safe_str_eq(op->action, "meta-data")) {
 767         /* TODO: See if we can teach the lrmd not to make these calls synchronously */
 768         op->stdout_data = systemd_unit_metadata(op->agent, op->timeout);
 769         op->rc = PCMK_OCF_OK;
 770 
 771         if (op->synchronous == FALSE) {
 772             return operation_finalize(op);
 773         }
 774         return TRUE;
 775     }
 776 
 777     unit = systemd_unit_by_name(op->agent, op);
 778     free(unit);
 779 
 780     if (op->synchronous == FALSE) {
 781         if (op->opaque->pending) {
 782             op->opaque->timerid = g_timeout_add(op->timeout + 5000, systemd_timeout_callback, op);
 783             services_add_inflight_op(op);
 784             return TRUE;
 785 
 786         } else {
 787             return operation_finalize(op);
 788         }
 789     }
 790 
 791     return op->rc == PCMK_OCF_OK;
 792 }

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