pacemaker  2.1.6-802a72226b
Scalable High-Availability cluster resource manager
systemd.c
Go to the documentation of this file.
1 /*
2  * Copyright 2012-2022 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, "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, op == NULL || pcmk__str_eq(op->action, "meta-data", pcmk__str_none));
463  CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
464  DBUS_TYPE_INVALID));
465  free(name);
466 
467  if ((op == NULL) || op->synchronous) {
468  // For synchronous ops, wait for a reply and extract the result
469  const char *unit = NULL;
470  int rc = pcmk_rc_ok;
471 
472  reply = systemd_send_recv(msg, NULL,
473  (op? op->timeout : DBUS_TIMEOUT_USE_DEFAULT));
474  dbus_message_unref(msg);
475 
476  unit = execute_after_loadunit(reply, op);
477  if (unit == NULL) {
478  rc = ENOENT;
479  if (path != NULL) {
480  *path = NULL;
481  }
482  } else if (path != NULL) {
483  *path = strdup(unit);
484  if (*path == NULL) {
485  rc = ENOMEM;
486  }
487  }
488 
489  if (reply != NULL) {
490  dbus_message_unref(reply);
491  }
492  return rc;
493  }
494 
495  // For asynchronous ops, initiate the LoadUnit call and return
496  pending = systemd_send(msg, loadunit_completed, op, op->timeout);
497  if (pending == NULL) {
499  "Unable to send DBus message");
500  dbus_message_unref(msg);
501  return ECOMM;
502  }
503 
504  // LoadUnit was successfully initiated
506  services_set_op_pending(op, pending);
507  dbus_message_unref(msg);
508  return pcmk_rc_ok;
509 }
510 
523 static gint
524 sort_str(gconstpointer a, gconstpointer b)
525 {
526  if (!a && !b) {
527  return 0;
528  } else if (!a) {
529  return -1;
530  } else if (!b) {
531  return 1;
532  }
533  return strcasecmp(a, b);
534 }
535 
536 GList *
538 {
539  int nfiles = 0;
540  GList *units = NULL;
541  DBusMessageIter args;
542  DBusMessageIter unit;
543  DBusMessageIter elem;
544  DBusMessage *reply = NULL;
545 
546  if (systemd_init() == FALSE) {
547  return NULL;
548  }
549 
550 /*
551  " <method name=\"ListUnitFiles\">\n" \
552  " <arg name=\"files\" type=\"a(ss)\" direction=\"out\"/>\n" \
553  " </method>\n" \
554 */
555 
556  reply = systemd_call_simple_method("ListUnitFiles");
557  if (reply == NULL) {
558  return NULL;
559  }
560  if (!dbus_message_iter_init(reply, &args)) {
561  crm_err("Could not list systemd unit files: systemd reply has no arguments");
562  dbus_message_unref(reply);
563  return NULL;
564  }
565  if (!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY,
566  __func__, __LINE__)) {
567  crm_err("Could not list systemd unit files: systemd reply has invalid arguments");
568  dbus_message_unref(reply);
569  return NULL;
570  }
571 
572  dbus_message_iter_recurse(&args, &unit);
573  for (; dbus_message_iter_get_arg_type(&unit) != DBUS_TYPE_INVALID;
574  dbus_message_iter_next(&unit)) {
575 
576  DBusBasicValue value;
577  const char *match = NULL;
578  char *unit_name = NULL;
579  char *basename = NULL;
580 
581  if(!pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_STRUCT, __func__, __LINE__)) {
582  crm_warn("Skipping systemd reply argument with unexpected type");
583  continue;
584  }
585 
586  dbus_message_iter_recurse(&unit, &elem);
587  if(!pcmk_dbus_type_check(reply, &elem, DBUS_TYPE_STRING, __func__, __LINE__)) {
588  crm_warn("Skipping systemd reply argument with no string");
589  continue;
590  }
591 
592  dbus_message_iter_get_basic(&elem, &value);
593  if (value.str == NULL) {
594  crm_debug("ListUnitFiles reply did not provide a string");
595  continue;
596  }
597  crm_trace("DBus ListUnitFiles listed: %s", value.str);
598 
599  match = systemd_unit_extension(value.str);
600  if (match == NULL) {
601  // This is not a unit file type we know how to manage
602  crm_debug("ListUnitFiles entry '%s' is not supported as resource",
603  value.str);
604  continue;
605  }
606 
607  // ListUnitFiles returns full path names, we just want base name
608  basename = strrchr(value.str, '/');
609  if (basename) {
610  basename = basename + 1;
611  } else {
612  basename = value.str;
613  }
614 
615  if (!strcmp(match, ".service")) {
616  // Service is the "default" unit type, so strip it
617  unit_name = strndup(basename, match - basename);
618  } else {
619  unit_name = strdup(basename);
620  }
621 
622  nfiles++;
623  units = g_list_prepend(units, unit_name);
624  }
625 
626  dbus_message_unref(reply);
627 
628  crm_trace("Found %d manageable systemd unit files", nfiles);
629  units = g_list_sort(units, sort_str);
630  return units;
631 }
632 
633 gboolean
635 {
636  char *path = NULL;
637  char *state = NULL;
638 
639  /* Note: Makes a blocking dbus calls
640  * Used by resources_find_service_class() when resource class=service
641  */
642  if ((invoke_unit_by_name(name, NULL, &path) != pcmk_rc_ok)
643  || (path == NULL)) {
644  return FALSE;
645  }
646 
647  /* A successful LoadUnit is not sufficient to determine the unit's
648  * existence; it merely means the LoadUnit request received a reply.
649  * We must make another blocking call to check the LoadState property.
650  */
651  state = systemd_get_property(path, "LoadState", NULL, NULL, NULL,
653  free(path);
654  if (pcmk__str_any_of(state, "loaded", "masked", NULL)) {
655  free(state);
656  return TRUE;
657  }
658  free(state);
659  return FALSE;
660 }
661 
662 #define METADATA_FORMAT \
663  "<?xml version=\"1.0\"?>\n" \
664  "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n" \
665  "<resource-agent name=\"%s\" version=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n" \
666  " <version>1.1</version>\n" \
667  " <longdesc lang=\"en\">\n" \
668  " %s\n" \
669  " </longdesc>\n" \
670  " <shortdesc lang=\"en\">systemd unit file for %s</shortdesc>\n" \
671  " <parameters/>\n" \
672  " <actions>\n" \
673  " <action name=\"start\" timeout=\"100\" />\n" \
674  " <action name=\"stop\" timeout=\"100\" />\n" \
675  " <action name=\"status\" timeout=\"100\" />\n" \
676  " <action name=\"monitor\" timeout=\"100\" interval=\"60\"/>\n" \
677  " <action name=\"meta-data\" timeout=\"5\" />\n" \
678  " </actions>\n" \
679  " <special tag=\"systemd\"/>\n" \
680  "</resource-agent>\n"
681 
682 static char *
683 systemd_unit_metadata(const char *name, int timeout)
684 {
685  char *meta = NULL;
686  char *desc = NULL;
687  char *path = NULL;
688 
689  char *escaped = NULL;
690 
691  if (invoke_unit_by_name(name, NULL, &path) == pcmk_rc_ok) {
692  /* TODO: Worth a making blocking call for? Probably not. Possibly if cached. */
693  desc = systemd_get_property(path, "Description", NULL, NULL, NULL,
694  timeout);
695  } else {
696  desc = crm_strdup_printf("Systemd unit file for %s", name);
697  }
698 
699  escaped = crm_xml_escape(desc);
700 
701  meta = crm_strdup_printf(METADATA_FORMAT, name, escaped, name);
702  free(desc);
703  free(path);
704  free(escaped);
705  return meta;
706 }
707 
715 static void
716 process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
717 {
718  DBusError error;
719 
720  /* The first use of error here is not used other than as a non-NULL flag to
721  * indicate that a request was indeed sent
722  */
723  if (pcmk_dbus_find_error((void *) &error, reply, &error)) {
724  set_result_from_method_error(op, &error);
725  dbus_error_free(&error);
726 
727  } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
728  __func__, __LINE__)) {
729  crm_info("DBus request for %s of %s succeeded but "
730  "return type was unexpected",
731  op->action, pcmk__s(op->rsc, "unknown resource"));
733  "systemd DBus method had unexpected reply");
734 
735  } else {
736  const char *path = NULL;
737 
738  dbus_message_get_args(reply, NULL,
739  DBUS_TYPE_OBJECT_PATH, &path,
740  DBUS_TYPE_INVALID);
741  crm_debug("DBus request for %s of %s using %s succeeded",
742  op->action, pcmk__s(op->rsc, "unknown resource"), path);
744  }
745 }
746 
754 static void
755 unit_method_complete(DBusPendingCall *pending, void *user_data)
756 {
757  DBusMessage *reply = NULL;
758  svc_action_t *op = user_data;
759 
760  crm_trace("Result for %s arrived", op->id);
761 
762  // Grab the reply
763  if (pending != NULL) {
764  reply = dbus_pending_call_steal_reply(pending);
765  }
766 
767  // The call is no longer pending
768  CRM_LOG_ASSERT(pending == op->opaque->pending);
769  services_set_op_pending(op, NULL);
770 
771  // Determine result and finalize action
772  process_unit_method_reply(reply, op);
774  if (reply != NULL) {
775  dbus_message_unref(reply);
776  }
777 }
778 
779 #define SYSTEMD_OVERRIDE_ROOT "/run/systemd/system/"
780 
781 /* When the cluster manages a systemd resource, we create a unit file override
782  * to order the service "before" pacemaker. The "before" relationship won't
783  * actually be used, since systemd won't ever start the resource -- we're
784  * interested in the reverse shutdown ordering it creates, to ensure that
785  * systemd doesn't stop the resource at shutdown while pacemaker is still
786  * running.
787  *
788  * @TODO Add start timeout
789  */
790 #define SYSTEMD_OVERRIDE_TEMPLATE \
791  "[Unit]\n" \
792  "Description=Cluster Controlled %s\n" \
793  "Before=pacemaker.service pacemaker_remote.service\n" \
794  "\n" \
795  "[Service]\n" \
796  "Restart=no\n"
797 
798 // Temporarily use rwxr-xr-x umask when opening a file for writing
799 static FILE *
800 create_world_readable(const char *filename)
801 {
802  mode_t orig_umask = umask(S_IWGRP | S_IWOTH);
803  FILE *fp = fopen(filename, "w");
804 
805  umask(orig_umask);
806  return fp;
807 }
808 
809 static void
810 create_override_dir(const char *agent)
811 {
812  char *override_dir = crm_strdup_printf(SYSTEMD_OVERRIDE_ROOT
813  "/%s.service.d", agent);
814  int rc = pcmk__build_path(override_dir, 0755);
815 
816  if (rc != pcmk_rc_ok) {
817  crm_warn("Could not create systemd override directory %s: %s",
818  override_dir, pcmk_rc_str(rc));
819  }
820  free(override_dir);
821 }
822 
823 static char *
824 get_override_filename(const char *agent)
825 {
827  "/%s.service.d/50-pacemaker.conf", agent);
828 }
829 
830 static void
831 systemd_create_override(const char *agent, int timeout)
832 {
833  FILE *file_strm = NULL;
834  char *override_file = get_override_filename(agent);
835 
836  create_override_dir(agent);
837 
838  /* Ensure the override file is world-readable. This is not strictly
839  * necessary, but it avoids a systemd warning in the logs.
840  */
841  file_strm = create_world_readable(override_file);
842  if (file_strm == NULL) {
843  crm_err("Cannot open systemd override file %s for writing",
844  override_file);
845  } else {
846  char *override = crm_strdup_printf(SYSTEMD_OVERRIDE_TEMPLATE, agent);
847 
848  int rc = fprintf(file_strm, "%s\n", override);
849 
850  free(override);
851  if (rc < 0) {
852  crm_perror(LOG_WARNING, "Cannot write to systemd override file %s",
853  override_file);
854  }
855  fflush(file_strm);
856  fclose(file_strm);
857  systemd_daemon_reload(timeout);
858  }
859 
860  free(override_file);
861 }
862 
863 static void
864 systemd_remove_override(const char *agent, int timeout)
865 {
866  char *override_file = get_override_filename(agent);
867  int rc = unlink(override_file);
868 
869  if (rc < 0) {
870  // Stop may be called when already stopped, which is fine
871  crm_perror(LOG_DEBUG, "Cannot remove systemd override file %s",
872  override_file);
873  } else {
874  systemd_daemon_reload(timeout);
875  }
876  free(override_file);
877 }
878 
890 static void
891 parse_status_result(const char *name, const char *state, void *userdata)
892 {
893  svc_action_t *op = userdata;
894 
895  crm_trace("Resource %s has %s='%s'",
896  pcmk__s(op->rsc, "(unspecified)"), name,
897  pcmk__s(state, "<null>"));
898 
899  if (pcmk__str_eq(state, "active", pcmk__str_none)) {
901 
902  } else if (pcmk__str_eq(state, "reloading", pcmk__str_none)) {
904 
905  } else if (pcmk__str_eq(state, "activating", pcmk__str_none)) {
907 
908  } else if (pcmk__str_eq(state, "deactivating", pcmk__str_none)) {
910 
911  } else {
913  }
914 
915  if (!(op->synchronous)) {
916  services_set_op_pending(op, NULL);
918  }
919 }
920 
928 static void
929 invoke_unit_by_path(svc_action_t *op, const char *unit)
930 {
931  const char *method = NULL;
932  DBusMessage *msg = NULL;
933  DBusMessage *reply = NULL;
934 
935  if (pcmk__str_any_of(op->action, "monitor", "status", NULL)) {
936  DBusPendingCall *pending = NULL;
937  char *state;
938 
939  state = systemd_get_property(unit, "ActiveState",
940  (op->synchronous? NULL : parse_status_result),
941  op, (op->synchronous? NULL : &pending),
942  op->timeout);
943  if (op->synchronous) {
944  parse_status_result("ActiveState", state, op);
945  free(state);
946 
947  } else if (pending == NULL) { // Could not get ActiveState property
949  "Could not get state for unit %s from DBus",
950  op->agent);
952 
953  } else {
954  services_set_op_pending(op, pending);
955  }
956  return;
957 
958  } else if (pcmk__str_eq(op->action, "start", pcmk__str_none)) {
959  method = "StartUnit";
960  systemd_create_override(op->agent, op->timeout);
961 
962  } else if (pcmk__str_eq(op->action, "stop", pcmk__str_none)) {
963  method = "StopUnit";
964  systemd_remove_override(op->agent, op->timeout);
965 
966  } else if (pcmk__str_eq(op->action, "restart", pcmk__str_none)) {
967  method = "RestartUnit";
968 
969  } else {
972  "Action %s not implemented "
973  "for systemd resources",
974  pcmk__s(op->action, "(unspecified)"));
975  if (!(op->synchronous)) {
977  }
978  return;
979  }
980 
981  crm_trace("Calling %s for unit path %s%s%s",
982  method, unit,
983  ((op->rsc == NULL)? "" : " for resource "), pcmk__s(op->rsc, ""));
984 
985  msg = systemd_new_method(method);
986  CRM_ASSERT(msg != NULL);
987 
988  /* (ss) */
989  {
990  const char *replace_s = "replace";
991  char *name = systemd_service_name(op->agent, pcmk__str_eq(op->action, "meta-data", pcmk__str_none));
992 
993  CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID));
994  CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &replace_s, DBUS_TYPE_INVALID));
995 
996  free(name);
997  }
998 
999  if (op->synchronous) {
1000  reply = systemd_send_recv(msg, NULL, op->timeout);
1001  dbus_message_unref(msg);
1002  process_unit_method_reply(reply, op);
1003  if (reply != NULL) {
1004  dbus_message_unref(reply);
1005  }
1006 
1007  } else {
1008  DBusPendingCall *pending = systemd_send(msg, unit_method_complete, op,
1009  op->timeout);
1010 
1011  dbus_message_unref(msg);
1012  if (pending == NULL) {
1014  "Unable to send DBus message");
1016 
1017  } else {
1018  services_set_op_pending(op, pending);
1019  }
1020  }
1021 }
1022 
1023 static gboolean
1024 systemd_timeout_callback(gpointer p)
1025 {
1026  svc_action_t * op = p;
1027 
1028  op->opaque->timerid = 0;
1029  crm_info("%s action for systemd unit %s named '%s' timed out",
1030  op->action, op->agent, op->rsc);
1032  "%s action for systemd unit %s "
1033  "did not complete in time", op->action, op->agent);
1035  return FALSE;
1036 }
1037 
1054 int
1056 {
1057  CRM_ASSERT(op != NULL);
1058 
1059  if ((op->action == NULL) || (op->agent == NULL)) {
1061  "Bug in action caller");
1062  goto done;
1063  }
1064 
1065  if (!systemd_init()) {
1067  "No DBus connection");
1068  goto done;
1069  }
1070 
1071  crm_debug("Performing %ssynchronous %s op on systemd unit %s%s%s",
1072  (op->synchronous? "" : "a"), op->action, op->agent,
1073  ((op->rsc == NULL)? "" : " for resource "), pcmk__s(op->rsc, ""));
1074 
1075  if (pcmk__str_eq(op->action, "meta-data", pcmk__str_casei)) {
1076  op->stdout_data = systemd_unit_metadata(op->agent, op->timeout);
1078  goto done;
1079  }
1080 
1081  /* invoke_unit_by_name() should always override these values, which are here
1082  * just as a fail-safe in case there are any code paths that neglect to
1083  */
1085  "Bug in service library");
1086 
1087  if (invoke_unit_by_name(op->agent, op, NULL) == pcmk_rc_ok) {
1088  op->opaque->timerid = g_timeout_add(op->timeout + 5000,
1089  systemd_timeout_callback, op);
1091  return pcmk_rc_ok;
1092  }
1093 
1094 done:
1095  if (op->synchronous) {
1096  return (op->rc == PCMK_OCF_OK)? pcmk_rc_ok : pcmk_rc_error;
1097  } else {
1098  return services__finalize_async_op(op);
1099  }
1100 }
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:235
int services__execute_systemd(svc_action_t *op)
Definition: systemd.c:1055
A dumping ground.
ocf_exitcode
Exit status codes for resource agents.
Definition: results.h:166
const char * name
Definition: cib.c:24
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
Service safely stopped.
Definition: results.h:174
#define METADATA_FORMAT
Definition: systemd.c:662
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:219
int timeout
Action timeout (in milliseconds)
Definition: services.h:141
Action did not complete in time.
Definition: results.h:317
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:488
Wrappers for and extensions to glib mainloop.
char * strndup(const char *str, size_t len)
#define crm_warn(fmt, args...)
Definition: logging.h:378
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:382
#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:173
gboolean systemd_unit_exists(const char *name)
Definition: systemd.c:634
int pcmk__build_path(const char *path_c, mode_t mode)
Definition: io.c:45
#define crm_trace(fmt, args...)
Definition: logging.h:383
#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
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:315
#define ECOMM
Definition: portability.h:125
#define BUS_PATH
Definition: systemd.c:28
Execution failed, do not retry anywhere.
Definition: results.h:321
Dependencies not available locally.
Definition: results.h:172
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:957
#define SYSTEMD_OVERRIDE_ROOT
Definition: systemd.c:779
GList * systemd_unit_listall(void)
Definition: systemd.c:537
Unspecified error.
Definition: results.h:168
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:170
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:319
const char * path
Definition: cib.c:26
#define crm_err(fmt, args...)
Definition: logging.h:377
enum ocf_exitcode services__systemd2ocf(int exit_status)
Definition: systemd.c:57
#define CRM_ASSERT(expr)
Definition: results.h:42
Success.
Definition: results.h:167
Action is pending.
Definition: results.h:186
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:322
Action is in progress.
Definition: results.h:314
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:693
#define SYSTEMD_OVERRIDE_TEMPLATE
Definition: systemd.c:790
char * crm_xml_escape(const char *text)
Replace special characters with their XML escape sequences.
Definition: xml.c:1310
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:319
#define crm_info(fmt, args...)
Definition: logging.h:380
#define BUS_NAME_MANAGER
Definition: systemd.c:26