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