This source file includes following definitions.
- services__systemd_prepare
- services__systemd2ocf
- systemd_new_method
- systemd_send
- systemd_send_recv
- systemd_call_simple_method
- systemd_init
- systemd_get_property
- systemd_cleanup
- systemd_unit_extension
- systemd_service_name
- systemd_daemon_reload_complete
- systemd_daemon_reload
- set_result_from_method_error
- execute_after_loadunit
- loadunit_completed
- invoke_unit_by_name
- sort_str
- systemd_unit_listall
- systemd_unit_exists
- systemd_unit_metadata
- process_unit_method_reply
- unit_method_complete
- create_world_readable
- create_override_dir
- get_override_filename
- systemd_create_override
- systemd_remove_override
- parse_status_result
- invoke_unit_by_path
- systemd_timeout_callback
- services__execute_systemd
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11 #include <crm/crm.h>
12 #include <crm/common/xml.h>
13 #include <crm/services.h>
14 #include <crm/services_internal.h>
15 #include <crm/common/mainloop.h>
16
17 #include <sys/stat.h>
18 #include <gio/gio.h>
19 #include <services_private.h>
20 #include <systemd.h>
21 #include <dbus/dbus.h>
22 #include <pcmk-dbus.h>
23
24 static void invoke_unit_by_path(svc_action_t *op, const char *unit);
25
26 #define BUS_NAME "org.freedesktop.systemd1"
27 #define BUS_NAME_MANAGER BUS_NAME ".Manager"
28 #define BUS_NAME_UNIT BUS_NAME ".Unit"
29 #define BUS_PATH "/org/freedesktop/systemd1"
30
31
32
33
34
35
36
37
38
39 int
40 services__systemd_prepare(svc_action_t *op)
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
49
50
51
52
53
54
55
56
57 enum ocf_exitcode
58 services__systemd2ocf(int exit_status)
59 {
60
61 return (enum ocf_exitcode) exit_status;
62 }
63
64 static inline DBusMessage *
65 systemd_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
74
75
76 static DBusConnection* systemd_proxy = NULL;
77
78 static inline DBusPendingCall *
79 systemd_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
86 static inline DBusMessage *
87 systemd_send_recv(DBusMessage *msg, DBusError *error, int timeout)
88 {
89 return pcmk_dbus_send_recv(msg, systemd_proxy, error, timeout);
90 }
91
92
93
94
95
96
97
98
99
100
101
102
103 static DBusMessage *
104 systemd_call_simple_method(const char *method)
105 {
106 DBusMessage *msg = systemd_new_method(method);
107 DBusMessage *reply = NULL;
108 DBusError error;
109
110
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
136 static gboolean
137 systemd_init(void)
138 {
139 static int need_init = 1;
140
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
160 static inline char *
161 systemd_get_property(const char *unit, const char *name,
162 void (*callback)(const char *name, const char *value, void *userdata),
163 void *userdata, DBusPendingCall **pending, int timeout)
164 {
165 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
171 void
172 systemd_cleanup(void)
173 {
174 if (systemd_proxy) {
175 pcmk_dbus_disconnect(systemd_proxy);
176 systemd_proxy = NULL;
177 }
178 }
179
180
181
182
183
184
185
186
187
188
189
190
191
192 static const char *
193 systemd_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
209 static char *
210 systemd_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
219
220
221
222
223
224
225
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
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
251 static void
252 systemd_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
280 static bool
281 systemd_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
295
296
297
298
299
300
301
302 static void
303 set_result_from_method_error(svc_action_t *op, const DBusError *error)
304 {
305 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
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"));
316 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
317 return;
318 }
319
320 services__format_result(op, PCMK_OCF_NOT_INSTALLED,
321 PCMK_EXEC_NOT_INSTALLED,
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
331
332
333
334
335
336
337
338
339
340
341 static const char *
342 execute_after_loadunit(DBusMessage *reply, svc_action_t *op)
343 {
344 const char *path = NULL;
345 DBusError error;
346
347
348
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) {
359 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
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)) {
379 services__format_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
380 "No DBus object found for systemd unit %s",
381 op->agent);
382 services__finalize_async_op(op);
383 }
384 }
385
386 return path;
387 }
388
389
390
391
392
393
394
395
396 static void
397 loadunit_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
405 if (pending != NULL) {
406 reply = dbus_pending_call_steal_reply(pending);
407 }
408
409
410 CRM_LOG_ASSERT(pending == op->opaque->pending);
411 services_set_op_pending(op, NULL);
412
413
414 execute_after_loadunit(reply, user_data);
415 if (reply != NULL) {
416 dbus_message_unref(reply);
417 }
418 }
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435 static int
436 invoke_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) {
445 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
446 "No DBus connection");
447 }
448 return ENOTCONN;
449 }
450
451
452
453
454
455
456
457
458
459 msg = systemd_new_method("LoadUnit");
460 CRM_ASSERT(msg != NULL);
461
462
463 name = systemd_service_name(arg_name,
464 (op == NULL)
465 || pcmk__str_eq(op->action,
466 PCMK_ACTION_META_DATA,
467 pcmk__str_none));
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
474 const char *unit = NULL;
475 int rc = pcmk_rc_ok;
476
477 reply = systemd_send_recv(msg, NULL,
478 (op? op->timeout : DBUS_TIMEOUT_USE_DEFAULT));
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
501 pending = systemd_send(msg, loadunit_completed, op, op->timeout);
502 if (pending == NULL) {
503 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
504 "Unable to send DBus message");
505 dbus_message_unref(msg);
506 return ECOMM;
507 }
508
509
510 services__set_result(op, PCMK_OCF_UNKNOWN, PCMK_EXEC_PENDING, NULL);
511 services_set_op_pending(op, pending);
512 dbus_message_unref(msg);
513 return pcmk_rc_ok;
514 }
515
516
517
518
519
520
521
522
523
524
525
526
527
528 static gint
529 sort_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
541 GList *
542 systemd_unit_listall(void)
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
557
558
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
607 crm_debug("ListUnitFiles entry '%s' is not supported as resource",
608 value.str);
609 continue;
610 }
611
612
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
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
638 gboolean
639 systemd_unit_exists(const char *name)
640 {
641 char *path = NULL;
642 char *state = NULL;
643
644
645
646
647 if ((invoke_unit_by_name(name, NULL, &path) != pcmk_rc_ok)
648 || (path == NULL)) {
649 return FALSE;
650 }
651
652
653
654
655
656 state = systemd_get_property(path, "LoadState", NULL, NULL, NULL,
657 DBUS_TIMEOUT_USE_DEFAULT);
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
668 #define METADATA_FORMAT \
669 "<?xml " PCMK_XA_VERSION "=\"1.0\"?>\n" \
670 "<" PCMK_XE_RESOURCE_AGENT " " \
671 PCMK_XA_NAME "=\"%s\" " \
672 PCMK_XA_VERSION "=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n" \
673 " <" PCMK_XE_VERSION ">1.1</" PCMK_XE_VERSION ">\n" \
674 " <" PCMK_XE_LONGDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">\n" \
675 " %s\n" \
676 " </" PCMK_XE_LONGDESC ">\n" \
677 " <" PCMK_XE_SHORTDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">" \
678 "systemd unit file for %s" \
679 "</" PCMK_XE_SHORTDESC ">\n" \
680 " <" PCMK_XE_PARAMETERS "/>\n" \
681 " <" PCMK_XE_ACTIONS ">\n" \
682 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_START "\"" \
683 " " PCMK_META_TIMEOUT "=\"100s\" />\n" \
684 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STOP "\"" \
685 " " PCMK_META_TIMEOUT "=\"100s\" />\n" \
686 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STATUS "\"" \
687 " " PCMK_META_TIMEOUT "=\"100s\" />\n" \
688 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_MONITOR "\"" \
689 " " PCMK_META_TIMEOUT "=\"100s\"" \
690 " " PCMK_META_INTERVAL "=\"60s\" />\n" \
691 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_META_DATA "\"" \
692 " " PCMK_META_TIMEOUT "=\"5s\" />\n" \
693 " </" PCMK_XE_ACTIONS ">\n" \
694 " <" PCMK_XE_SPECIAL " " PCMK_XA_TAG "=\"systemd\"/>\n" \
695 "</" PCMK_XE_RESOURCE_AGENT ">\n"
696
697 static char *
698 systemd_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
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
712 if (pcmk__xml_needs_escape(desc, pcmk__xml_escape_text)) {
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 {
719 meta = crm_strdup_printf(METADATA_FORMAT, name, desc, name);
720 }
721
722 free(desc);
723 free(path);
724 return meta;
725 }
726
727
728
729
730
731
732
733
734 static void
735 process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
736 {
737 DBusError error;
738
739 dbus_error_init(&error);
740
741
742
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"));
753 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE,
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);
764 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
765 }
766 }
767
768
769
770
771
772
773
774
775 static void
776 unit_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
784 if (pending != NULL) {
785 reply = dbus_pending_call_steal_reply(pending);
786 }
787
788
789 CRM_LOG_ASSERT(pending == op->opaque->pending);
790 services_set_op_pending(op, NULL);
791
792
793 process_unit_method_reply(reply, op);
794 services__finalize_async_op(op);
795 if (reply != NULL) {
796 dbus_message_unref(reply);
797 }
798 }
799
800 #define SYSTEMD_OVERRIDE_ROOT "/run/systemd/system/"
801
802
803
804
805
806
807
808
809
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
820 static FILE *
821 create_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
830 static void
831 create_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
844 static char *
845 get_override_filename(const char *agent)
846 {
847 return crm_strdup_printf(SYSTEMD_OVERRIDE_ROOT
848 "/%s.service.d/50-pacemaker.conf", agent);
849 }
850
851 static void
852 systemd_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
860
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
884 static void
885 systemd_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
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
900
901
902
903
904
905
906
907
908
909
910
911 static void
912 parse_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)) {
921 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
922
923 } else if (pcmk__str_eq(state, "reloading", pcmk__str_none)) {
924 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
925
926 } else if (pcmk__str_eq(state, "activating", pcmk__str_none)) {
927 services__set_result(op, PCMK_OCF_UNKNOWN, PCMK_EXEC_PENDING, NULL);
928
929 } else if (pcmk__str_eq(state, "deactivating", pcmk__str_none)) {
930 services__set_result(op, PCMK_OCF_UNKNOWN, PCMK_EXEC_PENDING, NULL);
931
932 } else {
933 services__set_result(op, PCMK_OCF_NOT_RUNNING, PCMK_EXEC_DONE, state);
934 }
935
936 if (!(op->synchronous)) {
937 services_set_op_pending(op, NULL);
938 services__finalize_async_op(op);
939 }
940 }
941
942
943
944
945
946
947
948
949 static void
950 invoke_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
956 if (pcmk__str_any_of(op->action, PCMK_ACTION_MONITOR, PCMK_ACTION_STATUS,
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) {
970 services__format_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
971 "Could not get state for unit %s from DBus",
972 op->agent);
973 services__finalize_async_op(op);
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 {
992 services__format_result(op, PCMK_OCF_UNIMPLEMENT_FEATURE,
993 PCMK_EXEC_ERROR,
994 "Action %s not implemented "
995 "for systemd resources",
996 pcmk__s(op->action, "(unspecified)"));
997 if (!(op->synchronous)) {
998 services__finalize_async_op(op);
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
1011 {
1012 const char *replace_s = "replace";
1013 char *name = systemd_service_name(op->agent,
1014 pcmk__str_eq(op->action,
1015 PCMK_ACTION_META_DATA,
1016 pcmk__str_none));
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) {
1038 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
1039 "Unable to send DBus message");
1040 services__finalize_async_op(op);
1041
1042 } else {
1043 services_set_op_pending(op, pending);
1044 }
1045 }
1046 }
1047
1048 static gboolean
1049 systemd_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);
1056 services__format_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_TIMEOUT,
1057 "%s action for systemd unit %s "
1058 "did not complete in time", op->action, op->agent);
1059 services__finalize_async_op(op);
1060 return FALSE;
1061 }
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079 int
1080 services__execute_systemd(svc_action_t *op)
1081 {
1082 CRM_ASSERT(op != NULL);
1083
1084 if ((op->action == NULL) || (op->agent == NULL)) {
1085 services__set_result(op, PCMK_OCF_NOT_CONFIGURED, PCMK_EXEC_ERROR_FATAL,
1086 "Bug in action caller");
1087 goto done;
1088 }
1089
1090 if (!systemd_init()) {
1091 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
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);
1102 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
1103 goto done;
1104 }
1105
1106
1107
1108
1109 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
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);
1115 services_add_inflight_op(op);
1116 return pcmk_rc_ok;
1117 }
1118
1119 done:
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 }