pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
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>
13 #include <crm/services_internal.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 
23 static 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 
38 int
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 
56 enum ocf_exitcode
57 services__systemd2ocf(int exit_status)
58 {
59  // This library uses OCF codes for systemd actions
60  return (enum ocf_exitcode) exit_status;
61 }
62 
63 static inline DBusMessage *
64 systemd_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 
75 static DBusConnection* systemd_proxy = NULL;
76 
77 static inline DBusPendingCall *
78 systemd_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 
85 static inline DBusMessage *
86 systemd_send_recv(DBusMessage *msg, DBusError *error, int timeout)
87 {
88  return pcmk_dbus_send_recv(msg, systemd_proxy, error, timeout);
89 }
90 
102 static DBusMessage *
103 systemd_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 
135 static gboolean
136 systemd_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 
159 static inline char *
160 systemd_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 
170 void
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 
191 static const char *
192 systemd_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 
208 static char *
209 systemd_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 
250 static void
251 systemd_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 
279 static bool
280 systemd_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 
301 static void
302 set_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 
340 static const char *
341 execute_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 
395 static void
396 loadunit_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 
434 static int
435 invoke_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,
466  pcmk__str_none));
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,
477  (op? op->timeout : DBUS_TIMEOUT_USE_DEFAULT));
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 
527 static gint
528 sort_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 
540 GList *
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 
637 gboolean
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 
686 static char *
687 systemd_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 
719 static void
720 process_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 
760 static void
761 unit_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
805 static FILE *
806 create_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 
815 static void
816 create_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 
829 static char *
830 get_override_filename(const char *agent)
831 {
833  "/%s.service.d/50-pacemaker.conf", agent);
834 }
835 
836 static void
837 systemd_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 
869 static void
870 systemd_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 
896 static void
897 parse_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 
934 static void
935 invoke_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,
1001  pcmk__str_none));
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 
1033 static gboolean
1034 systemd_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 
1064 int
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 
1104 done:
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 }
Services API.
int rc
Exit status of action (set by library upon completion)
Definition: services.h:150
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
int services__execute_systemd(svc_action_t *op)
Definition: systemd.c:1065
A dumping ground.
ocf_exitcode
Exit status codes for resource agents.
Definition: results.h:169
const char * name
Definition: cib.c:26
char * id
Definition: services.h:121
void pcmk_dbus_disconnect(DBusConnection *connection)
Definition: dbus.c:295
#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:59
Service safely stopped.
Definition: results.h:177
#define METADATA_FORMAT
Definition: systemd.c:666
char * rsc
XML ID of resource being executed for resource actions, otherwise NULL.
Definition: services.h:124
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:222
int timeout
Action timeout (in milliseconds)
Definition: services.h:141
Action did not complete in time.
Definition: results.h:320
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:72
#define crm_warn(fmt, args...)
Definition: logging.h:382
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:177
#define crm_debug(fmt, args...)
Definition: logging.h:386
#define BUS_NAME_UNIT
Definition: systemd.c:27
char * stdout_data
Action stdout (set by library)
Definition: services.h:173
Parameter invalid (inherently)
Definition: results.h:176
gboolean systemd_unit_exists(const char *name)
Definition: systemd.c:638
int pcmk__build_path(const char *path_c, mode_t mode)
Definition: io.c:45
#define crm_trace(fmt, args...)
Definition: logging.h:387
#define BUS_NAME
Definition: systemd.c:25
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:117
#define PCMK_ACTION_START
Definition: actions.h:71
char * agent
Resource agent name for resource actions, otherwise NULL.
Definition: services.h:139
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 synchronous
Definition: services.h:168
Action completed, result is known.
Definition: results.h:318
#define PCMK_ACTION_STOP
Definition: actions.h:74
#define ECOMM
Definition: portability.h:86
#define BUS_PATH
Definition: systemd.c:28
Execution failed, do not retry anywhere.
Definition: results.h:324
Dependencies not available locally.
Definition: results.h:175
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:957
#define SYSTEMD_OVERRIDE_ROOT
Definition: systemd.c:785
GList * systemd_unit_listall(void)
Definition: systemd.c:541
Unspecified error.
Definition: results.h:171
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:835
Requested action not implemented.
Definition: results.h:173
int services__systemd_prepare(svc_action_t *op)
Definition: systemd.c:39
void systemd_cleanup(void)
Definition: systemd.c:171
char * action
Name of action being executed for resource actions, otherwise NULL.
Definition: services.h:127
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:323
const char * path
Definition: cib.c:28
#define crm_err(fmt, args...)
Definition: logging.h:381
enum ocf_exitcode services__systemd2ocf(int exit_status)
Definition: systemd.c:57
#define CRM_ASSERT(expr)
Definition: results.h:42
Success.
Definition: results.h:170
Action is pending.
Definition: results.h:189
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:325
Action is in progress.
Definition: results.h:317
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:796
char * crm_xml_escape(const char *text)
Replace special characters with their XML escape sequences.
Definition: xml.c:1329
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:322
#define crm_info(fmt, args...)
Definition: logging.h:384
#define BUS_NAME_MANAGER
Definition: systemd.c:26