pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
systemd.c
Go to the documentation of this file.
1/*
2 * Copyright 2012-2025 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 <inttypes.h> // PRIu32
18#include <stdbool.h>
19#include <stdint.h> // uint32_t
20#include <stdio.h> // fopen(), NULL, etc.
21#include <sys/stat.h>
22#include <gio/gio.h>
23#include <services_private.h>
24#include <systemd.h>
25#include <dbus/dbus.h>
26#include <pcmk-dbus.h>
27
28static void invoke_unit_by_path(svc_action_t *op, const char *unit);
29
30/* Systemd D-Bus interface
31 * https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.systemd1.html
32 */
33#define BUS_NAME "org.freedesktop.systemd1"
34#define BUS_NAME_MANAGER BUS_NAME ".Manager"
35#define BUS_NAME_UNIT BUS_NAME ".Unit"
36#define BUS_PATH "/org/freedesktop/systemd1"
37
46int
48{
49 op->opaque->exec = strdup("systemd-dbus");
50 if (op->opaque->exec == NULL) {
51 return ENOMEM;
52 }
53 return pcmk_rc_ok;
54}
55
64enum ocf_exitcode
65services__systemd2ocf(int exit_status)
66{
67 // This library uses OCF codes for systemd actions
68 return (enum ocf_exitcode) exit_status;
69}
70
71static inline DBusMessage *
72systemd_new_method(const char *method)
73{
74 crm_trace("Calling: %s on " BUS_NAME_MANAGER, method);
75 return dbus_message_new_method_call(BUS_NAME, BUS_PATH, BUS_NAME_MANAGER,
76 method);
77}
78
79/*
80 * Functions to manage a static DBus connection
81 */
82
83static DBusConnection* systemd_proxy = NULL;
84
85static inline DBusPendingCall *
86systemd_send(DBusMessage *msg,
87 void(*done)(DBusPendingCall *pending, void *user_data),
88 void *user_data, int timeout)
89{
90 return pcmk_dbus_send(msg, systemd_proxy, done, user_data, timeout);
91}
92
93static inline DBusMessage *
94systemd_send_recv(DBusMessage *msg, DBusError *error, int timeout)
95{
96 return pcmk_dbus_send_recv(msg, systemd_proxy, error, timeout);
97}
98
110static DBusMessage *
111systemd_call_simple_method(const char *method)
112{
113 DBusMessage *msg = NULL;
114 DBusMessage *reply = NULL;
115 DBusError error;
116
117 /* Don't call systemd_init() here, because that calls this */
118 CRM_CHECK(systemd_proxy, return NULL);
119
120 msg = systemd_new_method(method);
121
122 if (msg == NULL) {
123 crm_err("Could not create message to send %s to systemd", method);
124 return NULL;
125 }
126
127 dbus_error_init(&error);
128 reply = systemd_send_recv(msg, &error, DBUS_TIMEOUT_USE_DEFAULT);
129 dbus_message_unref(msg);
130
131 if (dbus_error_is_set(&error)) {
132 crm_err("Could not send %s to systemd: %s (%s)",
133 method, error.message, error.name);
134 dbus_error_free(&error);
135 return NULL;
136
137 } else if (reply == NULL) {
138 crm_err("Could not send %s to systemd: no reply received", method);
139 return NULL;
140 }
141
142 return reply;
143}
144
155static int
156subscribe_to_signals(void)
157{
158 const char *match_rule = "type='signal',"
159 "sender='" BUS_NAME "',"
160 "interface='" BUS_NAME_MANAGER "',"
161 "path='" BUS_PATH "'";
162 DBusMessage *reply = NULL;
163 DBusError error;
164
165 /* Tell D-Bus to accept signal messages from systemd.
166 * https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
167 */
168 dbus_error_init(&error);
169 dbus_bus_add_match(systemd_proxy, match_rule, &error);
170
171 if (dbus_error_is_set(&error)) {
172 crm_err("Could not listen for systemd DBus signals: %s " QB_XS " (%s)",
173 error.message, error.name);
174 dbus_error_free(&error);
175 return ECOMM;
176 }
177
178 // Tell systemd to broadcast signals
179 reply = systemd_call_simple_method("Subscribe");
180 if (reply == NULL) {
181 dbus_bus_remove_match(systemd_proxy, match_rule, &error);
182 return ECOMM;
183 }
184
185 dbus_message_unref(reply);
186 return pcmk_rc_ok;
187}
188
189static bool
190systemd_init(void)
191{
192 static int need_init = 1;
193 // https://dbus.freedesktop.org/doc/api/html/group__DBusConnection.html
194
195 if (systemd_proxy
196 && dbus_connection_get_is_connected(systemd_proxy) == FALSE) {
197 crm_warn("Connection to System DBus is closed. Reconnecting...");
198 pcmk_dbus_disconnect(systemd_proxy);
199 systemd_proxy = NULL;
200 need_init = 1;
201 }
202
203 if (need_init) {
204 need_init = 0;
205 systemd_proxy = pcmk_dbus_connect();
206
207 if (subscribe_to_signals() != pcmk_rc_ok) {
208 pcmk_dbus_disconnect(systemd_proxy);
209 systemd_proxy = NULL;
210 }
211 }
212
213 return (systemd_proxy != NULL);
214}
215
216static inline char *
217systemd_get_property(const char *unit, const char *name,
218 void (*callback)(const char *name, const char *value, void *userdata),
219 void *userdata, DBusPendingCall **pending, int timeout)
220{
221 return systemd_proxy?
222 pcmk_dbus_get_property(systemd_proxy, BUS_NAME, unit, BUS_NAME_UNIT,
223 name, callback, userdata, pending, timeout)
224 : NULL;
225}
226
227void
229{
230 if (systemd_proxy) {
231 pcmk_dbus_disconnect(systemd_proxy);
232 systemd_proxy = NULL;
233 }
234}
235
236/*
237 * end of systemd_proxy functions
238 */
239
248static const char *
249systemd_unit_extension(const char *name)
250{
251 if (name) {
252 const char *dot = strrchr(name, '.');
253
254 if (dot && (!strcmp(dot, ".service")
255 || !strcmp(dot, ".socket")
256 || !strcmp(dot, ".mount")
257 || !strcmp(dot, ".timer")
258 || !strcmp(dot, ".path"))) {
259 return dot;
260 }
261 }
262 return NULL;
263}
264
265static char *
266systemd_unit_name(const char *name, bool add_instance_name)
267{
268 const char *dot = NULL;
269
270 if (pcmk__str_empty(name)) {
271 return NULL;
272 }
273
274 /* Services that end with an @ sign are systemd templates. They expect an
275 * instance name to follow the service name. If no instance name was
276 * provided, just add "pacemaker" to the string as the instance name. It
277 * doesn't seem to matter for purposes of looking up whether a service
278 * exists or not.
279 *
280 * A template can be specified either with or without the unit extension,
281 * so this block handles both cases.
282 */
283 dot = systemd_unit_extension(name);
284
285 if (dot) {
286 if (dot != name && *(dot-1) == '@') {
287 return crm_strdup_printf("%.*spacemaker%s",
288 (int) (dot - name), name, dot);
289 } else {
290 return pcmk__str_copy(name);
291 }
292
293 } else if (add_instance_name && *(name+strlen(name)-1) == '@') {
294 return crm_strdup_printf("%spacemaker.service", name);
295
296 } else {
297 return crm_strdup_printf("%s.service", name);
298 }
299}
300
301static void
302systemd_daemon_reload_complete(DBusPendingCall *pending, void *user_data)
303{
304 DBusError error;
305 DBusMessage *reply = NULL;
306 unsigned int reload_count = GPOINTER_TO_UINT(user_data);
307
308 dbus_error_init(&error);
309 if(pending) {
310 reply = dbus_pending_call_steal_reply(pending);
311 }
312
313 if (pcmk_dbus_find_error(pending, reply, &error)) {
314 crm_warn("Could not issue systemd reload %d: %s",
315 reload_count, error.message);
316 dbus_error_free(&error);
317
318 } else {
319 crm_trace("Reload %d complete", reload_count);
320 }
321
322 if(pending) {
323 dbus_pending_call_unref(pending);
324 }
325 if(reply) {
326 dbus_message_unref(reply);
327 }
328}
329
330static bool
331systemd_daemon_reload(int timeout)
332{
333 static unsigned int reload_count = 0;
334 DBusMessage *msg = systemd_new_method("Reload");
335
336 reload_count++;
337 pcmk__assert(msg != NULL);
338 systemd_send(msg, systemd_daemon_reload_complete,
339 GUINT_TO_POINTER(reload_count), timeout);
340 dbus_message_unref(msg);
341
342 return TRUE;
343}
344
352static void
353set_result_from_method_error(svc_action_t *op, const DBusError *error)
354{
356 "Unable to invoke systemd DBus method");
357
358 if (strstr(error->name, "org.freedesktop.systemd1.InvalidName")
359 || strstr(error->name, "org.freedesktop.systemd1.LoadFailed")
360 || strstr(error->name, "org.freedesktop.systemd1.NoSuchUnit")) {
361
362 if (pcmk__str_eq(op->action, PCMK_ACTION_STOP, pcmk__str_casei)) {
363 crm_trace("Masking systemd stop failure (%s) for %s "
364 "because unknown service can be considered stopped",
365 error->name, pcmk__s(op->rsc, "unknown resource"));
367 return;
368 }
369
372 "systemd unit %s not found", op->agent);
373
374 /* If systemd happens to be re-executing by `systemctl daemon-reexec` at the
375 * same time, dbus gives an error with the name
376 * `org.freedesktop.DBus.Error.NoReply` and the message "Message recipient
377 * disconnected from message bus without replying".
378 * Consider the monitor pending rather than return an error yet, so that it
379 * can retry with another iteration.
380 */
382 PCMK_ACTION_STATUS, NULL)
383 && strstr(error->name, DBUS_ERROR_NO_REPLY)
384 && strstr(error->message, "disconnected")) {
386 }
387
388 crm_info("DBus request for %s of systemd unit %s%s%s failed: %s",
389 op->action, op->agent,
390 ((op->rsc == NULL)? "" : " for resource "), pcmk__s(op->rsc, ""),
391 error->message);
392}
393
404static const char *
405execute_after_loadunit(DBusMessage *reply, svc_action_t *op)
406{
407 const char *path = NULL;
408 DBusError error;
409
410 /* path here is not used other than as a non-NULL flag to indicate that a
411 * request was indeed sent
412 */
413 if (pcmk_dbus_find_error((void *) &path, reply, &error)) {
414 if (op != NULL) {
415 set_result_from_method_error(op, &error);
416 }
417 dbus_error_free(&error);
418
419 } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
420 __func__, __LINE__)) {
421 if (op != NULL) {
423 "systemd DBus method had unexpected reply");
424 crm_info("Could not load systemd unit %s for %s: "
425 "DBus reply has unexpected type", op->agent, op->id);
426 } else {
427 crm_info("Could not load systemd unit: "
428 "DBus reply has unexpected type");
429 }
430
431 } else {
432 dbus_message_get_args (reply, NULL,
433 DBUS_TYPE_OBJECT_PATH, &path,
434 DBUS_TYPE_INVALID);
435 }
436
437 if (op != NULL) {
438 if (path != NULL) {
439 invoke_unit_by_path(op, path);
440
441 } else if (!(op->synchronous)) {
443 PCMK_ACTION_STATUS, NULL)
444 || op->status != PCMK_EXEC_PENDING) {
446 "No DBus object found for systemd unit %s",
447 op->agent);
448 }
450 }
451 }
452
453 return path;
454}
455
463static void
464loadunit_completed(DBusPendingCall *pending, void *user_data)
465{
466 DBusMessage *reply = NULL;
467 svc_action_t *op = user_data;
468
469 crm_trace("LoadUnit result for %s arrived", op->id);
470
471 // Grab the reply
472 if (pending != NULL) {
473 reply = dbus_pending_call_steal_reply(pending);
474 }
475
476 // The call is no longer pending
477 CRM_LOG_ASSERT(pending == op->opaque->pending);
478 services_set_op_pending(op, NULL);
479
480 // Execute the desired action based on the reply
481 execute_after_loadunit(reply, user_data);
482 if (reply != NULL) {
483 dbus_message_unref(reply);
484 }
485}
486
502static int
503invoke_unit_by_name(const char *arg_name, svc_action_t *op, char **path)
504{
505 DBusMessage *msg;
506 DBusMessage *reply = NULL;
507 DBusPendingCall *pending = NULL;
508 char *name = NULL;
509
510 if (pcmk__str_empty(arg_name)) {
511 return EINVAL;
512 }
513
514 if (!systemd_init()) {
515 if (op != NULL) {
517 "No DBus connection");
518 }
519 return ENOTCONN;
520 }
521
522 /* Create a LoadUnit DBus method (equivalent to GetUnit if already loaded),
523 * which makes the unit usable via further DBus methods.
524 *
525 * <method name="LoadUnit">
526 * <arg name="name" type="s" direction="in"/>
527 * <arg name="unit" type="o" direction="out"/>
528 * </method>
529 */
530 msg = systemd_new_method("LoadUnit");
531 pcmk__assert(msg != NULL);
532
533 // Add the (expanded) unit name as the argument
534 name = systemd_unit_name(arg_name,
535 (op == NULL)
536 || pcmk__str_eq(op->action, PCMK_ACTION_META_DATA,
538 CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
539 DBUS_TYPE_INVALID));
540 free(name);
541
542 if ((op == NULL) || op->synchronous) {
543 // For synchronous ops, wait for a reply and extract the result
544 const char *unit = NULL;
545 int rc = pcmk_rc_ok;
546
547 reply = systemd_send_recv(msg, NULL,
549 dbus_message_unref(msg);
550
551 unit = execute_after_loadunit(reply, op);
552 if (unit == NULL) {
553 rc = ENOENT;
554 if (path != NULL) {
555 *path = NULL;
556 }
557 } else if (path != NULL) {
558 *path = strdup(unit);
559 if (*path == NULL) {
560 rc = ENOMEM;
561 }
562 }
563
564 if (reply != NULL) {
565 dbus_message_unref(reply);
566 }
567 return rc;
568 }
569
570 // For asynchronous ops, initiate the LoadUnit call and return
571 pending = systemd_send(msg, loadunit_completed, op, op->timeout);
572 if (pending == NULL) {
574 "Unable to send DBus message");
575 dbus_message_unref(msg);
576 return ECOMM;
577 }
578
579 // LoadUnit was successfully initiated
581 services_set_op_pending(op, pending);
582 dbus_message_unref(msg);
583 return pcmk_rc_ok;
584}
585
598static gint
599sort_str(gconstpointer a, gconstpointer b)
600{
601 if (!a && !b) {
602 return 0;
603 } else if (!a) {
604 return -1;
605 } else if (!b) {
606 return 1;
607 }
608 return strcasecmp(a, b);
609}
610
611GList *
613{
614 int nfiles = 0;
615 GList *units = NULL;
616 DBusMessageIter args;
617 DBusMessageIter unit;
618 DBusMessageIter elem;
619 DBusMessage *reply = NULL;
620
621 if (!systemd_init()) {
622 return NULL;
623 }
624
625/*
626 " <method name=\"ListUnitFiles\">\n" \
627 " <arg name=\"files\" type=\"a(ss)\" direction=\"out\"/>\n" \
628 " </method>\n" \
629*/
630
631 reply = systemd_call_simple_method("ListUnitFiles");
632 if (reply == NULL) {
633 return NULL;
634 }
635 if (!dbus_message_iter_init(reply, &args)) {
636 crm_err("Could not list systemd unit files: systemd reply has no arguments");
637 dbus_message_unref(reply);
638 return NULL;
639 }
640 if (!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY,
641 __func__, __LINE__)) {
642 crm_err("Could not list systemd unit files: systemd reply has invalid arguments");
643 dbus_message_unref(reply);
644 return NULL;
645 }
646
647 dbus_message_iter_recurse(&args, &unit);
648 for (; dbus_message_iter_get_arg_type(&unit) != DBUS_TYPE_INVALID;
649 dbus_message_iter_next(&unit)) {
650
651 DBusBasicValue value;
652 const char *match = NULL;
653 char *unit_name = NULL;
654 char *basename = NULL;
655
656 if(!pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_STRUCT, __func__, __LINE__)) {
657 crm_warn("Skipping systemd reply argument with unexpected type");
658 continue;
659 }
660
661 dbus_message_iter_recurse(&unit, &elem);
662 if(!pcmk_dbus_type_check(reply, &elem, DBUS_TYPE_STRING, __func__, __LINE__)) {
663 crm_warn("Skipping systemd reply argument with no string");
664 continue;
665 }
666
667 dbus_message_iter_get_basic(&elem, &value);
668 if (value.str == NULL) {
669 crm_debug("ListUnitFiles reply did not provide a string");
670 continue;
671 }
672 crm_trace("DBus ListUnitFiles listed: %s", value.str);
673
674 match = systemd_unit_extension(value.str);
675 if (match == NULL) {
676 // This is not a unit file type we know how to manage
677 crm_debug("ListUnitFiles entry '%s' is not supported as resource",
678 value.str);
679 continue;
680 }
681
682 // ListUnitFiles returns full path names, we just want base name
683 basename = strrchr(value.str, '/');
684 if (basename) {
685 basename = basename + 1;
686 } else {
687 basename = value.str;
688 }
689
690 if (!strcmp(match, ".service")) {
691 // Service is the "default" unit type, so strip it
692 unit_name = strndup(basename, match - basename);
693 } else {
694 unit_name = strdup(basename);
695 }
696
697 nfiles++;
698 units = g_list_prepend(units, unit_name);
699 }
700
701 dbus_message_unref(reply);
702
703 crm_trace("Found %d manageable systemd unit files", nfiles);
704 units = g_list_sort(units, sort_str);
705 return units;
706}
707
708bool
710{
711 char *path = NULL;
712 char *state = NULL;
713 int rc = false;
714
715 /* Note: Makes a blocking dbus calls
716 * Used by resources_find_service_class() when resource class=service
717 */
718 if ((invoke_unit_by_name(name, NULL, &path) != pcmk_rc_ok)
719 || (path == NULL)) {
720 goto done;
721 }
722
723 /* A successful LoadUnit is not sufficient to determine the unit's
724 * existence; it merely means the LoadUnit request received a reply.
725 * We must make another blocking call to check the LoadState property.
726 */
727 state = systemd_get_property(path, "LoadState", NULL, NULL, NULL,
729 rc = pcmk__str_any_of(state, "loaded", "masked", NULL);
730
731done:
732 free(path);
733 free(state);
734 return rc;
735}
736
737// @TODO Use XML string constants and maybe a real XML object
738#define METADATA_FORMAT \
739 "<?xml " PCMK_XA_VERSION "=\"1.0\"?>\n" \
740 "<" PCMK_XE_RESOURCE_AGENT " " \
741 PCMK_XA_NAME "=\"%s\" " \
742 PCMK_XA_VERSION "=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n" \
743 " <" PCMK_XE_VERSION ">1.1</" PCMK_XE_VERSION ">\n" \
744 " <" PCMK_XE_LONGDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">\n" \
745 " %s\n" \
746 " </" PCMK_XE_LONGDESC ">\n" \
747 " <" PCMK_XE_SHORTDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">" \
748 "systemd unit file for %s" \
749 "</" PCMK_XE_SHORTDESC ">\n" \
750 " <" PCMK_XE_PARAMETERS "/>\n" \
751 " <" PCMK_XE_ACTIONS ">\n" \
752 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_START "\"" \
753 " " PCMK_META_TIMEOUT "=\"100s\" />\n" \
754 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STOP "\"" \
755 " " PCMK_META_TIMEOUT "=\"100s\" />\n" \
756 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STATUS "\"" \
757 " " PCMK_META_TIMEOUT "=\"100s\" />\n" \
758 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_MONITOR "\"" \
759 " " PCMK_META_TIMEOUT "=\"100s\"" \
760 " " PCMK_META_INTERVAL "=\"60s\" />\n" \
761 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_META_DATA "\"" \
762 " " PCMK_META_TIMEOUT "=\"5s\" />\n" \
763 " </" PCMK_XE_ACTIONS ">\n" \
764 " <" PCMK_XE_SPECIAL " " PCMK_XA_TAG "=\"systemd\"/>\n" \
765 "</" PCMK_XE_RESOURCE_AGENT ">\n"
766
767static char *
768systemd_unit_metadata(const char *name, int timeout)
769{
770 char *meta = NULL;
771 char *desc = NULL;
772 char *path = NULL;
773
774 if (invoke_unit_by_name(name, NULL, &path) == pcmk_rc_ok) {
775 /* TODO: Worth a making blocking call for? Probably not. Possibly if cached. */
776 desc = systemd_get_property(path, "Description", NULL, NULL, NULL,
777 timeout);
778 } else {
779 desc = crm_strdup_printf("Systemd unit file for %s", name);
780 }
781
783 gchar *escaped = pcmk__xml_escape(desc, pcmk__xml_escape_text);
784
785 meta = crm_strdup_printf(METADATA_FORMAT, name, escaped, name);
786 g_free(escaped);
787
788 } else {
790 }
791
792 free(desc);
793 free(path);
794 return meta;
795}
796
804static void
805process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
806{
807 bool start_stop = pcmk__strcase_any_of(op->action, PCMK_ACTION_START,
808 PCMK_ACTION_STOP, NULL);
809 DBusError error;
810
811 dbus_error_init(&error);
812
813 /* The first use of error here is not used other than as a non-NULL flag to
814 * indicate that a request was indeed sent
815 */
816 if (pcmk_dbus_find_error((void *) &error, reply, &error)) {
817 set_result_from_method_error(op, &error);
818 dbus_error_free(&error);
819
820 } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
821 __func__, __LINE__)) {
822 const char *reason = "systemd D-Bus method had unexpected reply";
823
824 crm_info("DBus request for %s of %s succeeded but "
825 "return type was unexpected",
826 op->action, pcmk__s(op->rsc, "unknown resource"));
827
828 if (!op->synchronous && start_stop) {
829 /* The start or stop job is enqueued but is not complete. We need a
830 * job path to detect completion in job_removed_filter().
831 */
833 reason);
834
835 } else {
836 /* Something weird happened, but the action is finished and there
837 * was no D-Bus error. So call it a success.
838 */
840 }
841
842 } else {
843 const char *path = NULL;
844
845 dbus_message_get_args(reply, NULL,
846 DBUS_TYPE_OBJECT_PATH, &path,
847 DBUS_TYPE_INVALID);
848
849 crm_debug("DBus request for %s of %s using %s succeeded",
850 op->action, pcmk__s(op->rsc, "unknown resource"), path);
851
852 if (!op->synchronous && start_stop) {
853 // Should be set to unknown/pending already
855 pcmk__str_update(&(op->opaque->job_path), path);
856
857 } else {
859 }
860 }
861}
862
882static DBusHandlerResult
883job_removed_filter(DBusConnection *connection, DBusMessage *message,
884 void *user_data)
885{
886 svc_action_t *action = user_data;
887 const char *action_name = NULL;
888 uint32_t job_id = 0;
889 const char *bus_path = NULL;
890 const char *unit_name = NULL;
891 const char *result = NULL;
892 DBusError error;
893
894 CRM_CHECK((connection != NULL) && (message != NULL),
895 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
896
897 // action should always be set when the filter is added
898 if ((action == NULL)
899 || !dbus_message_is_signal(message, BUS_NAME_MANAGER, "JobRemoved")) {
900 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
901 }
902
903 dbus_error_init(&error);
904 if (!dbus_message_get_args(message, &error,
905 DBUS_TYPE_UINT32, &job_id,
906 DBUS_TYPE_OBJECT_PATH, &bus_path,
907 DBUS_TYPE_STRING, &unit_name,
908 DBUS_TYPE_STRING, &result,
909 DBUS_TYPE_INVALID)) {
910 crm_err("Could not interpret systemd DBus signal: %s " QB_XS " (%s)",
911 error.message, error.name);
912 dbus_error_free(&error);
913 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
914 }
915
916 if (!pcmk__str_eq(bus_path, action->opaque->job_path, pcmk__str_none)) {
917 // This filter is not for this job
918 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
919 }
920
921 action_name = pcmk__s(action->action, "(unknown)");
922
923 crm_trace("Setting %s result for %s (JobRemoved id=%" PRIu32 ", result=%s",
924 action_name, unit_name, job_id, result);
925
926 if (pcmk__str_eq(result, "done", pcmk__str_none)) {
928
929 } else if (pcmk__str_eq(result, "timeout", pcmk__str_none)) {
931 "systemd %s job for %s timed out",
932 action_name, unit_name);
933
934 } else {
936 "systemd %s job for %s failed with result '%s'",
937 action_name, unit_name, result);
938 }
939
940 /* This instance of the filter was specifically for the given action.
941 *
942 * The action gets finalized by services__finalize_async_op() via the
943 * filter's free_data_function.
944 */
945 dbus_connection_remove_filter(systemd_proxy, job_removed_filter, action);
946 return DBUS_HANDLER_RESULT_HANDLED;
947}
948
955static void
956finalize_async_action_dbus(void *action)
957{
959}
960
968static void
969unit_method_complete(DBusPendingCall *pending, void *user_data)
970{
971 DBusMessage *reply = NULL;
972 svc_action_t *op = user_data;
973
974 crm_trace("Result for %s arrived", op->id);
975
976 // Grab the reply
977 if (pending != NULL) {
978 reply = dbus_pending_call_steal_reply(pending);
979 }
980
981 // The call is no longer pending
982 CRM_LOG_ASSERT(pending == op->opaque->pending);
983 services_set_op_pending(op, NULL);
984
985 process_unit_method_reply(reply, op);
986
987 if (reply != NULL) {
988 dbus_message_unref(reply);
989 }
990
991 if ((op->status == PCMK_EXEC_PENDING)
993 NULL)) {
994 /* Start and stop method calls return when the job is enqueued, not when
995 * it's complete. Start and stop actions must be finalized after the job
996 * is complete, because the action callback function may use it. We add
997 * a message filter to process the JobRemoved signal, which indicates
998 * completion.
999 *
1000 * The filter takes ownership of op, which will be finalized when the
1001 * filter is later removed.
1002 */
1003 if (dbus_connection_add_filter(systemd_proxy, job_removed_filter, op,
1004 finalize_async_action_dbus)) {
1005 return;
1006 }
1007 crm_err("Could not add D-Bus filter for systemd JobRemoved signals");
1009 "Failed to add D-Bus filter for systemd "
1010 "JobRemoved signal");
1011 }
1013}
1014
1015/* When the cluster manages a systemd resource, we create a unit file override
1016 * to order the service "before" pacemaker. The "before" relationship won't
1017 * actually be used, since systemd won't ever start the resource -- we're
1018 * interested in the reverse shutdown ordering it creates, to ensure that
1019 * systemd doesn't stop the resource at shutdown while pacemaker is still
1020 * running.
1021 *
1022 * @TODO Add start timeout
1023 */
1024#define SYSTEMD_UNIT_OVERRIDE_TEMPLATE \
1025 "[Unit]\n" \
1026 "Description=Cluster Controlled %s\n" \
1027 "Before=pacemaker.service pacemaker_remote.service\n"
1028
1029#define SYSTEMD_SERVICE_OVERRIDE \
1030 "\n" \
1031 "[Service]\n" \
1032 "Restart=no\n"
1033
1042static GString *
1043get_override_dir(const char *unit_name)
1044{
1045 GString *buf = g_string_sized_new(128);
1046
1047 pcmk__g_strcat(buf, "/run/systemd/system/", unit_name, ".d", NULL);
1048 return buf;
1049}
1050
1057static inline void
1058append_override_basename(GString *buf)
1059{
1060 g_string_append(buf, "/50-pacemaker.conf");
1061}
1062
1077static int
1078systemd_create_override(const char *agent, int timeout)
1079{
1080 char *unit_name = NULL;
1081 GString *filename = NULL;
1082 GString *override = NULL;
1083 FILE *fp = NULL;
1084 int fd = 0;
1085 int rc = pcmk_rc_ok;
1086
1087 unit_name = systemd_unit_name(agent, false);
1088 CRM_CHECK(!pcmk__str_empty(unit_name),
1089 rc = EINVAL; goto done);
1090
1091 filename = get_override_dir(unit_name);
1092 rc = pcmk__build_path(filename->str, 0755);
1093 if (rc != pcmk_rc_ok) {
1094 crm_err("Could not create systemd override directory %s: %s",
1095 filename->str, pcmk_rc_str(rc));
1096 goto done;
1097 }
1098
1099 append_override_basename(filename);
1100 fp = fopen(filename->str, "w");
1101 if (fp == NULL) {
1102 rc = errno;
1103 crm_err("Cannot open systemd override file %s for writing: %s",
1104 filename->str, pcmk_rc_str(rc));
1105 goto done;
1106 }
1107
1108 // Ensure the override file is world-readable (avoid systemd warning in log)
1109 fd = fileno(fp);
1110 if ((fd < 0) || (fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)) {
1111 rc = errno;
1112 crm_err("Failed to set permissions on systemd override file %s: %s",
1113 filename->str, pcmk_rc_str(rc));
1114 goto done;
1115 }
1116
1117 override = g_string_sized_new(2 * sizeof(SYSTEMD_UNIT_OVERRIDE_TEMPLATE));
1118 g_string_printf(override, SYSTEMD_UNIT_OVERRIDE_TEMPLATE, unit_name);
1119 if (pcmk__ends_with_ext(unit_name, ".service")) {
1120 g_string_append(override, SYSTEMD_SERVICE_OVERRIDE);
1121 }
1122
1123 if (fputs(override->str, fp) == EOF) {
1124 rc = EIO;
1125 crm_err("Cannot write to systemd override file %s", filename->str);
1126 }
1127
1128done:
1129 if (fp != NULL) {
1130 fclose(fp);
1131 }
1132
1133 if (rc == pcmk_rc_ok) {
1134 // @TODO Make sure the reload succeeds
1135 systemd_daemon_reload(timeout);
1136
1137 } else if (fp != NULL) {
1138 // File was created, so remove it
1139 unlink(filename->str);
1140 }
1141
1142 free(unit_name);
1143
1144 // coverity[check_after_deref : FALSE]
1145 if (filename != NULL) {
1146 g_string_free(filename, TRUE);
1147 }
1148 if (override != NULL) {
1149 g_string_free(override, TRUE);
1150 }
1151 return rc;
1152}
1153
1154static void
1155systemd_remove_override(const char *agent, int timeout)
1156{
1157 char *unit_name = systemd_unit_name(agent, false);
1158 GString *filename = NULL;
1159
1160 CRM_CHECK(!pcmk__str_empty(unit_name), goto done);
1161
1162 filename = get_override_dir(unit_name);
1163 append_override_basename(filename);
1164
1165 if (unlink(filename->str) < 0) {
1166 int rc = errno;
1167
1168 if (rc != ENOENT) {
1169 // Stop may be called when already stopped, which is fine
1170 crm_warn("Cannot remove systemd override file %s: %s",
1171 filename->str, pcmk_rc_str(rc));
1172 }
1173
1174 } else {
1175 systemd_daemon_reload(timeout);
1176 }
1177
1178done:
1179 free(unit_name);
1180
1181 // coverity[check_after_deref : FALSE]
1182 if (filename != NULL) {
1183 g_string_free(filename, TRUE);
1184 }
1185}
1186
1198static void
1199parse_status_result(const char *name, const char *state, void *userdata)
1200{
1201 svc_action_t *op = userdata;
1202
1203 crm_trace("Resource %s has %s='%s'",
1204 pcmk__s(op->rsc, "(unspecified)"), name,
1205 pcmk__s(state, "<null>"));
1206
1207 if (pcmk__str_eq(state, "active", pcmk__str_none)) {
1209
1210 } else if (pcmk__str_eq(state, "reloading", pcmk__str_none)) {
1212
1213 } else if (pcmk__str_eq(state, "activating", pcmk__str_none)) {
1215
1216 } else if (pcmk__str_eq(state, "deactivating", pcmk__str_none)) {
1218
1219 } else {
1221 }
1222
1223 if (!(op->synchronous)) {
1224 services_set_op_pending(op, NULL);
1226 }
1227}
1228
1236static void
1237invoke_unit_by_path(svc_action_t *op, const char *unit)
1238{
1239 const char *method = NULL;
1240 DBusMessage *msg = NULL;
1241 DBusMessage *reply = NULL;
1242
1244 NULL)) {
1245 DBusPendingCall *pending = NULL;
1246 char *state;
1247
1248 state = systemd_get_property(unit, "ActiveState",
1249 (op->synchronous? NULL : parse_status_result),
1250 op, (op->synchronous? NULL : &pending),
1251 op->timeout);
1252 if (op->synchronous) {
1253 parse_status_result("ActiveState", state, op);
1254 free(state);
1255
1256 } else if (pending == NULL) { // Could not get ActiveState property
1258 "Could not get state for unit %s from DBus",
1259 op->agent);
1261
1262 } else {
1263 services_set_op_pending(op, pending);
1264 }
1265 return;
1266
1267 } else if (pcmk__str_eq(op->action, PCMK_ACTION_START, pcmk__str_none)) {
1268 int rc = pcmk_rc_ok;
1269
1270 method = "StartUnit";
1271 rc = systemd_create_override(op->agent, op->timeout);
1272 if (rc != pcmk_rc_ok) {
1274 "Failed to create systemd override file "
1275 "for %s",
1276 pcmk__s(op->agent, "(unspecified)"));
1277 if (!(op->synchronous)) {
1279 }
1280 return;
1281 }
1282
1283 } else if (pcmk__str_eq(op->action, PCMK_ACTION_STOP, pcmk__str_none)) {
1284 method = "StopUnit";
1285 systemd_remove_override(op->agent, op->timeout);
1286
1287 } else if (pcmk__str_eq(op->action, "restart", pcmk__str_none)) {
1288 method = "RestartUnit";
1289
1290 } else {
1293 "Action %s not implemented "
1294 "for systemd resources",
1295 pcmk__s(op->action, "(unspecified)"));
1296 if (!(op->synchronous)) {
1298 }
1299 return;
1300 }
1301
1302 crm_trace("Calling %s for unit path %s%s%s",
1303 method, unit,
1304 ((op->rsc == NULL)? "" : " for resource "), pcmk__s(op->rsc, ""));
1305
1306 msg = systemd_new_method(method);
1307 pcmk__assert(msg != NULL);
1308
1309 /* (ss) */
1310 {
1311 const char *replace_s = "replace";
1312 char *name = systemd_unit_name(op->agent,
1313 pcmk__str_eq(op->action,
1316
1317 CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID));
1318 CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &replace_s, DBUS_TYPE_INVALID));
1319
1320 free(name);
1321 }
1322
1323 if (op->synchronous) {
1324 reply = systemd_send_recv(msg, NULL, op->timeout);
1325 dbus_message_unref(msg);
1326 process_unit_method_reply(reply, op);
1327 if (reply != NULL) {
1328 dbus_message_unref(reply);
1329 }
1330
1331 } else {
1332 DBusPendingCall *pending = systemd_send(msg, unit_method_complete, op,
1333 op->timeout);
1334
1335 dbus_message_unref(msg);
1336 if (pending == NULL) {
1338 "Unable to send DBus message");
1340
1341 } else {
1342 services_set_op_pending(op, pending);
1343 }
1344 }
1345}
1346
1347static gboolean
1348systemd_timeout_callback(gpointer p)
1349{
1350 svc_action_t * op = p;
1351
1352 op->opaque->timerid = 0;
1353 crm_info("%s action for systemd unit %s named '%s' timed out",
1354 op->action, op->agent, op->rsc);
1356 "%s action for systemd unit %s "
1357 "did not complete in time", op->action, op->agent);
1358
1359 if (op->opaque->job_path != NULL) {
1360 // A filter owns this op
1361 dbus_connection_remove_filter(systemd_proxy, job_removed_filter, op);
1362
1363 } else {
1365 }
1366 return FALSE;
1367}
1368
1385int
1387{
1388 pcmk__assert(op != NULL);
1389
1390 if (pcmk__str_empty(op->action) || pcmk__str_empty(op->agent)) {
1392 "Bug in action caller");
1393 goto done;
1394 }
1395
1396 if (!systemd_init()) {
1398 "No DBus connection");
1399 goto done;
1400 }
1401
1402 crm_debug("Performing %ssynchronous %s op on systemd unit %s%s%s",
1403 (op->synchronous? "" : "a"), op->action, op->agent,
1404 ((op->rsc == NULL)? "" : " for resource "), pcmk__s(op->rsc, ""));
1405
1406 if (pcmk__str_eq(op->action, PCMK_ACTION_META_DATA, pcmk__str_casei)) {
1407 op->stdout_data = systemd_unit_metadata(op->agent, op->timeout);
1409 goto done;
1410 }
1411
1412 /* invoke_unit_by_name() should always override these values, which are here
1413 * just as a fail-safe in case there are any code paths that neglect to
1414 */
1416 "Bug in service library");
1417
1418 if (invoke_unit_by_name(op->agent, op, NULL) == pcmk_rc_ok) {
1419 // @TODO Why plus 5000? No explanation in fccd046.
1420 op->opaque->timerid = pcmk__create_timer(op->timeout + 5000,
1421 systemd_timeout_callback, op);
1423 return pcmk_rc_ok;
1424 }
1425
1426done:
1427 if (op->synchronous) {
1428 return (op->rc == PCMK_OCF_OK)? pcmk_rc_ok : pcmk_rc_error;
1429 } else {
1430 return services__finalize_async_op(op);
1431 }
1432}
#define PCMK_ACTION_STATUS
Definition actions.h:64
#define PCMK_ACTION_STOP
Definition actions.h:66
#define PCMK_ACTION_META_DATA
Definition actions.h:47
#define PCMK_ACTION_START
Definition actions.h:63
#define PCMK_ACTION_MONITOR
Definition actions.h:51
const char * path
Definition cib.c:28
const char * name
Definition cib.c:26
guint pcmk__create_timer(guint interval_ms, GSourceFunc fn, gpointer data)
Definition utils.c:405
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:41
#define crm_info(fmt, args...)
Definition logging.h:365
#define crm_warn(fmt, args...)
Definition logging.h:360
#define CRM_LOG_ASSERT(expr)
Definition logging.h:196
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_err(fmt, args...)
Definition logging.h:357
#define crm_trace(fmt, args...)
Definition logging.h:370
Wrappers for and extensions to glib mainloop.
#define DBUS_TIMEOUT_USE_DEFAULT
Definition pcmk-dbus.h:19
unsigned int timeout
Definition pcmk_fence.c:34
const char * action
Definition pcmk_fence.c:32
pcmk__action_result_t result
Definition pcmk_fence.c:37
#define ECOMM
Definition portability.h:41
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:617
enum ocf_exitcode pcmk_rc2ocf(int rc)
Map a function return code to the most similar OCF exit code.
Definition results.c:952
ocf_exitcode
Exit status codes for resource agents.
Definition results.h:173
@ PCMK_OCF_UNIMPLEMENT_FEATURE
Requested action not implemented.
Definition results.h:180
@ PCMK_OCF_NOT_CONFIGURED
Parameter invalid (inherently)
Definition results.h:183
@ PCMK_OCF_NOT_INSTALLED
Dependencies not available locally.
Definition results.h:182
@ PCMK_OCF_UNKNOWN_ERROR
Unspecified error.
Definition results.h:177
@ PCMK_OCF_NOT_RUNNING
Service safely stopped.
Definition results.h:186
@ PCMK_OCF_OK
Success.
Definition results.h:174
@ PCMK_OCF_UNKNOWN
Action is pending.
Definition results.h:199
@ pcmk_rc_ok
Definition results.h:159
@ pcmk_rc_error
Definition results.h:154
@ PCMK_EXEC_ERROR_FATAL
Execution failed, do not retry anywhere.
Definition results.h:317
@ PCMK_EXEC_NOT_INSTALLED
Agent or dependency not available locally.
Definition results.h:318
@ PCMK_EXEC_DONE
Action completed, result is known.
Definition results.h:311
@ PCMK_EXEC_ERROR
Execution failed, may be retried.
Definition results.h:315
@ PCMK_EXEC_TIMEOUT
Action did not complete in time.
Definition results.h:313
@ PCMK_EXEC_PENDING
Action is in progress.
Definition results.h:310
#define pcmk__assert(expr)
void services_add_inflight_op(svc_action_t *op)
Definition services.c:814
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:1218
int services__finalize_async_op(svc_action_t *op)
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition strings.c:637
void pcmk__str_update(char **str, const char *value)
Definition strings.c:1280
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1029
@ pcmk__str_none
@ pcmk__str_casei
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1053
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1299
#define pcmk__str_copy(str)
Object for executing external actions.
Definition services.h:99
char * id
Definition services.h:103
char * agent
Resource agent name for resource actions, otherwise NULL.
Definition services.h:121
int rc
Exit status of action (set by library upon completion)
Definition services.h:132
char * rsc
XML ID of resource being executed for resource actions, otherwise NULL.
Definition services.h:106
char * action
Name of action being executed for resource actions, otherwise NULL.
Definition services.h:109
int synchronous
Definition services.h:150
int timeout
Action timeout (in milliseconds)
Definition services.h:123
char * stdout_data
Action stdout (set by library)
Definition services.h:155
int status
Execution status (enum pcmk_exec_status set by library)
Definition services.h:140
svc_action_private_t * opaque
This field should be treated as internal to Pacemaker.
Definition services.h:159
#define BUS_PATH
Definition systemd.c:36
#define BUS_NAME_MANAGER
Definition systemd.c:34
#define METADATA_FORMAT
Definition systemd.c:738
#define BUS_NAME_UNIT
Definition systemd.c:35
#define SYSTEMD_SERVICE_OVERRIDE
Definition systemd.c:1029
enum ocf_exitcode services__systemd2ocf(int exit_status)
Definition systemd.c:65
int services__execute_systemd(svc_action_t *op)
Definition systemd.c:1386
#define BUS_NAME
Definition systemd.c:33
int services__systemd_prepare(svc_action_t *op)
Definition systemd.c:47
GList * systemd_unit_listall(void)
Definition systemd.c:612
#define SYSTEMD_UNIT_OVERRIDE_TEMPLATE
Definition systemd.c:1024
void systemd_cleanup(void)
Definition systemd.c:228
bool systemd_unit_exists(const char *name)
Definition systemd.c:709
Wrappers for and extensions to libxml2.
bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
Definition xml.c:909
@ pcmk__xml_escape_text
char * pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
Definition xml.c:991