pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
systemd.c
Go to the documentation of this file.
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>
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
24static 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
39int
41{
42 op->opaque->exec = strdup("systemd-dbus");
43 if (op->opaque->exec == NULL) {
44 return ENOMEM;
45 }
46 return pcmk_rc_ok;
47}
48
57enum ocf_exitcode
58services__systemd2ocf(int exit_status)
59{
60 // This library uses OCF codes for systemd actions
61 return (enum ocf_exitcode) exit_status;
62}
63
64static inline DBusMessage *
65systemd_new_method(const char *method)
66{
67 crm_trace("Calling: %s on " BUS_NAME_MANAGER, method);
68 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
76static DBusConnection* systemd_proxy = NULL;
77
78static inline DBusPendingCall *
79systemd_send(DBusMessage *msg,
80 void(*done)(DBusPendingCall *pending, void *user_data),
81 void *user_data, int timeout)
82{
83 return pcmk_dbus_send(msg, systemd_proxy, done, user_data, timeout);
84}
85
86static inline DBusMessage *
87systemd_send_recv(DBusMessage *msg, DBusError *error, int timeout)
88{
89 return pcmk_dbus_send_recv(msg, systemd_proxy, error, timeout);
90}
91
103static DBusMessage *
104systemd_call_simple_method(const char *method)
105{
106 DBusMessage *msg = systemd_new_method(method);
107 DBusMessage *reply = NULL;
108 DBusError error;
109
110 /* Don't call systemd_init() here, because that calls this */
111 CRM_CHECK(systemd_proxy, return NULL);
112
113 if (msg == NULL) {
114 crm_err("Could not create message to send %s to systemd", method);
115 return NULL;
116 }
117
118 dbus_error_init(&error);
119 reply = systemd_send_recv(msg, &error, DBUS_TIMEOUT_USE_DEFAULT);
120 dbus_message_unref(msg);
121
122 if (dbus_error_is_set(&error)) {
123 crm_err("Could not send %s to systemd: %s (%s)",
124 method, error.message, error.name);
125 dbus_error_free(&error);
126 return NULL;
127
128 } else if (reply == NULL) {
129 crm_err("Could not send %s to systemd: no reply received", method);
130 return NULL;
131 }
132
133 return reply;
134}
135
136static gboolean
137systemd_init(void)
138{
139 static int need_init = 1;
140 // https://dbus.freedesktop.org/doc/api/html/group__DBusConnection.html
141
142 if (systemd_proxy
143 && dbus_connection_get_is_connected(systemd_proxy) == FALSE) {
144 crm_warn("Connection to System DBus is closed. Reconnecting...");
145 pcmk_dbus_disconnect(systemd_proxy);
146 systemd_proxy = NULL;
147 need_init = 1;
148 }
149
150 if (need_init) {
151 need_init = 0;
152 systemd_proxy = pcmk_dbus_connect();
153 }
154 if (systemd_proxy == NULL) {
155 return FALSE;
156 }
157 return TRUE;
158}
159
160static inline char *
161systemd_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 return systemd_proxy?
166 pcmk_dbus_get_property(systemd_proxy, BUS_NAME, unit, BUS_NAME_UNIT,
167 name, callback, userdata, pending, timeout)
168 : NULL;
169}
170
171void
173{
174 if (systemd_proxy) {
175 pcmk_dbus_disconnect(systemd_proxy);
176 systemd_proxy = NULL;
177 }
178}
179
180/*
181 * end of systemd_proxy functions
182 */
183
192static const char *
193systemd_unit_extension(const char *name)
194{
195 if (name) {
196 const char *dot = strrchr(name, '.');
197
198 if (dot && (!strcmp(dot, ".service")
199 || !strcmp(dot, ".socket")
200 || !strcmp(dot, ".mount")
201 || !strcmp(dot, ".timer")
202 || !strcmp(dot, ".path"))) {
203 return dot;
204 }
205 }
206 return NULL;
207}
208
209static char *
210systemd_service_name(const char *name, bool add_instance_name)
211{
212 const char *dot = NULL;
213
214 if (pcmk__str_empty(name)) {
215 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 dot = systemd_unit_extension(name);
228
229 if (dot) {
230 if (dot != name && *(dot-1) == '@') {
231 char *s = NULL;
232
233 if (asprintf(&s, "%.*spacemaker%s", (int) (dot-name), name, dot) == -1) {
234 /* If asprintf fails, just return name. */
235 return strdup(name);
236 }
237
238 return s;
239 } else {
240 return strdup(name);
241 }
242
243 } else if (add_instance_name && *(name+strlen(name)-1) == '@') {
244 return crm_strdup_printf("%spacemaker.service", name);
245
246 } else {
247 return crm_strdup_printf("%s.service", name);
248 }
249}
250
251static void
252systemd_daemon_reload_complete(DBusPendingCall *pending, void *user_data)
253{
254 DBusError error;
255 DBusMessage *reply = NULL;
256 unsigned int reload_count = GPOINTER_TO_UINT(user_data);
257
258 dbus_error_init(&error);
259 if(pending) {
260 reply = dbus_pending_call_steal_reply(pending);
261 }
262
263 if (pcmk_dbus_find_error(pending, reply, &error)) {
264 crm_warn("Could not issue systemd reload %d: %s",
265 reload_count, error.message);
266 dbus_error_free(&error);
267
268 } else {
269 crm_trace("Reload %d complete", reload_count);
270 }
271
272 if(pending) {
273 dbus_pending_call_unref(pending);
274 }
275 if(reply) {
276 dbus_message_unref(reply);
277 }
278}
279
280static bool
281systemd_daemon_reload(int timeout)
282{
283 static unsigned int reload_count = 0;
284 DBusMessage *msg = systemd_new_method("Reload");
285
286 reload_count++;
287 CRM_ASSERT(msg != NULL);
288 systemd_send(msg, systemd_daemon_reload_complete,
289 GUINT_TO_POINTER(reload_count), timeout);
290 dbus_message_unref(msg);
291
292 return TRUE;
293}
294
302static void
303set_result_from_method_error(svc_action_t *op, const DBusError *error)
304{
306 "Unable to invoke systemd DBus method");
307
308 if (strstr(error->name, "org.freedesktop.systemd1.InvalidName")
309 || strstr(error->name, "org.freedesktop.systemd1.LoadFailed")
310 || strstr(error->name, "org.freedesktop.systemd1.NoSuchUnit")) {
311
312 if (pcmk__str_eq(op->action, PCMK_ACTION_STOP, pcmk__str_casei)) {
313 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"));
317 return;
318 }
319
322 "systemd unit %s not found", op->agent);
323 }
324
325 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
341static const char *
342execute_after_loadunit(DBusMessage *reply, svc_action_t *op)
343{
344 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 if (pcmk_dbus_find_error((void *) &path, reply, &error)) {
351 if (op != NULL) {
352 set_result_from_method_error(op, &error);
353 }
354 dbus_error_free(&error);
355
356 } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
357 __func__, __LINE__)) {
358 if (op != NULL) {
360 "systemd DBus method had unexpected reply");
361 crm_info("Could not load systemd unit %s for %s: "
362 "DBus reply has unexpected type", op->agent, op->id);
363 } else {
364 crm_info("Could not load systemd unit: "
365 "DBus reply has unexpected type");
366 }
367
368 } else {
369 dbus_message_get_args (reply, NULL,
370 DBUS_TYPE_OBJECT_PATH, &path,
371 DBUS_TYPE_INVALID);
372 }
373
374 if (op != NULL) {
375 if (path != NULL) {
376 invoke_unit_by_path(op, path);
377
378 } else if (!(op->synchronous)) {
380 "No DBus object found for systemd unit %s",
381 op->agent);
383 }
384 }
385
386 return path;
387}
388
396static void
397loadunit_completed(DBusPendingCall *pending, void *user_data)
398{
399 DBusMessage *reply = NULL;
400 svc_action_t *op = user_data;
401
402 crm_trace("LoadUnit result for %s arrived", op->id);
403
404 // Grab the reply
405 if (pending != NULL) {
406 reply = dbus_pending_call_steal_reply(pending);
407 }
408
409 // The call is no longer pending
410 CRM_LOG_ASSERT(pending == op->opaque->pending);
411 services_set_op_pending(op, NULL);
412
413 // Execute the desired action based on the reply
414 execute_after_loadunit(reply, user_data);
415 if (reply != NULL) {
416 dbus_message_unref(reply);
417 }
418}
419
435static int
436invoke_unit_by_name(const char *arg_name, svc_action_t *op, char **path)
437{
438 DBusMessage *msg;
439 DBusMessage *reply = NULL;
440 DBusPendingCall *pending = NULL;
441 char *name = NULL;
442
443 if (!systemd_init()) {
444 if (op != NULL) {
446 "No DBus connection");
447 }
448 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 msg = systemd_new_method("LoadUnit");
460 CRM_ASSERT(msg != NULL);
461
462 // Add the (expanded) unit name as the argument
463 name = systemd_service_name(arg_name,
464 (op == NULL)
465 || pcmk__str_eq(op->action,
468 CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
469 DBUS_TYPE_INVALID));
470 free(name);
471
472 if ((op == NULL) || op->synchronous) {
473 // For synchronous ops, wait for a reply and extract the result
474 const char *unit = NULL;
475 int rc = pcmk_rc_ok;
476
477 reply = systemd_send_recv(msg, NULL,
479 dbus_message_unref(msg);
480
481 unit = execute_after_loadunit(reply, op);
482 if (unit == NULL) {
483 rc = ENOENT;
484 if (path != NULL) {
485 *path = NULL;
486 }
487 } else if (path != NULL) {
488 *path = strdup(unit);
489 if (*path == NULL) {
490 rc = ENOMEM;
491 }
492 }
493
494 if (reply != NULL) {
495 dbus_message_unref(reply);
496 }
497 return rc;
498 }
499
500 // For asynchronous ops, initiate the LoadUnit call and return
501 pending = systemd_send(msg, loadunit_completed, op, op->timeout);
502 if (pending == NULL) {
504 "Unable to send DBus message");
505 dbus_message_unref(msg);
506 return ECOMM;
507 }
508
509 // LoadUnit was successfully initiated
511 services_set_op_pending(op, pending);
512 dbus_message_unref(msg);
513 return pcmk_rc_ok;
514}
515
528static gint
529sort_str(gconstpointer a, gconstpointer b)
530{
531 if (!a && !b) {
532 return 0;
533 } else if (!a) {
534 return -1;
535 } else if (!b) {
536 return 1;
537 }
538 return strcasecmp(a, b);
539}
540
541GList *
543{
544 int nfiles = 0;
545 GList *units = NULL;
546 DBusMessageIter args;
547 DBusMessageIter unit;
548 DBusMessageIter elem;
549 DBusMessage *reply = NULL;
550
551 if (systemd_init() == FALSE) {
552 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 reply = systemd_call_simple_method("ListUnitFiles");
562 if (reply == NULL) {
563 return NULL;
564 }
565 if (!dbus_message_iter_init(reply, &args)) {
566 crm_err("Could not list systemd unit files: systemd reply has no arguments");
567 dbus_message_unref(reply);
568 return NULL;
569 }
570 if (!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY,
571 __func__, __LINE__)) {
572 crm_err("Could not list systemd unit files: systemd reply has invalid arguments");
573 dbus_message_unref(reply);
574 return NULL;
575 }
576
577 dbus_message_iter_recurse(&args, &unit);
578 for (; dbus_message_iter_get_arg_type(&unit) != DBUS_TYPE_INVALID;
579 dbus_message_iter_next(&unit)) {
580
581 DBusBasicValue value;
582 const char *match = NULL;
583 char *unit_name = NULL;
584 char *basename = NULL;
585
586 if(!pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_STRUCT, __func__, __LINE__)) {
587 crm_warn("Skipping systemd reply argument with unexpected type");
588 continue;
589 }
590
591 dbus_message_iter_recurse(&unit, &elem);
592 if(!pcmk_dbus_type_check(reply, &elem, DBUS_TYPE_STRING, __func__, __LINE__)) {
593 crm_warn("Skipping systemd reply argument with no string");
594 continue;
595 }
596
597 dbus_message_iter_get_basic(&elem, &value);
598 if (value.str == NULL) {
599 crm_debug("ListUnitFiles reply did not provide a string");
600 continue;
601 }
602 crm_trace("DBus ListUnitFiles listed: %s", value.str);
603
604 match = systemd_unit_extension(value.str);
605 if (match == NULL) {
606 // This is not a unit file type we know how to manage
607 crm_debug("ListUnitFiles entry '%s' is not supported as resource",
608 value.str);
609 continue;
610 }
611
612 // ListUnitFiles returns full path names, we just want base name
613 basename = strrchr(value.str, '/');
614 if (basename) {
615 basename = basename + 1;
616 } else {
617 basename = value.str;
618 }
619
620 if (!strcmp(match, ".service")) {
621 // Service is the "default" unit type, so strip it
622 unit_name = strndup(basename, match - basename);
623 } else {
624 unit_name = strdup(basename);
625 }
626
627 nfiles++;
628 units = g_list_prepend(units, unit_name);
629 }
630
631 dbus_message_unref(reply);
632
633 crm_trace("Found %d manageable systemd unit files", nfiles);
634 units = g_list_sort(units, sort_str);
635 return units;
636}
637
638gboolean
640{
641 char *path = NULL;
642 char *state = NULL;
643
644 /* Note: Makes a blocking dbus calls
645 * Used by resources_find_service_class() when resource class=service
646 */
647 if ((invoke_unit_by_name(name, NULL, &path) != pcmk_rc_ok)
648 || (path == NULL)) {
649 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 state = systemd_get_property(path, "LoadState", NULL, NULL, NULL,
658 free(path);
659 if (pcmk__str_any_of(state, "loaded", "masked", NULL)) {
660 free(state);
661 return TRUE;
662 }
663 free(state);
664 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
697static char *
698systemd_unit_metadata(const char *name, int timeout)
699{
700 char *meta = NULL;
701 char *desc = NULL;
702 char *path = NULL;
703
704 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 desc = systemd_get_property(path, "Description", NULL, NULL, NULL,
707 timeout);
708 } else {
709 desc = crm_strdup_printf("Systemd unit file for %s", name);
710 }
711
713 gchar *escaped = pcmk__xml_escape(desc, pcmk__xml_escape_text);
714
715 meta = crm_strdup_printf(METADATA_FORMAT, name, escaped, name);
716 g_free(escaped);
717
718 } else {
720 }
721
722 free(desc);
723 free(path);
724 return meta;
725}
726
734static void
735process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
736{
737 DBusError error;
738
739 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 if (pcmk_dbus_find_error((void *) &error, reply, &error)) {
745 set_result_from_method_error(op, &error);
746 dbus_error_free(&error);
747
748 } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
749 __func__, __LINE__)) {
750 crm_info("DBus request for %s of %s succeeded but "
751 "return type was unexpected",
752 op->action, pcmk__s(op->rsc, "unknown resource"));
754 "systemd DBus method had unexpected reply");
755
756 } else {
757 const char *path = NULL;
758
759 dbus_message_get_args(reply, NULL,
760 DBUS_TYPE_OBJECT_PATH, &path,
761 DBUS_TYPE_INVALID);
762 crm_debug("DBus request for %s of %s using %s succeeded",
763 op->action, pcmk__s(op->rsc, "unknown resource"), path);
765 }
766}
767
775static void
776unit_method_complete(DBusPendingCall *pending, void *user_data)
777{
778 DBusMessage *reply = NULL;
779 svc_action_t *op = user_data;
780
781 crm_trace("Result for %s arrived", op->id);
782
783 // Grab the reply
784 if (pending != NULL) {
785 reply = dbus_pending_call_steal_reply(pending);
786 }
787
788 // The call is no longer pending
789 CRM_LOG_ASSERT(pending == op->opaque->pending);
790 services_set_op_pending(op, NULL);
791
792 // Determine result and finalize action
793 process_unit_method_reply(reply, op);
795 if (reply != NULL) {
796 dbus_message_unref(reply);
797 }
798}
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
820static FILE *
821create_world_readable(const char *filename)
822{
823 mode_t orig_umask = umask(S_IWGRP | S_IWOTH);
824 FILE *fp = fopen(filename, "w");
825
826 umask(orig_umask);
827 return fp;
828}
829
830static void
831create_override_dir(const char *agent)
832{
833 char *override_dir = crm_strdup_printf(SYSTEMD_OVERRIDE_ROOT
834 "/%s.service.d", agent);
835 int rc = pcmk__build_path(override_dir, 0755);
836
837 if (rc != pcmk_rc_ok) {
838 crm_warn("Could not create systemd override directory %s: %s",
839 override_dir, pcmk_rc_str(rc));
840 }
841 free(override_dir);
842}
843
844static char *
845get_override_filename(const char *agent)
846{
848 "/%s.service.d/50-pacemaker.conf", agent);
849}
850
851static void
852systemd_create_override(const char *agent, int timeout)
853{
854 FILE *file_strm = NULL;
855 char *override_file = get_override_filename(agent);
856
857 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 file_strm = create_world_readable(override_file);
863 if (file_strm == NULL) {
864 crm_err("Cannot open systemd override file %s for writing",
865 override_file);
866 } else {
867 char *override = crm_strdup_printf(SYSTEMD_OVERRIDE_TEMPLATE, agent);
868
869 int rc = fprintf(file_strm, "%s\n", override);
870
871 free(override);
872 if (rc < 0) {
873 crm_perror(LOG_WARNING, "Cannot write to systemd override file %s",
874 override_file);
875 }
876 fflush(file_strm);
877 fclose(file_strm);
878 systemd_daemon_reload(timeout);
879 }
880
881 free(override_file);
882}
883
884static void
885systemd_remove_override(const char *agent, int timeout)
886{
887 char *override_file = get_override_filename(agent);
888 int rc = unlink(override_file);
889
890 if (rc < 0) {
891 // Stop may be called when already stopped, which is fine
892 crm_perror(LOG_DEBUG, "Cannot remove systemd override file %s",
893 override_file);
894 } else {
895 systemd_daemon_reload(timeout);
896 }
897 free(override_file);
898}
899
911static void
912parse_status_result(const char *name, const char *state, void *userdata)
913{
914 svc_action_t *op = userdata;
915
916 crm_trace("Resource %s has %s='%s'",
917 pcmk__s(op->rsc, "(unspecified)"), name,
918 pcmk__s(state, "<null>"));
919
920 if (pcmk__str_eq(state, "active", pcmk__str_none)) {
922
923 } else if (pcmk__str_eq(state, "reloading", pcmk__str_none)) {
925
926 } else if (pcmk__str_eq(state, "activating", pcmk__str_none)) {
928
929 } else if (pcmk__str_eq(state, "deactivating", pcmk__str_none)) {
931
932 } else {
934 }
935
936 if (!(op->synchronous)) {
937 services_set_op_pending(op, NULL);
939 }
940}
941
949static void
950invoke_unit_by_path(svc_action_t *op, const char *unit)
951{
952 const char *method = NULL;
953 DBusMessage *msg = NULL;
954 DBusMessage *reply = NULL;
955
957 NULL)) {
958 DBusPendingCall *pending = NULL;
959 char *state;
960
961 state = systemd_get_property(unit, "ActiveState",
962 (op->synchronous? NULL : parse_status_result),
963 op, (op->synchronous? NULL : &pending),
964 op->timeout);
965 if (op->synchronous) {
966 parse_status_result("ActiveState", state, op);
967 free(state);
968
969 } else if (pending == NULL) { // Could not get ActiveState property
971 "Could not get state for unit %s from DBus",
972 op->agent);
974
975 } else {
976 services_set_op_pending(op, pending);
977 }
978 return;
979
980 } else if (pcmk__str_eq(op->action, PCMK_ACTION_START, pcmk__str_none)) {
981 method = "StartUnit";
982 systemd_create_override(op->agent, op->timeout);
983
984 } else if (pcmk__str_eq(op->action, PCMK_ACTION_STOP, pcmk__str_none)) {
985 method = "StopUnit";
986 systemd_remove_override(op->agent, op->timeout);
987
988 } else if (pcmk__str_eq(op->action, "restart", pcmk__str_none)) {
989 method = "RestartUnit";
990
991 } else {
994 "Action %s not implemented "
995 "for systemd resources",
996 pcmk__s(op->action, "(unspecified)"));
997 if (!(op->synchronous)) {
999 }
1000 return;
1001 }
1002
1003 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 msg = systemd_new_method(method);
1008 CRM_ASSERT(msg != NULL);
1009
1010 /* (ss) */
1011 {
1012 const char *replace_s = "replace";
1013 char *name = systemd_service_name(op->agent,
1014 pcmk__str_eq(op->action,
1017
1018 CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID));
1019 CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &replace_s, DBUS_TYPE_INVALID));
1020
1021 free(name);
1022 }
1023
1024 if (op->synchronous) {
1025 reply = systemd_send_recv(msg, NULL, op->timeout);
1026 dbus_message_unref(msg);
1027 process_unit_method_reply(reply, op);
1028 if (reply != NULL) {
1029 dbus_message_unref(reply);
1030 }
1031
1032 } else {
1033 DBusPendingCall *pending = systemd_send(msg, unit_method_complete, op,
1034 op->timeout);
1035
1036 dbus_message_unref(msg);
1037 if (pending == NULL) {
1039 "Unable to send DBus message");
1041
1042 } else {
1043 services_set_op_pending(op, pending);
1044 }
1045 }
1046}
1047
1048static gboolean
1049systemd_timeout_callback(gpointer p)
1050{
1051 svc_action_t * op = p;
1052
1053 op->opaque->timerid = 0;
1054 crm_info("%s action for systemd unit %s named '%s' timed out",
1055 op->action, op->agent, op->rsc);
1057 "%s action for systemd unit %s "
1058 "did not complete in time", op->action, op->agent);
1060 return FALSE;
1061}
1062
1079int
1081{
1082 CRM_ASSERT(op != NULL);
1083
1084 if ((op->action == NULL) || (op->agent == NULL)) {
1086 "Bug in action caller");
1087 goto done;
1088 }
1089
1090 if (!systemd_init()) {
1092 "No DBus connection");
1093 goto done;
1094 }
1095
1096 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 if (pcmk__str_eq(op->action, PCMK_ACTION_META_DATA, pcmk__str_casei)) {
1101 op->stdout_data = systemd_unit_metadata(op->agent, op->timeout);
1103 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 */
1110 "Bug in service library");
1111
1112 if (invoke_unit_by_name(op->agent, op, NULL) == pcmk_rc_ok) {
1113 op->opaque->timerid = g_timeout_add(op->timeout + 5000,
1114 systemd_timeout_callback, op);
1116 return pcmk_rc_ok;
1117 }
1118
1119done:
1120 if (op->synchronous) {
1121 return (op->rc == PCMK_OCF_OK)? pcmk_rc_ok : pcmk_rc_error;
1122 } else {
1123 return services__finalize_async_op(op);
1124 }
1125}
#define PCMK_ACTION_STATUS
Definition actions.h:73
#define PCMK_ACTION_STOP
Definition actions.h:75
#define PCMK_ACTION_META_DATA
Definition actions.h:56
#define PCMK_ACTION_START
Definition actions.h:72
#define PCMK_ACTION_MONITOR
Definition actions.h:60
const char * path
Definition cib.c:28
const char * name
Definition cib.c:26
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
A dumping ground.
char * pcmk_dbus_get_property(DBusConnection *connection, const char *target, const char *obj, const gchar *iface, const char *name, property_callback_func callback, void *userdata, DBusPendingCall **pending, int timeout)
Definition dbus.c:695
DBusConnection * pcmk_dbus_connect(void)
Definition dbus.c:259
DBusMessage * pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection, DBusError *error, int timeout)
Definition dbus.c:412
bool pcmk_dbus_find_error(const DBusPendingCall *pending, DBusMessage *reply, DBusError *ret)
Definition dbus.c:330
DBusPendingCall * pcmk_dbus_send(DBusMessage *msg, DBusConnection *connection, void(*done)(DBusPendingCall *pending, void *user_data), void *user_data, int timeout)
Definition dbus.c:476
bool pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected, const char *function, int line)
Definition dbus.c:516
void pcmk_dbus_disconnect(DBusConnection *connection)
Definition dbus.c:295
int pcmk__build_path(const char *path_c, mode_t mode)
Definition io.c:45
#define crm_info(fmt, args...)
Definition logging.h:397
#define crm_warn(fmt, args...)
Definition logging.h:392
#define CRM_LOG_ASSERT(expr)
Definition logging.h:228
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:331
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_debug(fmt, args...)
Definition logging.h:400
#define crm_err(fmt, args...)
Definition logging.h:389
#define crm_trace(fmt, args...)
Definition logging.h:402
Wrappers for and extensions to glib mainloop.
#define DBUS_TIMEOUT_USE_DEFAULT
Definition pcmk-dbus.h:16
unsigned int timeout
Definition pcmk_fence.c:32
#define ECOMM
Definition portability.h:86
#define CRM_ASSERT(expr)
Definition results.h:42
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:501
ocf_exitcode
Exit status codes for resource agents.
Definition results.h:177
@ PCMK_OCF_UNIMPLEMENT_FEATURE
Requested action not implemented.
Definition results.h:184
@ PCMK_OCF_NOT_CONFIGURED
Parameter invalid (inherently)
Definition results.h:187
@ PCMK_OCF_NOT_INSTALLED
Dependencies not available locally.
Definition results.h:186
@ PCMK_OCF_UNKNOWN_ERROR
Unspecified error.
Definition results.h:181
@ PCMK_OCF_NOT_RUNNING
Service safely stopped.
Definition results.h:190
@ PCMK_OCF_OK
Success.
Definition results.h:178
@ PCMK_OCF_UNKNOWN
Action is pending.
Definition results.h:203
@ pcmk_rc_ok
Definition results.h:162
@ pcmk_rc_error
Definition results.h:157
@ PCMK_EXEC_ERROR_FATAL
Execution failed, do not retry anywhere.
Definition results.h:339
@ PCMK_EXEC_NOT_INSTALLED
Agent or dependency not available locally.
Definition results.h:340
@ PCMK_EXEC_DONE
Action completed, result is known.
Definition results.h:333
@ PCMK_EXEC_ERROR
Execution failed, may be retried.
Definition results.h:337
@ PCMK_EXEC_TIMEOUT
Action did not complete in time.
Definition results.h:335
@ PCMK_EXEC_PENDING
Action is in progress.
Definition results.h:332
void services_add_inflight_op(svc_action_t *op)
Definition services.c:833
Services API.
void services__format_result(svc_action_t *action, int agent_status, enum pcmk_exec_status exec_status, const char *format,...) G_GNUC_PRINTF(4
void services__set_result(svc_action_t *action, int agent_status, enum pcmk_exec_status exec_status, const char *exit_reason)
Definition services.c:1269
int services__finalize_async_op(svc_action_t *op)
@ pcmk__str_none
@ pcmk__str_casei
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1050
Object for executing external actions.
Definition services.h:122
char * id
Definition services.h:126
char * agent
Resource agent name for resource actions, otherwise NULL.
Definition services.h:144
int rc
Exit status of action (set by library upon completion)
Definition services.h:155
char * rsc
XML ID of resource being executed for resource actions, otherwise NULL.
Definition services.h:129
char * action
Name of action being executed for resource actions, otherwise NULL.
Definition services.h:132
int synchronous
Definition services.h:173
int timeout
Action timeout (in milliseconds)
Definition services.h:146
char * stdout_data
Action stdout (set by library)
Definition services.h:178
svc_action_private_t * opaque
This field should be treated as internal to Pacemaker.
Definition services.h:182
gboolean systemd_unit_exists(const char *name)
Definition systemd.c:639
#define BUS_PATH
Definition systemd.c:29
#define BUS_NAME_MANAGER
Definition systemd.c:27
#define METADATA_FORMAT
Definition systemd.c:668
#define BUS_NAME_UNIT
Definition systemd.c:28
#define SYSTEMD_OVERRIDE_TEMPLATE
Definition systemd.c:811
enum ocf_exitcode services__systemd2ocf(int exit_status)
Definition systemd.c:58
#define SYSTEMD_OVERRIDE_ROOT
Definition systemd.c:800
int services__execute_systemd(svc_action_t *op)
Definition systemd.c:1080
#define BUS_NAME
Definition systemd.c:26
int services__systemd_prepare(svc_action_t *op)
Definition systemd.c:40
GList * systemd_unit_listall(void)
Definition systemd.c:542
void systemd_cleanup(void)
Definition systemd.c:172
Wrappers for and extensions to libxml2.
bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
Definition xml.c:1028
@ pcmk__xml_escape_text
char * pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
Definition xml.c:1110