pacemaker  2.1.4-dc6eb4362
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, crm_str(op->rsc));
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 for resource %s failed: %s",
325  op->action, op->agent, crm_str(op->rsc), error->message);
326 }
327 
338 static const char *
339 execute_after_loadunit(DBusMessage *reply, svc_action_t *op)
340 {
341  const char *path = NULL;
342  DBusError error;
343 
344  /* path here is not used other than as a non-NULL flag to indicate that a
345  * request was indeed sent
346  */
347  if (pcmk_dbus_find_error((void *) &path, reply, &error)) {
348  if (op != NULL) {
349  set_result_from_method_error(op, &error);
350  }
351  dbus_error_free(&error);
352 
353  } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
354  __func__, __LINE__)) {
355  if (op != NULL) {
357  "systemd DBus method had unexpected reply");
358  crm_info("Could not load systemd unit %s for %s: "
359  "DBus reply has unexpected type", op->agent, op->id);
360  } else {
361  crm_info("Could not load systemd unit: "
362  "DBus reply has unexpected type");
363  }
364 
365  } else {
366  dbus_message_get_args (reply, NULL,
367  DBUS_TYPE_OBJECT_PATH, &path,
368  DBUS_TYPE_INVALID);
369  }
370 
371  if (op != NULL) {
372  if (path != NULL) {
373  invoke_unit_by_path(op, path);
374 
375  } else if (!(op->synchronous)) {
377  "No DBus object found for systemd unit %s",
378  op->agent);
380  }
381  }
382 
383  return path;
384 }
385 
393 static void
394 loadunit_completed(DBusPendingCall *pending, void *user_data)
395 {
396  DBusMessage *reply = NULL;
397  svc_action_t *op = user_data;
398 
399  crm_trace("LoadUnit result for %s arrived", op->id);
400 
401  // Grab the reply
402  if (pending != NULL) {
403  reply = dbus_pending_call_steal_reply(pending);
404  }
405 
406  // The call is no longer pending
407  CRM_LOG_ASSERT(pending == op->opaque->pending);
408  services_set_op_pending(op, NULL);
409 
410  // Execute the desired action based on the reply
411  execute_after_loadunit(reply, user_data);
412  if (reply != NULL) {
413  dbus_message_unref(reply);
414  }
415 }
416 
432 static int
433 invoke_unit_by_name(const char *arg_name, svc_action_t *op, char **path)
434 {
435  DBusMessage *msg;
436  DBusMessage *reply = NULL;
437  DBusPendingCall *pending = NULL;
438  char *name = NULL;
439 
440  if (!systemd_init()) {
441  if (op != NULL) {
443  "No DBus connection");
444  }
445  return ENOTCONN;
446  }
447 
448  /* Create a LoadUnit DBus method (equivalent to GetUnit if already loaded),
449  * which makes the unit usable via further DBus methods.
450  *
451  * <method name="LoadUnit">
452  * <arg name="name" type="s" direction="in"/>
453  * <arg name="unit" type="o" direction="out"/>
454  * </method>
455  */
456  msg = systemd_new_method("LoadUnit");
457  CRM_ASSERT(msg != NULL);
458 
459  // Add the (expanded) unit name as the argument
460  name = systemd_service_name(arg_name, op == NULL || pcmk__str_eq(op->action, "meta-data", pcmk__str_none));
461  CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
462  DBUS_TYPE_INVALID));
463  free(name);
464 
465  if ((op == NULL) || op->synchronous) {
466  // For synchronous ops, wait for a reply and extract the result
467  const char *unit = NULL;
468  int rc = pcmk_rc_ok;
469 
470  reply = systemd_send_recv(msg, NULL,
471  (op? op->timeout : DBUS_TIMEOUT_USE_DEFAULT));
472  dbus_message_unref(msg);
473 
474  unit = execute_after_loadunit(reply, op);
475  if (unit == NULL) {
476  rc = ENOENT;
477  if (path != NULL) {
478  *path = NULL;
479  }
480  } else if (path != NULL) {
481  *path = strdup(unit);
482  if (*path == NULL) {
483  rc = ENOMEM;
484  }
485  }
486 
487  if (reply != NULL) {
488  dbus_message_unref(reply);
489  }
490  return rc;
491  }
492 
493  // For asynchronous ops, initiate the LoadUnit call and return
494  pending = systemd_send(msg, loadunit_completed, op, op->timeout);
495  if (pending == NULL) {
497  "Unable to send DBus message");
498  dbus_message_unref(msg);
499  return ECOMM;
500  }
501 
502  // LoadUnit was successfully initiated
504  services_set_op_pending(op, pending);
505  dbus_message_unref(msg);
506  return pcmk_rc_ok;
507 }
508 
521 static gint
522 sort_str(gconstpointer a, gconstpointer b)
523 {
524  if (!a && !b) {
525  return 0;
526  } else if (!a) {
527  return -1;
528  } else if (!b) {
529  return 1;
530  }
531  return strcasecmp(a, b);
532 }
533 
534 GList *
536 {
537  int nfiles = 0;
538  GList *units = NULL;
539  DBusMessageIter args;
540  DBusMessageIter unit;
541  DBusMessageIter elem;
542  DBusMessage *reply = NULL;
543 
544  if (systemd_init() == FALSE) {
545  return NULL;
546  }
547 
548 /*
549  " <method name=\"ListUnitFiles\">\n" \
550  " <arg name=\"files\" type=\"a(ss)\" direction=\"out\"/>\n" \
551  " </method>\n" \
552 */
553 
554  reply = systemd_call_simple_method("ListUnitFiles");
555  if (reply == NULL) {
556  return NULL;
557  }
558  if (!dbus_message_iter_init(reply, &args)) {
559  crm_err("Could not list systemd unit files: systemd reply has no arguments");
560  dbus_message_unref(reply);
561  return NULL;
562  }
563  if (!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY,
564  __func__, __LINE__)) {
565  crm_err("Could not list systemd unit files: systemd reply has invalid arguments");
566  dbus_message_unref(reply);
567  return NULL;
568  }
569 
570  dbus_message_iter_recurse(&args, &unit);
571  for (; dbus_message_iter_get_arg_type(&unit) != DBUS_TYPE_INVALID;
572  dbus_message_iter_next(&unit)) {
573 
574  DBusBasicValue value;
575  const char *match = NULL;
576  char *unit_name = NULL;
577  char *basename = NULL;
578 
579  if(!pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_STRUCT, __func__, __LINE__)) {
580  crm_warn("Skipping systemd reply argument with unexpected type");
581  continue;
582  }
583 
584  dbus_message_iter_recurse(&unit, &elem);
585  if(!pcmk_dbus_type_check(reply, &elem, DBUS_TYPE_STRING, __func__, __LINE__)) {
586  crm_warn("Skipping systemd reply argument with no string");
587  continue;
588  }
589 
590  dbus_message_iter_get_basic(&elem, &value);
591  if (value.str == NULL) {
592  crm_debug("ListUnitFiles reply did not provide a string");
593  continue;
594  }
595  crm_trace("DBus ListUnitFiles listed: %s", value.str);
596 
597  match = systemd_unit_extension(value.str);
598  if (match == NULL) {
599  // This is not a unit file type we know how to manage
600  crm_debug("ListUnitFiles entry '%s' is not supported as resource",
601  value.str);
602  continue;
603  }
604 
605  // ListUnitFiles returns full path names, we just want base name
606  basename = strrchr(value.str, '/');
607  if (basename) {
608  basename = basename + 1;
609  } else {
610  basename = value.str;
611  }
612 
613  if (!strcmp(match, ".service")) {
614  // Service is the "default" unit type, so strip it
615  unit_name = strndup(basename, match - basename);
616  } else {
617  unit_name = strdup(basename);
618  }
619 
620  nfiles++;
621  units = g_list_prepend(units, unit_name);
622  }
623 
624  dbus_message_unref(reply);
625 
626  crm_trace("Found %d manageable systemd unit files", nfiles);
627  units = g_list_sort(units, sort_str);
628  return units;
629 }
630 
631 gboolean
633 {
634  char *path = NULL;
635  char *state = NULL;
636 
637  /* Note: Makes a blocking dbus calls
638  * Used by resources_find_service_class() when resource class=service
639  */
640  if ((invoke_unit_by_name(name, NULL, &path) != pcmk_rc_ok)
641  || (path == NULL)) {
642  return FALSE;
643  }
644 
645  /* A successful LoadUnit is not sufficient to determine the unit's
646  * existence; it merely means the LoadUnit request received a reply.
647  * We must make another blocking call to check the LoadState property.
648  */
649  state = systemd_get_property(path, "LoadState", NULL, NULL, NULL,
651  free(path);
652  if (pcmk__str_any_of(state, "loaded", "masked", NULL)) {
653  free(state);
654  return TRUE;
655  }
656  free(state);
657  return FALSE;
658 }
659 
660 #define METADATA_FORMAT \
661  "<?xml version=\"1.0\"?>\n" \
662  "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n" \
663  "<resource-agent name=\"%s\" version=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n" \
664  " <version>1.1</version>\n" \
665  " <longdesc lang=\"en\">\n" \
666  " %s\n" \
667  " </longdesc>\n" \
668  " <shortdesc lang=\"en\">systemd unit file for %s</shortdesc>\n" \
669  " <parameters/>\n" \
670  " <actions>\n" \
671  " <action name=\"start\" timeout=\"100\" />\n" \
672  " <action name=\"stop\" timeout=\"100\" />\n" \
673  " <action name=\"status\" timeout=\"100\" />\n" \
674  " <action name=\"monitor\" timeout=\"100\" interval=\"60\"/>\n" \
675  " <action name=\"meta-data\" timeout=\"5\" />\n" \
676  " </actions>\n" \
677  " <special tag=\"systemd\"/>\n" \
678  "</resource-agent>\n"
679 
680 static char *
681 systemd_unit_metadata(const char *name, int timeout)
682 {
683  char *meta = NULL;
684  char *desc = NULL;
685  char *path = NULL;
686 
687  char *escaped = NULL;
688 
689  if (invoke_unit_by_name(name, NULL, &path) == pcmk_rc_ok) {
690  /* TODO: Worth a making blocking call for? Probably not. Possibly if cached. */
691  desc = systemd_get_property(path, "Description", NULL, NULL, NULL,
692  timeout);
693  } else {
694  desc = crm_strdup_printf("Systemd unit file for %s", name);
695  }
696 
697  escaped = crm_xml_escape(desc);
698 
699  meta = crm_strdup_printf(METADATA_FORMAT, name, escaped, name);
700  free(desc);
701  free(path);
702  free(escaped);
703  return meta;
704 }
705 
713 static void
714 process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
715 {
716  DBusError error;
717 
718  /* The first use of error here is not used other than as a non-NULL flag to
719  * indicate that a request was indeed sent
720  */
721  if (pcmk_dbus_find_error((void *) &error, reply, &error)) {
722  set_result_from_method_error(op, &error);
723  dbus_error_free(&error);
724 
725  } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
726  __func__, __LINE__)) {
727  crm_info("DBus request for %s of %s succeeded but "
728  "return type was unexpected", op->action, crm_str(op->rsc));
730  "systemd DBus method had unexpected reply");
731 
732  } else {
733  const char *path = NULL;
734 
735  dbus_message_get_args(reply, NULL,
736  DBUS_TYPE_OBJECT_PATH, &path,
737  DBUS_TYPE_INVALID);
738  crm_debug("DBus request for %s of %s using %s succeeded",
739  op->action, crm_str(op->rsc), path);
741  }
742 }
743 
751 static void
752 unit_method_complete(DBusPendingCall *pending, void *user_data)
753 {
754  DBusMessage *reply = NULL;
755  svc_action_t *op = user_data;
756 
757  crm_trace("Result for %s arrived", op->id);
758 
759  // Grab the reply
760  if (pending != NULL) {
761  reply = dbus_pending_call_steal_reply(pending);
762  }
763 
764  // The call is no longer pending
765  CRM_LOG_ASSERT(pending == op->opaque->pending);
766  services_set_op_pending(op, NULL);
767 
768  // Determine result and finalize action
769  process_unit_method_reply(reply, op);
771  if (reply != NULL) {
772  dbus_message_unref(reply);
773  }
774 }
775 
776 #define SYSTEMD_OVERRIDE_ROOT "/run/systemd/system/"
777 
778 /* When the cluster manages a systemd resource, we create a unit file override
779  * to order the service "before" pacemaker. The "before" relationship won't
780  * actually be used, since systemd won't ever start the resource -- we're
781  * interested in the reverse shutdown ordering it creates, to ensure that
782  * systemd doesn't stop the resource at shutdown while pacemaker is still
783  * running.
784  *
785  * @TODO Add start timeout
786  */
787 #define SYSTEMD_OVERRIDE_TEMPLATE \
788  "[Unit]\n" \
789  "Description=Cluster Controlled %s\n" \
790  "Before=pacemaker.service pacemaker_remote.service\n" \
791  "\n" \
792  "[Service]\n" \
793  "Restart=no\n"
794 
795 // Temporarily use rwxr-xr-x umask when opening a file for writing
796 static FILE *
797 create_world_readable(const char *filename)
798 {
799  mode_t orig_umask = umask(S_IWGRP | S_IWOTH);
800  FILE *fp = fopen(filename, "w");
801 
802  umask(orig_umask);
803  return fp;
804 }
805 
806 static void
807 create_override_dir(const char *agent)
808 {
809  char *override_dir = crm_strdup_printf(SYSTEMD_OVERRIDE_ROOT
810  "/%s.service.d", agent);
811  int rc = pcmk__build_path(override_dir, 0755);
812 
813  if (rc != pcmk_rc_ok) {
814  crm_warn("Could not create systemd override directory %s: %s",
815  override_dir, pcmk_rc_str(rc));
816  }
817  free(override_dir);
818 }
819 
820 static char *
821 get_override_filename(const char *agent)
822 {
824  "/%s.service.d/50-pacemaker.conf", agent);
825 }
826 
827 static void
828 systemd_create_override(const char *agent, int timeout)
829 {
830  FILE *file_strm = NULL;
831  char *override_file = get_override_filename(agent);
832 
833  create_override_dir(agent);
834 
835  /* Ensure the override file is world-readable. This is not strictly
836  * necessary, but it avoids a systemd warning in the logs.
837  */
838  file_strm = create_world_readable(override_file);
839  if (file_strm == NULL) {
840  crm_err("Cannot open systemd override file %s for writing",
841  override_file);
842  } else {
843  char *override = crm_strdup_printf(SYSTEMD_OVERRIDE_TEMPLATE, agent);
844 
845  int rc = fprintf(file_strm, "%s\n", override);
846 
847  free(override);
848  if (rc < 0) {
849  crm_perror(LOG_WARNING, "Cannot write to systemd override file %s",
850  override_file);
851  }
852  fflush(file_strm);
853  fclose(file_strm);
854  systemd_daemon_reload(timeout);
855  }
856 
857  free(override_file);
858 }
859 
860 static void
861 systemd_remove_override(const char *agent, int timeout)
862 {
863  char *override_file = get_override_filename(agent);
864  int rc = unlink(override_file);
865 
866  if (rc < 0) {
867  // Stop may be called when already stopped, which is fine
868  crm_perror(LOG_DEBUG, "Cannot remove systemd override file %s",
869  override_file);
870  } else {
871  systemd_daemon_reload(timeout);
872  }
873  free(override_file);
874 }
875 
887 static void
888 parse_status_result(const char *name, const char *state, void *userdata)
889 {
890  svc_action_t *op = userdata;
891 
892  crm_trace("Resource %s has %s='%s'",
893  crm_str(op->rsc), name, crm_str(state));
894 
895  if (pcmk__str_eq(state, "active", pcmk__str_none)) {
897 
898  } else if (pcmk__str_eq(state, "reloading", pcmk__str_none)) {
900 
901  } else if (pcmk__str_eq(state, "activating", pcmk__str_none)) {
903 
904  } else if (pcmk__str_eq(state, "deactivating", pcmk__str_none)) {
906 
907  } else {
909  }
910 
911  if (!(op->synchronous)) {
912  services_set_op_pending(op, NULL);
914  }
915 }
916 
924 static void
925 invoke_unit_by_path(svc_action_t *op, const char *unit)
926 {
927  const char *method = NULL;
928  DBusMessage *msg = NULL;
929  DBusMessage *reply = NULL;
930 
931  if (pcmk__str_any_of(op->action, "monitor", "status", NULL)) {
932  DBusPendingCall *pending = NULL;
933  char *state;
934 
935  state = systemd_get_property(unit, "ActiveState",
936  (op->synchronous? NULL : parse_status_result),
937  op, (op->synchronous? NULL : &pending),
938  op->timeout);
939  if (op->synchronous) {
940  parse_status_result("ActiveState", state, op);
941  free(state);
942 
943  } else if (pending == NULL) { // Could not get ActiveState property
945  "Could not get state for unit %s from DBus",
946  op->agent);
948 
949  } else {
950  services_set_op_pending(op, pending);
951  }
952  return;
953 
954  } else if (pcmk__str_eq(op->action, "start", pcmk__str_none)) {
955  method = "StartUnit";
956  systemd_create_override(op->agent, op->timeout);
957 
958  } else if (pcmk__str_eq(op->action, "stop", pcmk__str_none)) {
959  method = "StopUnit";
960  systemd_remove_override(op->agent, op->timeout);
961 
962  } else if (pcmk__str_eq(op->action, "restart", pcmk__str_none)) {
963  method = "RestartUnit";
964 
965  } else {
968  "Action %s not implemented "
969  "for systemd resources", crm_str(op->action));
970  if (!(op->synchronous)) {
972  }
973  return;
974  }
975 
976  crm_trace("Calling %s for unit path %s named %s",
977  method, unit, crm_str(op->rsc));
978 
979  msg = systemd_new_method(method);
980  CRM_ASSERT(msg != NULL);
981 
982  /* (ss) */
983  {
984  const char *replace_s = "replace";
985  char *name = systemd_service_name(op->agent, pcmk__str_eq(op->action, "meta-data", pcmk__str_none));
986 
987  CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID));
988  CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &replace_s, DBUS_TYPE_INVALID));
989 
990  free(name);
991  }
992 
993  if (op->synchronous) {
994  reply = systemd_send_recv(msg, NULL, op->timeout);
995  dbus_message_unref(msg);
996  process_unit_method_reply(reply, op);
997  if (reply != NULL) {
998  dbus_message_unref(reply);
999  }
1000 
1001  } else {
1002  DBusPendingCall *pending = systemd_send(msg, unit_method_complete, op,
1003  op->timeout);
1004 
1005  dbus_message_unref(msg);
1006  if (pending == NULL) {
1008  "Unable to send DBus message");
1010 
1011  } else {
1012  services_set_op_pending(op, pending);
1013  }
1014  }
1015 }
1016 
1017 static gboolean
1018 systemd_timeout_callback(gpointer p)
1019 {
1020  svc_action_t * op = p;
1021 
1022  op->opaque->timerid = 0;
1023  crm_info("%s action for systemd unit %s named '%s' timed out",
1024  op->action, op->agent, op->rsc);
1026  "%s action for systemd unit %s "
1027  "did not complete in time", op->action, op->agent);
1029  return FALSE;
1030 }
1031 
1048 int
1050 {
1051  CRM_ASSERT(op != NULL);
1052 
1053  if ((op->action == NULL) || (op->agent == NULL)) {
1055  "Bug in action caller");
1056  goto done;
1057  }
1058 
1059  if (!systemd_init()) {
1061  "No DBus connection");
1062  goto done;
1063  }
1064 
1065  crm_debug("Performing %ssynchronous %s op on systemd unit %s named '%s'",
1066  (op->synchronous? "" : "a"), op->action, op->agent,
1067  crm_str(op->rsc));
1068 
1069  if (pcmk__str_eq(op->action, "meta-data", pcmk__str_casei)) {
1070  op->stdout_data = systemd_unit_metadata(op->agent, op->timeout);
1072  goto done;
1073  }
1074 
1075  /* invoke_unit_by_name() should always override these values, which are here
1076  * just as a fail-safe in case there are any code paths that neglect to
1077  */
1079  "Bug in service library");
1080 
1081  if (invoke_unit_by_name(op->agent, op, NULL) == pcmk_rc_ok) {
1082  op->opaque->timerid = g_timeout_add(op->timeout + 5000,
1083  systemd_timeout_callback, op);
1085  return pcmk_rc_ok;
1086  }
1087 
1088 done:
1089  if (op->synchronous) {
1090  return (op->rc == PCMK_OCF_OK)? pcmk_rc_ok : pcmk_rc_error;
1091  } else {
1092  return services__finalize_async_op(op);
1093  }
1094 }
Services API.
int rc
Exit status of action (set by library upon completion)
Definition: services.h:154
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
int services__execute_systemd(svc_action_t *op)
Definition: systemd.c:1049
A dumping ground.
ocf_exitcode
Exit status codes for resource agents.
Definition: results.h:161
const char * name
Definition: cib.c:24
char * id
Definition: services.h:125
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:169
#define METADATA_FORMAT
Definition: systemd.c:660
char * rsc
XML ID of resource being executed for resource actions, otherwise NULL.
Definition: services.h:128
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:210
int timeout
Action timeout (in milliseconds)
Definition: services.h:145
Action did not complete in time.
Definition: results.h:310
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:370
Wrappers for and extensions to glib mainloop.
bool pcmk_dbus_find_error(DBusPendingCall *pending, DBusMessage *reply, DBusError *ret)
Definition: dbus.c:330
char * strndup(const char *str, size_t len)
#define crm_warn(fmt, args...)
Definition: logging.h:359
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:181
#define crm_debug(fmt, args...)
Definition: logging.h:363
#define BUS_NAME_UNIT
Definition: systemd.c:27
char * stdout_data
Action stdout (set by library)
Definition: services.h:177
Parameter invalid (inherently)
Definition: results.h:168
gboolean systemd_unit_exists(const char *name)
Definition: systemd.c:632
#define crm_trace(fmt, args...)
Definition: logging.h:364
#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:121
char * agent
Resource agent name for resource actions, otherwise NULL.
Definition: services.h:143
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:172
Action completed, result is known.
Definition: results.h:308
#define ECOMM
Definition: portability.h:125
#define BUS_PATH
Definition: systemd.c:28
Execution failed, do not retry anywhere.
Definition: results.h:314
Dependencies not available locally.
Definition: results.h:167
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:955
#define SYSTEMD_OVERRIDE_ROOT
Definition: systemd.c:776
GList * systemd_unit_listall(void)
Definition: systemd.c:535
Unspecified error.
Definition: results.h:163
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:165
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:131
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:309
const char * path
Definition: cib.c:26
#define crm_err(fmt, args...)
Definition: logging.h:358
enum ocf_exitcode services__systemd2ocf(int exit_status)
Definition: systemd.c:57
#define CRM_ASSERT(expr)
Definition: results.h:42
Success.
Definition: results.h:162
Action is pending.
Definition: results.h:188
bool pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected, const char *function, int line)
Definition: dbus.c:516
#define crm_str(x)
Definition: logging.h:384
Agent or dependency not available locally.
Definition: results.h:315
Action is in progress.
Definition: results.h:307
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:787
char * crm_xml_escape(const char *text)
Replace special characters with their XML escape sequences.
Definition: xml.c:1345
unsigned int timeout
Definition: pcmk_fence.c:31
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:312
#define crm_info(fmt, args...)
Definition: logging.h:361
#define BUS_NAME_MANAGER
Definition: systemd.c:26
int pcmk__build_path(const char *path_c, mode_t mode)
Definition: io.c:45