39 #define SYSTEMD_DBUS_SERVICE "org.freedesktop.systemd1"
40 #define SYSTEMD_DBUS_PATH "/org/freedesktop/systemd1"
41 #define SYSTEMD_DBUS_INTERFACE "org.freedesktop.systemd1.Manager"
42 #define SYSTEMD_DBUS_SIGNAL_JOB_REMOVED "JobRemoved"
43 #define SYSTEMD_DBUS_SIGNAL_JOB_REMOVED_MATCH \
45 "path='" SYSTEMD_DBUS_PATH "'," \
46 "interface='" SYSTEMD_DBUS_INTERFACE "'," \
47 "member='" SYSTEMD_DBUS_SIGNAL_JOB_REMOVED "'"
57 gboolean systemd_control_service(
const char *name,
const char *method, gboolean add_to_waitlist);
58 gboolean systemd_control_wait_for_waitlist(
void);
59 gboolean systemd_control_start (
void);
60 void systemd_control_stop (
void);
67 static DBusConnection *systemd_con = NULL;
72 static GQueue systemd_waitlist = G_QUEUE_INIT;
73 static gboolean systemd_waitlist_has_error = FALSE;
84 gboolean systemd_control_service(
const char *name,
const char *method, gboolean add_to_waitlist)
88 DBusMessage *req = NULL;
89 DBusMessage *rsp = NULL;
90 DBusError err = DBUS_ERROR_INIT;
91 const char *arg =
"replace";
94 log_debug(
"%s(%s) ...", method, name);
97 log_err(
"not connected to system bus; skip systemd unit control");
101 if (add_to_waitlist && g_queue_get_length(&systemd_waitlist) == 0) {
104 systemd_con, SYSTEMD_DBUS_SIGNAL_JOB_REMOVED_MATCH, NULL);
105 dbus_connection_flush(systemd_con);
108 req = dbus_message_new_method_call(SYSTEMD_DBUS_SERVICE,
110 SYSTEMD_DBUS_INTERFACE,
113 log_err(
"failed to construct %s.%s request",
114 SYSTEMD_DBUS_INTERFACE,
119 if( !dbus_message_append_args(req,
120 DBUS_TYPE_STRING, &name,
121 DBUS_TYPE_STRING, &arg,
124 log_debug(
"error appending arguments");
128 rsp = dbus_connection_send_with_reply_and_block(systemd_con, req, -1, &err);
130 log_err(
"no reply to %s.%s request: %s: %s",
131 SYSTEMD_DBUS_INTERFACE,
133 err.name, err.message);
137 if( dbus_set_error_from_message(&err, rsp) ) {
138 log_err(
"got error reply to %s.%s request: %s: %s",
139 SYSTEMD_DBUS_INTERFACE,
141 err.name, err.message);
145 if( !dbus_message_get_args(rsp, &err,
146 DBUS_TYPE_OBJECT_PATH, &res,
147 DBUS_TYPE_INVALID) ) {
148 log_err(
"failed to parse reply to %s.%s request: %s: %s",
149 SYSTEMD_DBUS_INTERFACE,
151 err.name, err.message);
157 dbus_error_free(&err);
159 log_debug(
"%s(%s) -> %s", method, name, res ?:
"N/A");
161 if (add_to_waitlist) {
163 g_queue_push_tail(&systemd_waitlist, g_strdup(res));
164 }
else if (g_queue_get_length(&systemd_waitlist) == 0) {
165 dbus_bus_remove_match(
166 systemd_con, SYSTEMD_DBUS_SIGNAL_JOB_REMOVED_MATCH, NULL);
167 dbus_connection_flush(systemd_con);
171 if( rsp ) dbus_message_unref(rsp);
172 if( req ) dbus_message_unref(req);
177 gboolean systemd_control_wait_for_waitlist(
void)
179 struct timespec wait_until;
180 gboolean has_error = FALSE;
183 clock_gettime(CLOCK_MONOTONIC, &wait_until);
184 wait_until.tv_sec += 5;
186 log_debug(
"Wait 5 seconds for systemd jobs in waitlist to starts");
188 while (g_queue_get_length(&systemd_waitlist) != 0) {
189 dbus_bool_t still_connected = dbus_connection_read_write_dispatch(systemd_con, 1000 );
192 clock_gettime(CLOCK_MONOTONIC, &now);
194 if (!still_connected || now.tv_sec > wait_until.tv_sec)
196 log_err(
"Waiting for systemd jobs fails");
197 log_debug(
"The following jobs are in the wait list at the time:");
200 for (list = systemd_waitlist.head; list; list = list->next) {
203 char *job = list->data;
204 log_debug(
"- %s", job);
209 g_queue_clear(&systemd_waitlist);
217 has_error |= systemd_waitlist_has_error;
218 systemd_waitlist_has_error = FALSE;
221 log_err(
"Some job fails. See the log above to find out more.");
223 dbus_bus_remove_match(
224 systemd_con, SYSTEMD_DBUS_SIGNAL_JOB_REMOVED_MATCH, NULL);
225 dbus_connection_flush(systemd_con);
234 static DBusHandlerResult systemd_dbus_msg_handler(DBusConnection *
const connection, DBusMessage *
const msg, gpointer
const user_data)
239 DBusError err = DBUS_ERROR_INIT;
240 DBusHandlerResult status = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
243 const char *job = NULL;
244 const char *unit = NULL;
245 const char *result = NULL;
248 char *match_rules = NULL;
251 if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
254 if (g_strcmp0(dbus_message_get_path(msg), SYSTEMD_DBUS_PATH) != 0)
257 if (g_strcmp0(dbus_message_get_interface(msg), SYSTEMD_DBUS_INTERFACE) != 0)
260 if (g_strcmp0(dbus_message_get_member(msg), SYSTEMD_DBUS_SIGNAL_JOB_REMOVED) != 0)
263 dbus_message_get_args(msg, &err,
264 DBUS_TYPE_UINT32, &
id,
265 DBUS_TYPE_OBJECT_PATH, &job,
266 DBUS_TYPE_STRING, &unit,
267 DBUS_TYPE_STRING, &result,
270 if (dbus_error_is_set(&err)) {
271 log_err(
"Fails to parse systemd JobRemoved signal: %s", err.message);
272 dbus_error_free(&err);
276 log_debug(
"Received JobRemoved for job '%s', unit '%s'. the result is '%s'.",
278 success = g_strcmp0(result,
"done") == 0 || g_strcmp0(result,
"skipped") == 0;
280 entry = g_queue_find_custom(&systemd_waitlist, job, (GCompareFunc) g_strcmp0);
282 log_debug(
"Job '%s' is not of our interest.", job);
283 status = DBUS_HANDLER_RESULT_HANDLED;
288 g_queue_delete_link(&systemd_waitlist, entry);
291 log_err(
"Job '%s' for unit '%s' fails with result '%s'", job, unit, result);
292 systemd_waitlist_has_error = TRUE;
295 status = DBUS_HANDLER_RESULT_HANDLED;
302 systemd_control_start(
void)
304 LOG_REGISTER_CONTEXT;
306 gboolean ack = FALSE;
307 DBusError error = DBUS_ERROR_INIT;
309 log_debug(
"starting systemd control");
314 if( (systemd_con = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error)) == 0 )
316 log_err(
"Could not connect to dbus for systemd control: %s\n", error.message);
317 dbus_error_free(&error);
321 if (!dbus_connection_add_filter(systemd_con, systemd_dbus_msg_handler, NULL, NULL))
332 systemd_control_stop(
void)
334 LOG_REGISTER_CONTEXT;
336 log_debug(
"stopping systemd control");
341 dbus_connection_close(systemd_con);
342 dbus_connection_unref(systemd_con),