28static void invoke_unit_by_path(
svc_action_t *op,
const char *unit);
33#define BUS_NAME "org.freedesktop.systemd1"
34#define BUS_NAME_MANAGER BUS_NAME ".Manager"
35#define BUS_NAME_UNIT BUS_NAME ".Unit"
36#define BUS_PATH "/org/freedesktop/systemd1"
71static inline DBusMessage *
72systemd_new_method(
const char *method)
83static DBusConnection* systemd_proxy = NULL;
85static inline DBusPendingCall *
86systemd_send(DBusMessage *msg,
87 void(*done)(DBusPendingCall *pending,
void *user_data),
93static inline DBusMessage *
94systemd_send_recv(DBusMessage *msg, DBusError *error,
int timeout)
111systemd_call_simple_method(
const char *method)
113 DBusMessage *msg = NULL;
114 DBusMessage *reply = NULL;
120 msg = systemd_new_method(method);
123 crm_err(
"Could not create message to send %s to systemd", method);
127 dbus_error_init(&error);
129 dbus_message_unref(msg);
131 if (dbus_error_is_set(&error)) {
132 crm_err(
"Could not send %s to systemd: %s (%s)",
133 method, error.message, error.name);
134 dbus_error_free(&error);
137 }
else if (reply == NULL) {
138 crm_err(
"Could not send %s to systemd: no reply received", method);
156subscribe_to_signals(
void)
158 const char *match_rule =
"type='signal',"
162 DBusMessage *reply = NULL;
168 dbus_error_init(&error);
169 dbus_bus_add_match(systemd_proxy, match_rule, &error);
171 if (dbus_error_is_set(&error)) {
172 crm_err(
"Could not listen for systemd DBus signals: %s " QB_XS
" (%s)",
173 error.message, error.name);
174 dbus_error_free(&error);
179 reply = systemd_call_simple_method(
"Subscribe");
181 dbus_bus_remove_match(systemd_proxy, match_rule, &error);
185 dbus_message_unref(reply);
192 static int need_init = 1;
196 && dbus_connection_get_is_connected(systemd_proxy) == FALSE) {
197 crm_warn(
"Connection to System DBus is closed. Reconnecting...");
199 systemd_proxy = NULL;
209 systemd_proxy = NULL;
213 return (systemd_proxy != NULL);
217systemd_get_property(
const char *unit,
const char *
name,
218 void (*callback)(
const char *
name,
const char *value,
void *userdata),
219 void *userdata, DBusPendingCall **pending,
int timeout)
221 return systemd_proxy?
232 systemd_proxy = NULL;
249systemd_unit_extension(
const char *
name)
252 const char *dot = strrchr(
name,
'.');
254 if (dot && (!strcmp(dot,
".service")
255 || !strcmp(dot,
".socket")
256 || !strcmp(dot,
".mount")
257 || !strcmp(dot,
".timer")
258 || !strcmp(dot,
".path"))) {
266systemd_unit_name(
const char *
name,
bool add_instance_name)
268 const char *dot = NULL;
270 if (pcmk__str_empty(
name)) {
283 dot = systemd_unit_extension(
name);
286 if (dot !=
name && *(dot-1) ==
'@') {
293 }
else if (add_instance_name && *(
name+strlen(
name)-1) ==
'@') {
302systemd_daemon_reload_complete(DBusPendingCall *pending,
void *user_data)
305 DBusMessage *reply = NULL;
306 unsigned int reload_count = GPOINTER_TO_UINT(user_data);
308 dbus_error_init(&error);
310 reply = dbus_pending_call_steal_reply(pending);
314 crm_warn(
"Could not issue systemd reload %d: %s",
315 reload_count, error.message);
316 dbus_error_free(&error);
319 crm_trace(
"Reload %d complete", reload_count);
323 dbus_pending_call_unref(pending);
326 dbus_message_unref(reply);
331systemd_daemon_reload(
int timeout)
333 static unsigned int reload_count = 0;
334 DBusMessage *msg = systemd_new_method(
"Reload");
338 systemd_send(msg, systemd_daemon_reload_complete,
339 GUINT_TO_POINTER(reload_count),
timeout);
340 dbus_message_unref(msg);
353set_result_from_method_error(
svc_action_t *op,
const DBusError *error)
356 "Unable to invoke systemd DBus method");
358 if (strstr(error->name,
"org.freedesktop.systemd1.InvalidName")
359 || strstr(error->name,
"org.freedesktop.systemd1.LoadFailed")
360 || strstr(error->name,
"org.freedesktop.systemd1.NoSuchUnit")) {
363 crm_trace(
"Masking systemd stop failure (%s) for %s "
364 "because unknown service can be considered stopped",
365 error->name, pcmk__s(op->
rsc,
"unknown resource"));
372 "systemd unit %s not found", op->
agent);
383 && strstr(error->name, DBUS_ERROR_NO_REPLY)
384 && strstr(error->message,
"disconnected")) {
388 crm_info(
"DBus request for %s of systemd unit %s%s%s failed: %s",
390 ((op->
rsc == NULL)?
"" :
" for resource "), pcmk__s(op->
rsc,
""),
405execute_after_loadunit(DBusMessage *reply,
svc_action_t *op)
407 const char *
path = NULL;
415 set_result_from_method_error(op, &error);
417 dbus_error_free(&error);
420 __func__, __LINE__)) {
423 "systemd DBus method had unexpected reply");
424 crm_info(
"Could not load systemd unit %s for %s: "
425 "DBus reply has unexpected type", op->
agent, op->
id);
427 crm_info(
"Could not load systemd unit: "
428 "DBus reply has unexpected type");
432 dbus_message_get_args (reply, NULL,
433 DBUS_TYPE_OBJECT_PATH, &
path,
439 invoke_unit_by_path(op,
path);
446 "No DBus object found for systemd unit %s",
464loadunit_completed(DBusPendingCall *pending,
void *user_data)
466 DBusMessage *reply = NULL;
469 crm_trace(
"LoadUnit result for %s arrived", op->
id);
472 if (pending != NULL) {
473 reply = dbus_pending_call_steal_reply(pending);
478 services_set_op_pending(op, NULL);
481 execute_after_loadunit(reply, user_data);
483 dbus_message_unref(reply);
506 DBusMessage *reply = NULL;
507 DBusPendingCall *pending = NULL;
510 if (pcmk__str_empty(arg_name)) {
514 if (!systemd_init()) {
517 "No DBus connection");
530 msg = systemd_new_method(
"LoadUnit");
534 name = systemd_unit_name(arg_name,
544 const char *unit = NULL;
547 reply = systemd_send_recv(msg, NULL,
549 dbus_message_unref(msg);
551 unit = execute_after_loadunit(reply, op);
557 }
else if (
path != NULL) {
558 *
path = strdup(unit);
565 dbus_message_unref(reply);
571 pending = systemd_send(msg, loadunit_completed, op, op->
timeout);
572 if (pending == NULL) {
574 "Unable to send DBus message");
575 dbus_message_unref(msg);
581 services_set_op_pending(op, pending);
582 dbus_message_unref(msg);
599sort_str(gconstpointer a, gconstpointer b)
608 return strcasecmp(a, b);
616 DBusMessageIter args;
617 DBusMessageIter unit;
618 DBusMessageIter elem;
619 DBusMessage *reply = NULL;
621 if (!systemd_init()) {
631 reply = systemd_call_simple_method(
"ListUnitFiles");
635 if (!dbus_message_iter_init(reply, &args)) {
636 crm_err(
"Could not list systemd unit files: systemd reply has no arguments");
637 dbus_message_unref(reply);
641 __func__, __LINE__)) {
642 crm_err(
"Could not list systemd unit files: systemd reply has invalid arguments");
643 dbus_message_unref(reply);
647 dbus_message_iter_recurse(&args, &unit);
648 for (; dbus_message_iter_get_arg_type(&unit) != DBUS_TYPE_INVALID;
649 dbus_message_iter_next(&unit)) {
651 DBusBasicValue value;
652 const char *match = NULL;
653 char *unit_name = NULL;
654 char *basename = NULL;
657 crm_warn(
"Skipping systemd reply argument with unexpected type");
661 dbus_message_iter_recurse(&unit, &elem);
663 crm_warn(
"Skipping systemd reply argument with no string");
667 dbus_message_iter_get_basic(&elem, &value);
668 if (value.str == NULL) {
669 crm_debug(
"ListUnitFiles reply did not provide a string");
672 crm_trace(
"DBus ListUnitFiles listed: %s", value.str);
674 match = systemd_unit_extension(value.str);
677 crm_debug(
"ListUnitFiles entry '%s' is not supported as resource",
683 basename = strrchr(value.str,
'/');
685 basename = basename + 1;
687 basename = value.str;
690 if (!strcmp(match,
".service")) {
692 unit_name = strndup(basename, match - basename);
694 unit_name = strdup(basename);
698 units = g_list_prepend(units, unit_name);
701 dbus_message_unref(reply);
703 crm_trace(
"Found %d manageable systemd unit files", nfiles);
704 units = g_list_sort(units, sort_str);
727 state = systemd_get_property(
path,
"LoadState", NULL, NULL, NULL,
738#define METADATA_FORMAT \
739 "<?xml " PCMK_XA_VERSION "=\"1.0\"?>\n" \
740 "<" PCMK_XE_RESOURCE_AGENT " " \
741 PCMK_XA_NAME "=\"%s\" " \
742 PCMK_XA_VERSION "=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n" \
743 " <" PCMK_XE_VERSION ">1.1</" PCMK_XE_VERSION ">\n" \
744 " <" PCMK_XE_LONGDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">\n" \
746 " </" PCMK_XE_LONGDESC ">\n" \
747 " <" PCMK_XE_SHORTDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">" \
748 "systemd unit file for %s" \
749 "</" PCMK_XE_SHORTDESC ">\n" \
750 " <" PCMK_XE_PARAMETERS "/>\n" \
751 " <" PCMK_XE_ACTIONS ">\n" \
752 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_START "\"" \
753 " " PCMK_META_TIMEOUT "=\"100s\" />\n" \
754 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STOP "\"" \
755 " " PCMK_META_TIMEOUT "=\"100s\" />\n" \
756 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STATUS "\"" \
757 " " PCMK_META_TIMEOUT "=\"100s\" />\n" \
758 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_MONITOR "\"" \
759 " " PCMK_META_TIMEOUT "=\"100s\"" \
760 " " PCMK_META_INTERVAL "=\"60s\" />\n" \
761 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_META_DATA "\"" \
762 " " PCMK_META_TIMEOUT "=\"5s\" />\n" \
763 " </" PCMK_XE_ACTIONS ">\n" \
764 " <" PCMK_XE_SPECIAL " " PCMK_XA_TAG "=\"systemd\"/>\n" \
765 "</" PCMK_XE_RESOURCE_AGENT ">\n"
768systemd_unit_metadata(
const char *
name,
int timeout)
776 desc = systemd_get_property(
path,
"Description", NULL, NULL, NULL,
805process_unit_method_reply(DBusMessage *reply,
svc_action_t *op)
811 dbus_error_init(&error);
817 set_result_from_method_error(op, &error);
818 dbus_error_free(&error);
821 __func__, __LINE__)) {
822 const char *reason =
"systemd D-Bus method had unexpected reply";
824 crm_info(
"DBus request for %s of %s succeeded but "
825 "return type was unexpected",
826 op->
action, pcmk__s(op->
rsc,
"unknown resource"));
843 const char *
path = NULL;
845 dbus_message_get_args(reply, NULL,
846 DBUS_TYPE_OBJECT_PATH, &
path,
849 crm_debug(
"DBus request for %s of %s using %s succeeded",
882static DBusHandlerResult
883job_removed_filter(DBusConnection *connection, DBusMessage *message,
887 const char *action_name = NULL;
889 const char *bus_path = NULL;
890 const char *unit_name = NULL;
891 const char *
result = NULL;
894 CRM_CHECK((connection != NULL) && (message != NULL),
895 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
900 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
903 dbus_error_init(&error);
904 if (!dbus_message_get_args(message, &error,
905 DBUS_TYPE_UINT32, &job_id,
906 DBUS_TYPE_OBJECT_PATH, &bus_path,
907 DBUS_TYPE_STRING, &unit_name,
908 DBUS_TYPE_STRING, &
result,
909 DBUS_TYPE_INVALID)) {
910 crm_err(
"Could not interpret systemd DBus signal: %s " QB_XS
" (%s)",
911 error.message, error.name);
912 dbus_error_free(&error);
913 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
918 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
921 action_name = pcmk__s(
action->action,
"(unknown)");
923 crm_trace(
"Setting %s result for %s (JobRemoved id=%" PRIu32
", result=%s",
924 action_name, unit_name, job_id,
result);
931 "systemd %s job for %s timed out",
932 action_name, unit_name);
936 "systemd %s job for %s failed with result '%s'",
937 action_name, unit_name,
result);
945 dbus_connection_remove_filter(systemd_proxy, job_removed_filter,
action);
946 return DBUS_HANDLER_RESULT_HANDLED;
956finalize_async_action_dbus(
void *
action)
969unit_method_complete(DBusPendingCall *pending,
void *user_data)
971 DBusMessage *reply = NULL;
977 if (pending != NULL) {
978 reply = dbus_pending_call_steal_reply(pending);
983 services_set_op_pending(op, NULL);
985 process_unit_method_reply(reply, op);
988 dbus_message_unref(reply);
1003 if (dbus_connection_add_filter(systemd_proxy, job_removed_filter, op,
1004 finalize_async_action_dbus)) {
1007 crm_err(
"Could not add D-Bus filter for systemd JobRemoved signals");
1009 "Failed to add D-Bus filter for systemd "
1010 "JobRemoved signal");
1024#define SYSTEMD_UNIT_OVERRIDE_TEMPLATE \
1026 "Description=Cluster Controlled %s\n" \
1027 "Before=pacemaker.service pacemaker_remote.service\n"
1029#define SYSTEMD_SERVICE_OVERRIDE \
1043get_override_dir(
const char *unit_name)
1045 GString *buf = g_string_sized_new(128);
1047 pcmk__g_strcat(buf,
"/run/systemd/system/", unit_name,
".d", NULL);
1058append_override_basename(GString *buf)
1060 g_string_append(buf,
"/50-pacemaker.conf");
1078systemd_create_override(
const char *agent,
int timeout)
1080 char *unit_name = NULL;
1081 GString *filename = NULL;
1082 GString *
override = NULL;
1087 unit_name = systemd_unit_name(agent,
false);
1089 rc = EINVAL;
goto done);
1091 filename = get_override_dir(unit_name);
1094 crm_err(
"Could not create systemd override directory %s: %s",
1099 append_override_basename(filename);
1100 fp = fopen(filename->str,
"w");
1103 crm_err(
"Cannot open systemd override file %s for writing: %s",
1110 if ((fd < 0) || (fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)) {
1112 crm_err(
"Failed to set permissions on systemd override file %s: %s",
1123 if (fputs(override->str, fp) == EOF) {
1125 crm_err(
"Cannot write to systemd override file %s", filename->str);
1135 systemd_daemon_reload(
timeout);
1137 }
else if (fp != NULL) {
1139 unlink(filename->str);
1145 if (filename != NULL) {
1146 g_string_free(filename, TRUE);
1148 if (
override != NULL) {
1149 g_string_free(
override, TRUE);
1155systemd_remove_override(
const char *agent,
int timeout)
1157 char *unit_name = systemd_unit_name(agent,
false);
1158 GString *filename = NULL;
1160 CRM_CHECK(!pcmk__str_empty(unit_name),
goto done);
1162 filename = get_override_dir(unit_name);
1163 append_override_basename(filename);
1165 if (unlink(filename->str) < 0) {
1170 crm_warn(
"Cannot remove systemd override file %s: %s",
1175 systemd_daemon_reload(
timeout);
1182 if (filename != NULL) {
1183 g_string_free(filename, TRUE);
1199parse_status_result(
const char *
name,
const char *state,
void *userdata)
1204 pcmk__s(op->
rsc,
"(unspecified)"),
name,
1205 pcmk__s(state,
"<null>"));
1216 }
else if (pcmk__str_eq(state,
"deactivating",
pcmk__str_none)) {
1224 services_set_op_pending(op, NULL);
1239 const char *method = NULL;
1240 DBusMessage *msg = NULL;
1241 DBusMessage *reply = NULL;
1245 DBusPendingCall *pending = NULL;
1248 state = systemd_get_property(unit,
"ActiveState",
1253 parse_status_result(
"ActiveState", state, op);
1256 }
else if (pending == NULL) {
1258 "Could not get state for unit %s from DBus",
1263 services_set_op_pending(op, pending);
1270 method =
"StartUnit";
1274 "Failed to create systemd override file "
1276 pcmk__s(op->
agent,
"(unspecified)"));
1284 method =
"StopUnit";
1288 method =
"RestartUnit";
1293 "Action %s not implemented "
1294 "for systemd resources",
1295 pcmk__s(op->
action,
"(unspecified)"));
1302 crm_trace(
"Calling %s for unit path %s%s%s",
1304 ((op->
rsc == NULL)?
"" :
" for resource "), pcmk__s(op->
rsc,
""));
1306 msg = systemd_new_method(method);
1311 const char *replace_s =
"replace";
1312 char *
name = systemd_unit_name(op->
agent,
1317 CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &
name, DBUS_TYPE_INVALID));
1318 CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &replace_s, DBUS_TYPE_INVALID));
1324 reply = systemd_send_recv(msg, NULL, op->
timeout);
1325 dbus_message_unref(msg);
1326 process_unit_method_reply(reply, op);
1327 if (reply != NULL) {
1328 dbus_message_unref(reply);
1332 DBusPendingCall *pending = systemd_send(msg, unit_method_complete, op,
1335 dbus_message_unref(msg);
1336 if (pending == NULL) {
1338 "Unable to send DBus message");
1342 services_set_op_pending(op, pending);
1348systemd_timeout_callback(gpointer p)
1353 crm_info(
"%s action for systemd unit %s named '%s' timed out",
1356 "%s action for systemd unit %s "
1357 "did not complete in time", op->
action, op->
agent);
1359 if (op->
opaque->job_path != NULL) {
1361 dbus_connection_remove_filter(systemd_proxy, job_removed_filter, op);
1390 if (pcmk__str_empty(op->
action) || pcmk__str_empty(op->
agent)) {
1392 "Bug in action caller");
1396 if (!systemd_init()) {
1398 "No DBus connection");
1402 crm_debug(
"Performing %ssynchronous %s op on systemd unit %s%s%s",
1404 ((op->
rsc == NULL)?
"" :
" for resource "), pcmk__s(op->
rsc,
""));
1416 "Bug in service library");
1421 systemd_timeout_callback, op);
#define PCMK_ACTION_STATUS
#define PCMK_ACTION_META_DATA
#define PCMK_ACTION_START
#define PCMK_ACTION_MONITOR
guint pcmk__create_timer(guint interval_ms, GSourceFunc fn, gpointer data)
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)
DBusConnection * pcmk_dbus_connect(void)
DBusMessage * pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection, DBusError *error, int timeout)
bool pcmk_dbus_find_error(const DBusPendingCall *pending, DBusMessage *reply, DBusError *ret)
DBusPendingCall * pcmk_dbus_send(DBusMessage *msg, DBusConnection *connection, void(*done)(DBusPendingCall *pending, void *user_data), void *user_data, int timeout)
bool pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected, const char *function, int line)
void pcmk_dbus_disconnect(DBusConnection *connection)
int pcmk__build_path(const char *path_c, mode_t mode)
#define crm_info(fmt, args...)
#define crm_warn(fmt, args...)
#define CRM_LOG_ASSERT(expr)
#define CRM_CHECK(expr, failure_action)
#define crm_debug(fmt, args...)
#define crm_err(fmt, args...)
#define crm_trace(fmt, args...)
Wrappers for and extensions to glib mainloop.
#define DBUS_TIMEOUT_USE_DEFAULT
pcmk__action_result_t result
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
enum ocf_exitcode pcmk_rc2ocf(int rc)
Map a function return code to the most similar OCF exit code.
ocf_exitcode
Exit status codes for resource agents.
@ PCMK_OCF_UNIMPLEMENT_FEATURE
Requested action not implemented.
@ PCMK_OCF_NOT_CONFIGURED
Parameter invalid (inherently)
@ PCMK_OCF_NOT_INSTALLED
Dependencies not available locally.
@ PCMK_OCF_UNKNOWN_ERROR
Unspecified error.
@ PCMK_OCF_NOT_RUNNING
Service safely stopped.
@ PCMK_OCF_UNKNOWN
Action is pending.
@ PCMK_EXEC_ERROR_FATAL
Execution failed, do not retry anywhere.
@ PCMK_EXEC_NOT_INSTALLED
Agent or dependency not available locally.
@ PCMK_EXEC_DONE
Action completed, result is known.
@ PCMK_EXEC_ERROR
Execution failed, may be retried.
@ PCMK_EXEC_TIMEOUT
Action did not complete in time.
@ PCMK_EXEC_PENDING
Action is in progress.
#define pcmk__assert(expr)
void services_add_inflight_op(svc_action_t *op)
void services__format_result(svc_action_t *action, int agent_status, enum pcmk_exec_status exec_status, const char *format,...) G_GNUC_PRINTF(4
void services__set_result(svc_action_t *action, int agent_status, enum pcmk_exec_status exec_status, const char *exit_reason)
int services__finalize_async_op(svc_action_t *op)
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
bool pcmk__ends_with_ext(const char *s, const char *match)
void pcmk__str_update(char **str, const char *value)
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
#define pcmk__str_copy(str)
Object for executing external actions.
char * agent
Resource agent name for resource actions, otherwise NULL.
int rc
Exit status of action (set by library upon completion)
char * rsc
XML ID of resource being executed for resource actions, otherwise NULL.
char * action
Name of action being executed for resource actions, otherwise NULL.
int timeout
Action timeout (in milliseconds)
char * stdout_data
Action stdout (set by library)
int status
Execution status (enum pcmk_exec_status set by library)
svc_action_private_t * opaque
This field should be treated as internal to Pacemaker.
#define SYSTEMD_SERVICE_OVERRIDE
enum ocf_exitcode services__systemd2ocf(int exit_status)
int services__execute_systemd(svc_action_t *op)
int services__systemd_prepare(svc_action_t *op)
GList * systemd_unit_listall(void)
#define SYSTEMD_UNIT_OVERRIDE_TEMPLATE
void systemd_cleanup(void)
bool systemd_unit_exists(const char *name)
Wrappers for and extensions to libxml2.
bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
char * pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)