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