23static void invoke_unit_by_path(
svc_action_t *op,
const char *unit);
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"
63static inline DBusMessage *
64systemd_new_method(
const char *method)
75static DBusConnection* systemd_proxy = NULL;
77static inline DBusPendingCall *
78systemd_send(DBusMessage *msg,
79 void(*done)(DBusPendingCall *pending,
void *user_data),
85static inline DBusMessage *
86systemd_send_recv(DBusMessage *msg, DBusError *error,
int timeout)
103systemd_call_simple_method(
const char *method)
105 DBusMessage *msg = systemd_new_method(method);
106 DBusMessage *reply = NULL;
113 crm_err(
"Could not create message to send %s to systemd", method);
117 dbus_error_init(&error);
119 dbus_message_unref(msg);
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);
127 }
else if (reply == NULL) {
128 crm_err(
"Could not send %s to systemd: no reply received", method);
138 static int need_init = 1;
142 && dbus_connection_get_is_connected(systemd_proxy) == FALSE) {
143 crm_warn(
"Connection to System DBus is closed. Reconnecting...");
145 systemd_proxy = NULL;
153 if (systemd_proxy == NULL) {
160systemd_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)
164 return systemd_proxy?
175 systemd_proxy = NULL;
192systemd_unit_extension(
const char *
name)
195 const char *dot = strrchr(
name,
'.');
197 if (dot && (!strcmp(dot,
".service")
198 || !strcmp(dot,
".socket")
199 || !strcmp(dot,
".mount")
200 || !strcmp(dot,
".timer")
201 || !strcmp(dot,
".path"))) {
209systemd_service_name(
const char *
name,
bool add_instance_name)
211 const char *dot = NULL;
213 if (pcmk__str_empty(
name)) {
226 dot = systemd_unit_extension(
name);
229 if (dot !=
name && *(dot-1) ==
'@') {
232 if (asprintf(&s,
"%.*spacemaker%s", (
int) (dot-
name),
name, dot) == -1) {
242 }
else if (add_instance_name && *(
name+strlen(
name)-1) ==
'@') {
251systemd_daemon_reload_complete(DBusPendingCall *pending,
void *user_data)
254 DBusMessage *reply = NULL;
255 unsigned int reload_count = GPOINTER_TO_UINT(user_data);
257 dbus_error_init(&error);
259 reply = dbus_pending_call_steal_reply(pending);
263 crm_warn(
"Could not issue systemd reload %d: %s",
264 reload_count, error.message);
265 dbus_error_free(&error);
268 crm_trace(
"Reload %d complete", reload_count);
272 dbus_pending_call_unref(pending);
275 dbus_message_unref(reply);
280systemd_daemon_reload(
int timeout)
282 static unsigned int reload_count = 0;
283 DBusMessage *msg = systemd_new_method(
"Reload");
287 systemd_send(msg, systemd_daemon_reload_complete,
288 GUINT_TO_POINTER(reload_count),
timeout);
289 dbus_message_unref(msg);
302set_result_from_method_error(
svc_action_t *op,
const DBusError *error)
305 "Unable to invoke systemd DBus method");
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")) {
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"));
321 "systemd unit %s not found", op->
agent);
324 crm_info(
"DBus request for %s of systemd unit %s%s%s failed: %s",
326 ((op->
rsc == NULL)?
"" :
" for resource "), pcmk__s(op->rsc,
""),
341execute_after_loadunit(DBusMessage *reply,
svc_action_t *op)
343 const char *
path = NULL;
351 set_result_from_method_error(op, &error);
353 dbus_error_free(&error);
356 __func__, __LINE__)) {
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);
363 crm_info(
"Could not load systemd unit: "
364 "DBus reply has unexpected type");
368 dbus_message_get_args (reply, NULL,
369 DBUS_TYPE_OBJECT_PATH, &
path,
375 invoke_unit_by_path(op,
path);
379 "No DBus object found for systemd unit %s",
396loadunit_completed(DBusPendingCall *pending,
void *user_data)
398 DBusMessage *reply = NULL;
401 crm_trace(
"LoadUnit result for %s arrived", op->
id);
404 if (pending != NULL) {
405 reply = dbus_pending_call_steal_reply(pending);
410 services_set_op_pending(op, NULL);
413 execute_after_loadunit(reply, user_data);
415 dbus_message_unref(reply);
438 DBusMessage *reply = NULL;
439 DBusPendingCall *pending = NULL;
442 if (!systemd_init()) {
445 "No DBus connection");
458 msg = systemd_new_method(
"LoadUnit");
462 name = systemd_service_name(arg_name,
464 || pcmk__str_eq(op->
action,
473 const char *unit = NULL;
476 reply = systemd_send_recv(msg, NULL,
478 dbus_message_unref(msg);
480 unit = execute_after_loadunit(reply, op);
486 }
else if (
path != NULL) {
487 *
path = strdup(unit);
494 dbus_message_unref(reply);
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);
510 services_set_op_pending(op, pending);
511 dbus_message_unref(msg);
528sort_str(gconstpointer a, gconstpointer b)
537 return strcasecmp(a, b);
545 DBusMessageIter args;
546 DBusMessageIter unit;
547 DBusMessageIter elem;
548 DBusMessage *reply = NULL;
550 if (systemd_init() == FALSE) {
560 reply = systemd_call_simple_method(
"ListUnitFiles");
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);
570 __func__, __LINE__)) {
571 crm_err(
"Could not list systemd unit files: systemd reply has invalid arguments");
572 dbus_message_unref(reply);
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)) {
580 DBusBasicValue value;
581 const char *match = NULL;
582 char *unit_name = NULL;
583 char *basename = NULL;
586 crm_warn(
"Skipping systemd reply argument with unexpected type");
590 dbus_message_iter_recurse(&unit, &elem);
592 crm_warn(
"Skipping systemd reply argument with no string");
596 dbus_message_iter_get_basic(&elem, &value);
597 if (value.str == NULL) {
598 crm_debug(
"ListUnitFiles reply did not provide a string");
601 crm_trace(
"DBus ListUnitFiles listed: %s", value.str);
603 match = systemd_unit_extension(value.str);
606 crm_debug(
"ListUnitFiles entry '%s' is not supported as resource",
612 basename = strrchr(value.str,
'/');
614 basename = basename + 1;
616 basename = value.str;
619 if (!strcmp(match,
".service")) {
621 unit_name = strndup(basename, match - basename);
623 unit_name = strdup(basename);
627 units = g_list_prepend(units, unit_name);
630 dbus_message_unref(reply);
632 crm_trace(
"Found %d manageable systemd unit files", nfiles);
633 units = g_list_sort(units, sort_str);
655 state = systemd_get_property(
path,
"LoadState", NULL, NULL, NULL,
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" \
674 " <shortdesc lang=\"en\">systemd unit file for %s</shortdesc>\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" \
683 " <special tag=\"systemd\"/>\n" \
684 "</resource-agent>\n"
687systemd_unit_metadata(
const char *
name,
int timeout)
693 char *escaped = NULL;
697 desc = systemd_get_property(
path,
"Description", NULL, NULL, NULL,
720process_unit_method_reply(DBusMessage *reply,
svc_action_t *op)
724 dbus_error_init(&error);
730 set_result_from_method_error(op, &error);
731 dbus_error_free(&error);
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");
742 const char *
path = NULL;
744 dbus_message_get_args(reply, NULL,
745 DBUS_TYPE_OBJECT_PATH, &
path,
747 crm_debug(
"DBus request for %s of %s using %s succeeded",
761unit_method_complete(DBusPendingCall *pending,
void *user_data)
763 DBusMessage *reply = NULL;
769 if (pending != NULL) {
770 reply = dbus_pending_call_steal_reply(pending);
775 services_set_op_pending(op, NULL);
778 process_unit_method_reply(reply, op);
781 dbus_message_unref(reply);
785#define SYSTEMD_OVERRIDE_ROOT "/run/systemd/system/"
796#define SYSTEMD_OVERRIDE_TEMPLATE \
798 "Description=Cluster Controlled %s\n" \
799 "Before=pacemaker.service pacemaker_remote.service\n" \
806create_world_readable(
const char *filename)
808 mode_t orig_umask = umask(S_IWGRP | S_IWOTH);
809 FILE *fp = fopen(filename,
"w");
816create_override_dir(
const char *agent)
819 "/%s.service.d", agent);
823 crm_warn(
"Could not create systemd override directory %s: %s",
830get_override_filename(
const char *agent)
833 "/%s.service.d/50-pacemaker.conf", agent);
837systemd_create_override(
const char *agent,
int timeout)
839 FILE *file_strm = NULL;
840 char *override_file = get_override_filename(agent);
842 create_override_dir(agent);
847 file_strm = create_world_readable(override_file);
848 if (file_strm == NULL) {
849 crm_err(
"Cannot open systemd override file %s for writing",
854 int rc = fprintf(file_strm,
"%s\n",
override);
858 crm_perror(LOG_WARNING,
"Cannot write to systemd override file %s",
863 systemd_daemon_reload(
timeout);
870systemd_remove_override(
const char *agent,
int timeout)
872 char *override_file = get_override_filename(agent);
873 int rc = unlink(override_file);
877 crm_perror(LOG_DEBUG,
"Cannot remove systemd override file %s",
880 systemd_daemon_reload(
timeout);
897parse_status_result(
const char *
name,
const char *state,
void *userdata)
902 pcmk__s(op->
rsc,
"(unspecified)"),
name,
903 pcmk__s(state,
"<null>"));
922 services_set_op_pending(op, NULL);
937 const char *method = NULL;
938 DBusMessage *msg = NULL;
939 DBusMessage *reply = NULL;
943 DBusPendingCall *pending = NULL;
946 state = systemd_get_property(unit,
"ActiveState",
948 op, (op->synchronous? NULL : &pending),
951 parse_status_result(
"ActiveState", state, op);
954 }
else if (pending == NULL) {
956 "Could not get state for unit %s from DBus",
961 services_set_op_pending(op, pending);
966 method =
"StartUnit";
974 method =
"RestartUnit";
979 "Action %s not implemented "
980 "for systemd resources",
981 pcmk__s(op->
action,
"(unspecified)"));
988 crm_trace(
"Calling %s for unit path %s%s%s",
990 ((op->
rsc == NULL)?
"" :
" for resource "), pcmk__s(op->rsc,
""));
992 msg = systemd_new_method(method);
997 const char *replace_s =
"replace";
998 char *
name = systemd_service_name(op->
agent,
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));
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);
1018 DBusPendingCall *pending = systemd_send(msg, unit_method_complete, op,
1021 dbus_message_unref(msg);
1022 if (pending == NULL) {
1024 "Unable to send DBus message");
1028 services_set_op_pending(op, pending);
1034systemd_timeout_callback(gpointer p)
1039 crm_info(
"%s action for systemd unit %s named '%s' timed out",
1042 "%s action for systemd unit %s "
1043 "did not complete in time", op->
action, op->
agent);
1069 if ((op->
action == NULL) || (op->
agent == NULL)) {
1071 "Bug in action caller");
1075 if (!systemd_init()) {
1077 "No DBus connection");
1081 crm_debug(
"Performing %ssynchronous %s op on systemd unit %s%s%s",
1083 ((op->
rsc == NULL)?
"" :
" for resource "), pcmk__s(op->
rsc,
""));
1095 "Bug in service library");
1099 systemd_timeout_callback, op);
#define PCMK_ACTION_STATUS
#define PCMK_ACTION_META_DATA
#define PCMK_ACTION_START
#define PCMK_ACTION_MONITOR
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
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_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
#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
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return 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.
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)
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
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)
svc_action_private_t * opaque
This field should be treated as internal to Pacemaker.
gboolean systemd_unit_exists(const char *name)
#define SYSTEMD_OVERRIDE_TEMPLATE
enum ocf_exitcode services__systemd2ocf(int exit_status)
#define SYSTEMD_OVERRIDE_ROOT
int services__execute_systemd(svc_action_t *op)
int services__systemd_prepare(svc_action_t *op)
GList * systemd_unit_listall(void)
void systemd_cleanup(void)
char * crm_xml_escape(const char *text)
Replace special characters with their XML escape sequences.