1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include "dbus-common.h"
29 #define BUS_JOB_INTERFACE \
30 " <interface name=\"org.freedesktop.systemd1.Job\">\n" \
31 " <method name=\"Cancel\"/>\n" \
32 " <property name=\"Id\" type=\"u\" access=\"read\"/>\n" \
33 " <property name=\"Unit\" type=\"(so)\" access=\"read\"/>\n" \
34 " <property name=\"JobType\" type=\"s\" access=\"read\"/>\n" \
35 " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
38 #define INTROSPECTION \
39 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
42 BUS_PROPERTIES_INTERFACE \
44 BUS_INTROSPECTABLE_INTERFACE \
47 const char bus_job_interface[] _introspect_("Job") = BUS_JOB_INTERFACE;
49 #define INTERFACES_LIST \
50 BUS_GENERIC_INTERFACES_LIST \
51 "org.freedesktop.systemd1.Job\0"
53 #define INVALIDATING_PROPERTIES \
56 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_state, job_state, JobState);
57 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_type, job_type, JobType);
59 static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *data) {
68 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
71 if (!(p = unit_dbus_path(j->unit)))
74 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->id) ||
75 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
82 if (!dbus_message_iter_close_container(i, &sub))
88 static const BusProperty bus_job_properties[] = {
89 { "Id", bus_property_append_uint32, "u", offsetof(Job, id) },
90 { "State", bus_job_append_state, "s", offsetof(Job, state) },
91 { "JobType", bus_job_append_type, "s", offsetof(Job, type) },
92 { "Unit", bus_job_append_unit, "(so)", 0 },
96 static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connection, DBusMessage *message) {
97 DBusMessage *reply = NULL;
99 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) {
100 if (!(reply = dbus_message_new_method_return(message)))
103 job_finish_and_invalidate(j, JOB_CANCELED, true);
106 const BusBoundProperties bps[] = {
107 { "org.freedesktop.systemd1.Job", bus_job_properties, j },
110 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
114 if (!dbus_connection_send(connection, reply, NULL))
117 dbus_message_unref(reply);
120 return DBUS_HANDLER_RESULT_HANDLED;
124 dbus_message_unref(reply);
126 return DBUS_HANDLER_RESULT_NEED_MEMORY;
129 static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
139 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/job")) {
140 /* Be nice to gdbus and return introspection data for our mid-level paths */
142 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
143 char *introspection = NULL;
148 if (!(reply = dbus_message_new_method_return(message)))
151 /* We roll our own introspection code here, instead of
152 * relying on bus_default_message_handler() because we
153 * need to generate our introspection string
156 if (!(f = open_memstream(&introspection, &size)))
159 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
162 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
163 fputs(BUS_PEER_INTERFACE, f);
165 HASHMAP_FOREACH(j, m->jobs, i)
166 fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
168 fputs("</node>\n", f);
181 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
188 if (!dbus_connection_send(connection, reply, NULL))
191 dbus_message_unref(reply);
193 return DBUS_HANDLER_RESULT_HANDLED;
196 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
199 if ((r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j)) < 0) {
202 return DBUS_HANDLER_RESULT_NEED_MEMORY;
208 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown job");
209 return bus_send_error_reply(connection, message, &e, r);
212 return bus_send_error_reply(connection, message, NULL, r);
215 return bus_job_message_dispatch(j, connection, message);
219 dbus_message_unref(reply);
221 return DBUS_HANDLER_RESULT_NEED_MEMORY;
224 const DBusObjectPathVTable bus_job_vtable = {
225 .message_function = bus_job_message_handler
228 static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
229 DBusMessage *m = NULL;
235 if (bus_has_subscriber(j->manager) || j->forgot_bus_clients) {
239 r = bus_broadcast(j->manager, m);
240 dbus_message_unref(m);
245 /* If nobody is subscribed, we just send the message
246 * to the client(s) which created the job */
248 assert(j->bus_client_list);
249 LIST_FOREACH(client, cl, j->bus_client_list) {
256 if (!dbus_message_set_destination(m, cl->name))
259 if (!dbus_connection_send(cl->bus, m, NULL))
262 dbus_message_unref(m);
270 dbus_message_unref(m);
274 static DBusMessage* new_change_signal_message(Job *j) {
275 DBusMessage *m = NULL;
278 p = job_dbus_path(j);
282 if (j->sent_dbus_new_signal) {
283 /* Send a properties changed signal */
284 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Job", INVALIDATING_PROPERTIES);
289 /* Send a new signal */
291 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew");
295 if (!dbus_message_append_args(m,
296 DBUS_TYPE_UINT32, &j->id,
297 DBUS_TYPE_OBJECT_PATH, &p,
298 DBUS_TYPE_STRING, &j->unit->id,
307 dbus_message_unref(m);
312 static DBusMessage* new_removed_signal_message(Job *j) {
313 DBusMessage *m = NULL;
317 p = job_dbus_path(j);
321 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved");
325 r = job_result_to_string(j->result);
327 if (!dbus_message_append_args(m,
328 DBUS_TYPE_UINT32, &j->id,
329 DBUS_TYPE_OBJECT_PATH, &p,
330 DBUS_TYPE_STRING, &j->unit->id,
331 DBUS_TYPE_STRING, &r,
339 dbus_message_unref(m);
344 void bus_job_send_change_signal(Job *j) {
347 if (j->in_dbus_queue) {
348 LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
349 j->in_dbus_queue = false;
352 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients) {
353 j->sent_dbus_new_signal = true;
357 if (job_send_message(j, new_change_signal_message) < 0)
360 j->sent_dbus_new_signal = true;
365 log_error("Failed to allocate job change signal.");
368 void bus_job_send_removed_signal(Job *j) {
371 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients)
374 if (!j->sent_dbus_new_signal)
375 bus_job_send_change_signal(j);
377 if (job_send_message(j, new_removed_signal_message) < 0)
383 log_error("Failed to allocate job remove signal.");