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"
28 #include "selinux-access.h"
30 #define BUS_JOB_INTERFACE \
31 " <interface name=\"org.freedesktop.systemd1.Job\">\n" \
32 " <method name=\"Cancel\"/>\n" \
33 " <property name=\"Id\" type=\"u\" access=\"read\"/>\n" \
34 " <property name=\"Unit\" type=\"(so)\" access=\"read\"/>\n" \
35 " <property name=\"JobType\" type=\"s\" access=\"read\"/>\n" \
36 " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
39 #define INTROSPECTION \
40 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
43 BUS_PROPERTIES_INTERFACE \
45 BUS_INTROSPECTABLE_INTERFACE \
48 const char bus_job_interface[] _introspect_("Job") = BUS_JOB_INTERFACE;
50 #define INTERFACES_LIST \
51 BUS_GENERIC_INTERFACES_LIST \
52 "org.freedesktop.systemd1.Job\0"
54 #define INVALIDATING_PROPERTIES \
57 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_state, job_state, JobState);
58 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_type, job_type, JobType);
60 static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *data) {
69 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
72 p = unit_dbus_path(j->unit);
76 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->id) ||
77 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
84 if (!dbus_message_iter_close_container(i, &sub))
90 static const BusProperty bus_job_properties[] = {
91 { "Id", bus_property_append_uint32, "u", offsetof(Job, id) },
92 { "State", bus_job_append_state, "s", offsetof(Job, state) },
93 { "JobType", bus_job_append_type, "s", offsetof(Job, type) },
94 { "Unit", bus_job_append_unit, "(so)", 0 },
98 static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connection, DBusMessage *message) {
99 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
101 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) {
103 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop");
104 job_finish_and_invalidate(j, JOB_CANCELED, true);
106 reply = dbus_message_new_method_return(message);
108 return DBUS_HANDLER_RESULT_NEED_MEMORY;
110 const BusBoundProperties bps[] = {
111 { "org.freedesktop.systemd1.Job", bus_job_properties, j },
115 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status");
116 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
119 if (!bus_maybe_send_reply(connection, message, reply))
120 return DBUS_HANDLER_RESULT_NEED_MEMORY;
122 return DBUS_HANDLER_RESULT_HANDLED;
125 static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
129 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
135 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/job")) {
136 /* Be nice to gdbus and return introspection data for our mid-level paths */
138 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
139 char *introspection = NULL;
144 SELINUX_ACCESS_CHECK(connection, message, "status");
146 reply = dbus_message_new_method_return(message);
150 /* We roll our own introspection code here, instead of
151 * relying on bus_default_message_handler() because we
152 * need to generate our introspection string
155 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 (!bus_maybe_send_reply(connection, message, reply))
191 return DBUS_HANDLER_RESULT_HANDLED;
194 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
197 r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j);
204 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown job");
205 return bus_send_error_reply(connection, message, &e, r);
208 return bus_send_error_reply(connection, message, NULL, r);
210 return bus_job_message_dispatch(j, connection, message);
213 return DBUS_HANDLER_RESULT_NEED_MEMORY;
216 const DBusObjectPathVTable bus_job_vtable = {
217 .message_function = bus_job_message_handler
220 static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
221 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
227 if (bus_has_subscriber(j->manager) || j->forgot_bus_clients) {
232 r = bus_broadcast(j->manager, m);
237 /* If nobody is subscribed, we just send the message
238 * to the client(s) which created the job */
240 assert(j->bus_client_list);
242 LIST_FOREACH(client, cl, j->bus_client_list) {
249 if (!dbus_message_set_destination(m, cl->name))
252 if (!dbus_connection_send(cl->bus, m, NULL))
255 dbus_message_unref(m);
263 static DBusMessage* new_change_signal_message(Job *j) {
264 _cleanup_free_ char *p = NULL;
267 p = job_dbus_path(j);
271 if (j->sent_dbus_new_signal) {
272 /* Send a properties changed signal */
273 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Job", INVALIDATING_PROPERTIES);
278 /* Send a new signal */
280 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew");
284 if (!dbus_message_append_args(m,
285 DBUS_TYPE_UINT32, &j->id,
286 DBUS_TYPE_OBJECT_PATH, &p,
287 DBUS_TYPE_STRING, &j->unit->id,
288 DBUS_TYPE_INVALID)) {
289 dbus_message_unref(m);
297 static DBusMessage* new_removed_signal_message(Job *j) {
298 _cleanup_free_ char *p = NULL;
302 p = job_dbus_path(j);
306 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved");
310 r = job_result_to_string(j->result);
312 if (!dbus_message_append_args(m,
313 DBUS_TYPE_UINT32, &j->id,
314 DBUS_TYPE_OBJECT_PATH, &p,
315 DBUS_TYPE_STRING, &j->unit->id,
316 DBUS_TYPE_STRING, &r,
317 DBUS_TYPE_INVALID)) {
318 dbus_message_unref(m);
325 void bus_job_send_change_signal(Job *j) {
328 if (j->in_dbus_queue) {
329 LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
330 j->in_dbus_queue = false;
333 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients) {
334 j->sent_dbus_new_signal = true;
338 if (job_send_message(j, new_change_signal_message) < 0)
341 j->sent_dbus_new_signal = true;
346 log_error("Failed to allocate job change signal.");
349 void bus_job_send_removed_signal(Job *j) {
352 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients)
355 if (!j->sent_dbus_new_signal)
356 bus_job_send_change_signal(j);
358 if (job_send_message(j, new_removed_signal_message) < 0)
364 log_error("Failed to allocate job remove signal.");