pacemaker  2.1.8-3980678f03
Scalable High-Availability cluster resource manager
systemd.c
Go to the documentation of this file.
1 /*
2  * Copyright 2012-2024 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 #include <crm/crm.h>
12 #include <crm/common/xml.h>
13 #include <crm/services.h>
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 
39 int
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 
57 enum ocf_exitcode
58 services__systemd2ocf(int exit_status)
59 {
60  // This library uses OCF codes for systemd actions
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  * Functions to manage a static DBus connection
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 
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  /* Don't call systemd_init() here, because that calls this */
111  CRM_CHECK(systemd_proxy, return NULL);
112 
113  if (msg == NULL) {
114  crm_err("Could not create message to send %s to systemd", method);
115  return NULL;
116  }
117 
118  dbus_error_init(&error);
119  reply = systemd_send_recv(msg, &error, DBUS_TIMEOUT_USE_DEFAULT);
120  dbus_message_unref(msg);
121 
122  if (dbus_error_is_set(&error)) {
123  crm_err("Could not send %s to systemd: %s (%s)",
124  method, error.message, error.name);
125  dbus_error_free(&error);
126  return NULL;
127 
128  } else if (reply == NULL) {
129  crm_err("Could not send %s to systemd: no reply received", method);
130  return NULL;
131  }
132 
133  return reply;
134 }
135 
136 static gboolean
137 systemd_init(void)
138 {
139  static int need_init = 1;
140  // https://dbus.freedesktop.org/doc/api/html/group__DBusConnection.html
141 
142  if (systemd_proxy
143  && dbus_connection_get_is_connected(systemd_proxy) == FALSE) {
144  crm_warn("Connection to System DBus is closed. Reconnecting...");
145  pcmk_dbus_disconnect(systemd_proxy);
146  systemd_proxy = NULL;
147  need_init = 1;
148  }
149 
150  if (need_init) {
151  need_init = 0;
152  systemd_proxy = pcmk_dbus_connect();
153  }
154  if (systemd_proxy == NULL) {
155  return FALSE;
156  }
157  return TRUE;
158 }
159 
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
173 {
174  if (systemd_proxy) {
175  pcmk_dbus_disconnect(systemd_proxy);
176  systemd_proxy = NULL;
177  }
178 }
179 
180 /*
181  * end of systemd_proxy functions
182  */
183 
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  /* Services that end with an @ sign are systemd templates. They expect an
219  * instance name to follow the service name. If no instance name was
220  * provided, just add "pacemaker" to the string as the instance name. It
221  * doesn't seem to matter for purposes of looking up whether a service
222  * exists or not.
223  *
224  * A template can be specified either with or without the unit extension,
225  * so this block handles both cases.
226  */
227  dot = systemd_unit_extension(name);
228 
229  if (dot) {
230  if (dot != name && *(dot-1) == '@') {
231  char *s = NULL;
232 
233  if (asprintf(&s, "%.*spacemaker%s", (int) (dot-name), name, dot) == -1) {
234  /* If asprintf fails, just return name. */
235  return strdup(name);
236  }
237 
238  return s;
239  } else {
240  return strdup(name);
241  }
242 
243  } else if (add_instance_name && *(name+strlen(name)-1) == '@') {
244  return crm_strdup_printf("%spacemaker.service", name);
245 
246  } else {
247  return crm_strdup_printf("%s.service", name);
248  }
249 }
250 
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 
302 static void
303 set_result_from_method_error(svc_action_t *op, const DBusError *error)
304 {
306  "Unable to invoke systemd DBus method");
307 
308  if (strstr(error->name, "org.freedesktop.systemd1.InvalidName")
309  || strstr(error->name, "org.freedesktop.systemd1.LoadFailed")
310  || strstr(error->name, "org.freedesktop.systemd1.NoSuchUnit")) {
311 
312  if (pcmk__str_eq(op->action, PCMK_ACTION_STOP, pcmk__str_casei)) {
313  crm_trace("Masking systemd stop failure (%s) for %s "
314  "because unknown service can be considered stopped",
315  error->name, pcmk__s(op->rsc, "unknown resource"));
317  return;
318  }
319 
322  "systemd unit %s not found", op->agent);
323  }
324 
325  crm_info("DBus request for %s of systemd unit %s%s%s failed: %s",
326  op->action, op->agent,
327  ((op->rsc == NULL)? "" : " for resource "), pcmk__s(op->rsc, ""),
328  error->message);
329 }
330 
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  /* path here is not used other than as a non-NULL flag to indicate that a
348  * request was indeed sent
349  */
350  if (pcmk_dbus_find_error((void *) &path, reply, &error)) {
351  if (op != NULL) {
352  set_result_from_method_error(op, &error);
353  }
354  dbus_error_free(&error);
355 
356  } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
357  __func__, __LINE__)) {
358  if (op != NULL) {
360  "systemd DBus method had unexpected reply");
361  crm_info("Could not load systemd unit %s for %s: "
362  "DBus reply has unexpected type", op->agent, op->id);
363  } else {
364  crm_info("Could not load systemd unit: "
365  "DBus reply has unexpected type");
366  }
367 
368  } else {
369  dbus_message_get_args (reply, NULL,
370  DBUS_TYPE_OBJECT_PATH, &path,
371  DBUS_TYPE_INVALID);
372  }
373 
374  if (op != NULL) {
375  if (path != NULL) {
376  invoke_unit_by_path(op, path);
377 
378  } else if (!(op->synchronous)) {
380  "No DBus object found for systemd unit %s",
381  op->agent);
383  }
384  }
385 
386  return path;
387 }
388 
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  // Grab the reply
405  if (pending != NULL) {
406  reply = dbus_pending_call_steal_reply(pending);
407  }
408 
409  // The call is no longer pending
410  CRM_LOG_ASSERT(pending == op->opaque->pending);
411  services_set_op_pending(op, NULL);
412 
413  // Execute the desired action based on the reply
414  execute_after_loadunit(reply, user_data);
415  if (reply != NULL) {
416  dbus_message_unref(reply);
417  }
418 }
419 
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) {
446  "No DBus connection");
447  }
448  return ENOTCONN;
449  }
450 
451  /* Create a LoadUnit DBus method (equivalent to GetUnit if already loaded),
452  * which makes the unit usable via further DBus methods.
453  *
454  * <method name="LoadUnit">
455  * <arg name="name" type="s" direction="in"/>
456  * <arg name="unit" type="o" direction="out"/>
457  * </method>
458  */
459  msg = systemd_new_method("LoadUnit");
460  CRM_ASSERT(msg != NULL);
461 
462  // Add the (expanded) unit name as the argument
463  name = systemd_service_name(arg_name,
464  (op == NULL)
465  || pcmk__str_eq(op->action,
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  // For synchronous ops, wait for a reply and extract the result
474  const char *unit = NULL;
475  int rc = pcmk_rc_ok;
476 
477  reply = systemd_send_recv(msg, NULL,
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  // For asynchronous ops, initiate the LoadUnit call and return
501  pending = systemd_send(msg, loadunit_completed, op, op->timeout);
502  if (pending == NULL) {
504  "Unable to send DBus message");
505  dbus_message_unref(msg);
506  return ECOMM;
507  }
508 
509  // LoadUnit was successfully initiated
511  services_set_op_pending(op, pending);
512  dbus_message_unref(msg);
513  return pcmk_rc_ok;
514 }
515 
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 *
543 {
544  int nfiles = 0;
545  GList *units = NULL;
546  DBusMessageIter args;
547  DBusMessageIter unit;
548  DBusMessageIter elem;
549  DBusMessage *reply = NULL;
550 
551  if (systemd_init() == FALSE) {
552  return NULL;
553  }
554 
555 /*
556  " <method name=\"ListUnitFiles\">\n" \
557  " <arg name=\"files\" type=\"a(ss)\" direction=\"out\"/>\n" \
558  " </method>\n" \
559 */
560 
561  reply = systemd_call_simple_method("ListUnitFiles");
562  if (reply == NULL) {
563  return NULL;
564  }
565  if (!dbus_message_iter_init(reply, &args)) {
566  crm_err("Could not list systemd unit files: systemd reply has no arguments");
567  dbus_message_unref(reply);
568  return NULL;
569  }
570  if (!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY,
571  __func__, __LINE__)) {
572  crm_err("Could not list systemd unit files: systemd reply has invalid arguments");
573  dbus_message_unref(reply);
574  return NULL;
575  }
576 
577  dbus_message_iter_recurse(&args, &unit);
578  for (; dbus_message_iter_get_arg_type(&unit) != DBUS_TYPE_INVALID;
579  dbus_message_iter_next(&unit)) {
580 
581  DBusBasicValue value;
582  const char *match = NULL;
583  char *unit_name = NULL;
584  char *basename = NULL;
585 
586  if(!pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_STRUCT, __func__, __LINE__)) {
587  crm_warn("Skipping systemd reply argument with unexpected type");
588  continue;
589  }
590 
591  dbus_message_iter_recurse(&unit, &elem);
592  if(!pcmk_dbus_type_check(reply, &elem, DBUS_TYPE_STRING, __func__, __LINE__)) {
593  crm_warn("Skipping systemd reply argument with no string");
594  continue;
595  }
596 
597  dbus_message_iter_get_basic(&elem, &value);
598  if (value.str == NULL) {
599  crm_debug("ListUnitFiles reply did not provide a string");
600  continue;
601  }
602  crm_trace("DBus ListUnitFiles listed: %s", value.str);
603 
604  match = systemd_unit_extension(value.str);
605  if (match == NULL) {
606  // This is not a unit file type we know how to manage
607  crm_debug("ListUnitFiles entry '%s' is not supported as resource",
608  value.str);
609  continue;
610  }
611 
612  // ListUnitFiles returns full path names, we just want base name
613  basename = strrchr(value.str, '/');
614  if (basename) {
615  basename = basename + 1;
616  } else {
617  basename = value.str;
618  }
619 
620  if (!strcmp(match, ".service")) {
621  // Service is the "default" unit type, so strip it
622  unit_name = strndup(basename, match - basename);
623  } else {
624  unit_name = strdup(basename);
625  }
626 
627  nfiles++;
628  units = g_list_prepend(units, unit_name);
629  }
630 
631  dbus_message_unref(reply);
632 
633  crm_trace("Found %d manageable systemd unit files", nfiles);
634  units = g_list_sort(units, sort_str);
635  return units;
636 }
637 
638 gboolean
640 {
641  char *path = NULL;
642  char *state = NULL;
643 
644  /* Note: Makes a blocking dbus calls
645  * Used by resources_find_service_class() when resource class=service
646  */
647  if ((invoke_unit_by_name(name, NULL, &path) != pcmk_rc_ok)
648  || (path == NULL)) {
649  return FALSE;
650  }
651 
652  /* A successful LoadUnit is not sufficient to determine the unit's
653  * existence; it merely means the LoadUnit request received a reply.
654  * We must make another blocking call to check the LoadState property.
655  */
656  state = systemd_get_property(path, "LoadState", NULL, NULL, NULL,
658  free(path);
659  if (pcmk__str_any_of(state, "loaded", "masked", NULL)) {
660  free(state);
661  return TRUE;
662  }
663  free(state);
664  return FALSE;
665 }
666 
667 // @TODO Use XML string constants and maybe a real XML object
668 #define METADATA_FORMAT \
669  "<?xml " PCMK_XA_VERSION "=\"1.0\"?>\n" \
670  "<" PCMK_XE_RESOURCE_AGENT " " \
671  PCMK_XA_NAME "=\"%s\" " \
672  PCMK_XA_VERSION "=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n" \
673  " <" PCMK_XE_VERSION ">1.1</" PCMK_XE_VERSION ">\n" \
674  " <" PCMK_XE_LONGDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">\n" \
675  " %s\n" \
676  " </" PCMK_XE_LONGDESC ">\n" \
677  " <" PCMK_XE_SHORTDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">" \
678  "systemd unit file for %s" \
679  "</" PCMK_XE_SHORTDESC ">\n" \
680  " <" PCMK_XE_PARAMETERS "/>\n" \
681  " <" PCMK_XE_ACTIONS ">\n" \
682  " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_START "\"" \
683  " " PCMK_META_TIMEOUT "=\"100s\" />\n" \
684  " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STOP "\"" \
685  " " PCMK_META_TIMEOUT "=\"100s\" />\n" \
686  " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STATUS "\"" \
687  " " PCMK_META_TIMEOUT "=\"100s\" />\n" \
688  " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_MONITOR "\"" \
689  " " PCMK_META_TIMEOUT "=\"100s\"" \
690  " " PCMK_META_INTERVAL "=\"60s\" />\n" \
691  " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_META_DATA "\"" \
692  " " PCMK_META_TIMEOUT "=\"5s\" />\n" \
693  " </" PCMK_XE_ACTIONS ">\n" \
694  " <" PCMK_XE_SPECIAL " " PCMK_XA_TAG "=\"systemd\"/>\n" \
695  "</" PCMK_XE_RESOURCE_AGENT ">\n"
696 
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  /* TODO: Worth a making blocking call for? Probably not. Possibly if cached. */
706  desc = systemd_get_property(path, "Description", NULL, NULL, NULL,
707  timeout);
708  } else {
709  desc = crm_strdup_printf("Systemd unit file for %s", name);
710  }
711 
713  gchar *escaped = pcmk__xml_escape(desc, pcmk__xml_escape_text);
714 
715  meta = crm_strdup_printf(METADATA_FORMAT, name, escaped, name);
716  g_free(escaped);
717 
718  } else {
719  meta = crm_strdup_printf(METADATA_FORMAT, name, desc, name);
720  }
721 
722  free(desc);
723  free(path);
724  return meta;
725 }
726 
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  /* The first use of error here is not used other than as a non-NULL flag to
742  * indicate that a request was indeed sent
743  */
744  if (pcmk_dbus_find_error((void *) &error, reply, &error)) {
745  set_result_from_method_error(op, &error);
746  dbus_error_free(&error);
747 
748  } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
749  __func__, __LINE__)) {
750  crm_info("DBus request for %s of %s succeeded but "
751  "return type was unexpected",
752  op->action, pcmk__s(op->rsc, "unknown resource"));
754  "systemd DBus method had unexpected reply");
755 
756  } else {
757  const char *path = NULL;
758 
759  dbus_message_get_args(reply, NULL,
760  DBUS_TYPE_OBJECT_PATH, &path,
761  DBUS_TYPE_INVALID);
762  crm_debug("DBus request for %s of %s using %s succeeded",
763  op->action, pcmk__s(op->rsc, "unknown resource"), path);
765  }
766 }
767 
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  // Grab the reply
784  if (pending != NULL) {
785  reply = dbus_pending_call_steal_reply(pending);
786  }
787 
788  // The call is no longer pending
789  CRM_LOG_ASSERT(pending == op->opaque->pending);
790  services_set_op_pending(op, NULL);
791 
792  // Determine result and finalize action
793  process_unit_method_reply(reply, op);
795  if (reply != NULL) {
796  dbus_message_unref(reply);
797  }
798 }
799 
800 #define SYSTEMD_OVERRIDE_ROOT "/run/systemd/system/"
801 
802 /* When the cluster manages a systemd resource, we create a unit file override
803  * to order the service "before" pacemaker. The "before" relationship won't
804  * actually be used, since systemd won't ever start the resource -- we're
805  * interested in the reverse shutdown ordering it creates, to ensure that
806  * systemd doesn't stop the resource at shutdown while pacemaker is still
807  * running.
808  *
809  * @TODO Add start timeout
810  */
811 #define SYSTEMD_OVERRIDE_TEMPLATE \
812  "[Unit]\n" \
813  "Description=Cluster Controlled %s\n" \
814  "Before=pacemaker.service pacemaker_remote.service\n" \
815  "\n" \
816  "[Service]\n" \
817  "Restart=no\n"
818 
819 // Temporarily use rwxr-xr-x umask when opening a file for writing
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 {
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  /* Ensure the override file is world-readable. This is not strictly
860  * necessary, but it avoids a systemd warning in the logs.
861  */
862  file_strm = create_world_readable(override_file);
863  if (file_strm == NULL) {
864  crm_err("Cannot open systemd override file %s for writing",
865  override_file);
866  } else {
867  char *override = crm_strdup_printf(SYSTEMD_OVERRIDE_TEMPLATE, agent);
868 
869  int rc = fprintf(file_strm, "%s\n", override);
870 
871  free(override);
872  if (rc < 0) {
873  crm_perror(LOG_WARNING, "Cannot write to systemd override file %s",
874  override_file);
875  }
876  fflush(file_strm);
877  fclose(file_strm);
878  systemd_daemon_reload(timeout);
879  }
880 
881  free(override_file);
882 }
883 
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  // Stop may be called when already stopped, which is fine
892  crm_perror(LOG_DEBUG, "Cannot remove systemd override file %s",
893  override_file);
894  } else {
895  systemd_daemon_reload(timeout);
896  }
897  free(override_file);
898 }
899 
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)) {
922 
923  } else if (pcmk__str_eq(state, "reloading", pcmk__str_none)) {
925 
926  } else if (pcmk__str_eq(state, "activating", pcmk__str_none)) {
928 
929  } else if (pcmk__str_eq(state, "deactivating", pcmk__str_none)) {
931 
932  } else {
934  }
935 
936  if (!(op->synchronous)) {
937  services_set_op_pending(op, NULL);
939  }
940 }
941 
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 
957  NULL)) {
958  DBusPendingCall *pending = NULL;
959  char *state;
960 
961  state = systemd_get_property(unit, "ActiveState",
962  (op->synchronous? NULL : parse_status_result),
963  op, (op->synchronous? NULL : &pending),
964  op->timeout);
965  if (op->synchronous) {
966  parse_status_result("ActiveState", state, op);
967  free(state);
968 
969  } else if (pending == NULL) { // Could not get ActiveState property
971  "Could not get state for unit %s from DBus",
972  op->agent);
974 
975  } else {
976  services_set_op_pending(op, pending);
977  }
978  return;
979 
980  } else if (pcmk__str_eq(op->action, PCMK_ACTION_START, pcmk__str_none)) {
981  method = "StartUnit";
982  systemd_create_override(op->agent, op->timeout);
983 
984  } else if (pcmk__str_eq(op->action, PCMK_ACTION_STOP, pcmk__str_none)) {
985  method = "StopUnit";
986  systemd_remove_override(op->agent, op->timeout);
987 
988  } else if (pcmk__str_eq(op->action, "restart", pcmk__str_none)) {
989  method = "RestartUnit";
990 
991  } else {
994  "Action %s not implemented "
995  "for systemd resources",
996  pcmk__s(op->action, "(unspecified)"));
997  if (!(op->synchronous)) {
999  }
1000  return;
1001  }
1002 
1003  crm_trace("Calling %s for unit path %s%s%s",
1004  method, unit,
1005  ((op->rsc == NULL)? "" : " for resource "), pcmk__s(op->rsc, ""));
1006 
1007  msg = systemd_new_method(method);
1008  CRM_ASSERT(msg != NULL);
1009 
1010  /* (ss) */
1011  {
1012  const char *replace_s = "replace";
1013  char *name = systemd_service_name(op->agent,
1014  pcmk__str_eq(op->action,
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) {
1039  "Unable to send DBus message");
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);
1057  "%s action for systemd unit %s "
1058  "did not complete in time", op->action, op->agent);
1060  return FALSE;
1061 }
1062 
1079 int
1081 {
1082  CRM_ASSERT(op != NULL);
1083 
1084  if ((op->action == NULL) || (op->agent == NULL)) {
1086  "Bug in action caller");
1087  goto done;
1088  }
1089 
1090  if (!systemd_init()) {
1092  "No DBus connection");
1093  goto done;
1094  }
1095 
1096  crm_debug("Performing %ssynchronous %s op on systemd unit %s%s%s",
1097  (op->synchronous? "" : "a"), op->action, op->agent,
1098  ((op->rsc == NULL)? "" : " for resource "), pcmk__s(op->rsc, ""));
1099 
1100  if (pcmk__str_eq(op->action, PCMK_ACTION_META_DATA, pcmk__str_casei)) {
1101  op->stdout_data = systemd_unit_metadata(op->agent, op->timeout);
1103  goto done;
1104  }
1105 
1106  /* invoke_unit_by_name() should always override these values, which are here
1107  * just as a fail-safe in case there are any code paths that neglect to
1108  */
1110  "Bug in service library");
1111 
1112  if (invoke_unit_by_name(op->agent, op, NULL) == pcmk_rc_ok) {
1113  op->opaque->timerid = g_timeout_add(op->timeout + 5000,
1114  systemd_timeout_callback, op);
1116  return pcmk_rc_ok;
1117  }
1118 
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 }
Services API.
int rc
Exit status of action (set by library upon completion)
Definition: services.h:155
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
int services__execute_systemd(svc_action_t *op)
Definition: systemd.c:1080
A dumping ground.
ocf_exitcode
Exit status codes for resource agents.
Definition: results.h:177
char * pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
Definition: xml.c:1110
const char * name
Definition: cib.c:26
char * id
Definition: services.h:126
void pcmk_dbus_disconnect(DBusConnection *connection)
Definition: dbus.c:295
bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
Definition: xml.c:1028
#define DBUS_TIMEOUT_USE_DEFAULT
Definition: pcmk-dbus.h:16
#define PCMK_ACTION_META_DATA
Definition: actions.h:56
#define PCMK_ACTION_MONITOR
Definition: actions.h:60
Service safely stopped.
Definition: results.h:190
#define METADATA_FORMAT
Definition: systemd.c:668
char * rsc
XML ID of resource being executed for resource actions, otherwise NULL.
Definition: services.h:129
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:228
int timeout
Action timeout (in milliseconds)
Definition: services.h:146
Action did not complete in time.
Definition: results.h:335
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:501
Wrappers for and extensions to glib mainloop.
#define PCMK_ACTION_STATUS
Definition: actions.h:73
#define crm_warn(fmt, args...)
Definition: logging.h:394
void services__format_result(svc_action_t *action, int agent_status, enum pcmk_exec_status exec_status, const char *format,...) G_GNUC_PRINTF(4
svc_action_private_t * opaque
This field should be treated as internal to Pacemaker.
Definition: services.h:182
#define crm_debug(fmt, args...)
Definition: logging.h:402
#define BUS_NAME_UNIT
Definition: systemd.c:28
char * stdout_data
Action stdout (set by library)
Definition: services.h:178
Parameter invalid (inherently)
Definition: results.h:187
gboolean systemd_unit_exists(const char *name)
Definition: systemd.c:639
int pcmk__build_path(const char *path_c, mode_t mode)
Definition: io.c:45
#define crm_trace(fmt, args...)
Definition: logging.h:404
#define BUS_NAME
Definition: systemd.c:26
int services__finalize_async_op(svc_action_t *op)
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
Object for executing external actions.
Definition: services.h:122
#define PCMK_ACTION_START
Definition: actions.h:72
char * agent
Resource agent name for resource actions, otherwise NULL.
Definition: services.h:144
void services__set_result(svc_action_t *action, int agent_status, enum pcmk_exec_status exec_status, const char *exit_reason)
Definition: services.c:1269
Wrappers for and extensions to libxml2.
int synchronous
Definition: services.h:173
Action completed, result is known.
Definition: results.h:333
#define PCMK_ACTION_STOP
Definition: actions.h:75
#define ECOMM
Definition: portability.h:86
#define BUS_PATH
Definition: systemd.c:29
Execution failed, do not retry anywhere.
Definition: results.h:339
Dependencies not available locally.
Definition: results.h:186
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1050
#define SYSTEMD_OVERRIDE_ROOT
Definition: systemd.c:800
GList * systemd_unit_listall(void)
Definition: systemd.c:542
Unspecified error.
Definition: results.h:181
DBusPendingCall * pcmk_dbus_send(DBusMessage *msg, DBusConnection *connection, void(*done)(DBusPendingCall *pending, void *user_data), void *user_data, int timeout)
Definition: dbus.c:476
DBusConnection * pcmk_dbus_connect(void)
Definition: dbus.c:259
void services_add_inflight_op(svc_action_t *op)
Definition: services.c:833
Requested action not implemented.
Definition: results.h:184
int services__systemd_prepare(svc_action_t *op)
Definition: systemd.c:40
void systemd_cleanup(void)
Definition: systemd.c:172
char * action
Name of action being executed for resource actions, otherwise NULL.
Definition: services.h:132
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:331
const char * path
Definition: cib.c:28
#define crm_err(fmt, args...)
Definition: logging.h:391
enum ocf_exitcode services__systemd2ocf(int exit_status)
Definition: systemd.c:58
#define CRM_ASSERT(expr)
Definition: results.h:42
Success.
Definition: results.h:178
Action is pending.
Definition: results.h:203
bool pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected, const char *function, int line)
Definition: dbus.c:516
Agent or dependency not available locally.
Definition: results.h:340
Action is in progress.
Definition: results.h:332
bool pcmk_dbus_find_error(const DBusPendingCall *pending, DBusMessage *reply, DBusError *ret)
Definition: dbus.c:330
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
#define SYSTEMD_OVERRIDE_TEMPLATE
Definition: systemd.c:811
unsigned int timeout
Definition: pcmk_fence.c:32
DBusMessage * pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection, DBusError *error, int timeout)
Definition: dbus.c:412
Execution failed, may be retried.
Definition: results.h:337
#define crm_info(fmt, args...)
Definition: logging.h:399
#define BUS_NAME_MANAGER
Definition: systemd.c:27