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");
105 reply = dbus_message_new_method_return(message);
107 return DBUS_HANDLER_RESULT_NEED_MEMORY;
109 job_finish_and_invalidate(j, JOB_CANCELED, true);
111 const BusBoundProperties bps[] = {
112 { "org.freedesktop.systemd1.Job", bus_job_properties, j },
116 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status");
118 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
121 if (!dbus_connection_send(connection, reply, NULL))
122 return DBUS_HANDLER_RESULT_NEED_MEMORY;
124 return DBUS_HANDLER_RESULT_HANDLED;
127 static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
131 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
137 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/job")) {
138 /* Be nice to gdbus and return introspection data for our mid-level paths */
140 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
141 char *introspection = NULL;
146 SELINUX_ACCESS_CHECK(connection, message, "status");
148 reply = dbus_message_new_method_return(message);
152 /* We roll our own introspection code here, instead of
153 * relying on bus_default_message_handler() because we
154 * need to generate our introspection string
157 f = open_memstream(&introspection, &size);
161 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
164 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
165 fputs(BUS_PEER_INTERFACE, f);
167 HASHMAP_FOREACH(j, m->jobs, i)
168 fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
170 fputs("</node>\n", f);
183 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
190 if (!dbus_connection_send(connection, reply, NULL))
193 return DBUS_HANDLER_RESULT_HANDLED;
196 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
199 r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j);
206 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown job");
207 return bus_send_error_reply(connection, message, &e, r);
210 return bus_send_error_reply(connection, message, NULL, r);
212 return bus_job_message_dispatch(j, connection, message);
215 return DBUS_HANDLER_RESULT_NEED_MEMORY;
218 const DBusObjectPathVTable bus_job_vtable = {
219 .message_function = bus_job_message_handler
222 static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
223 DBusMessage *m = NULL;
229 if (bus_has_subscriber(j->manager) || j->forgot_bus_clients) {
233 r = bus_broadcast(j->manager, m);
234 dbus_message_unref(m);
239 /* If nobody is subscribed, we just send the message
240 * to the client(s) which created the job */
242 assert(j->bus_client_list);
243 LIST_FOREACH(client, cl, j->bus_client_list) {
250 if (!dbus_message_set_destination(m, cl->name))
253 if (!dbus_connection_send(cl->bus, m, NULL))
256 dbus_message_unref(m);
264 dbus_message_unref(m);
268 static DBusMessage* new_change_signal_message(Job *j) {
269 DBusMessage *m = NULL;
272 p = job_dbus_path(j);
276 if (j->sent_dbus_new_signal) {
277 /* Send a properties changed signal */
278 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Job", INVALIDATING_PROPERTIES);
283 /* Send a new signal */
285 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew");
289 if (!dbus_message_append_args(m,
290 DBUS_TYPE_UINT32, &j->id,
291 DBUS_TYPE_OBJECT_PATH, &p,
292 DBUS_TYPE_STRING, &j->unit->id,
301 dbus_message_unref(m);
306 static DBusMessage* new_removed_signal_message(Job *j) {
307 DBusMessage *m = NULL;
311 p = job_dbus_path(j);
315 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved");
319 r = job_result_to_string(j->result);
321 if (!dbus_message_append_args(m,
322 DBUS_TYPE_UINT32, &j->id,
323 DBUS_TYPE_OBJECT_PATH, &p,
324 DBUS_TYPE_STRING, &j->unit->id,
325 DBUS_TYPE_STRING, &r,
333 dbus_message_unref(m);
338 void bus_job_send_change_signal(Job *j) {
341 if (j->in_dbus_queue) {
342 LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
343 j->in_dbus_queue = false;
346 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients) {
347 j->sent_dbus_new_signal = true;
351 if (job_send_message(j, new_change_signal_message) < 0)
354 j->sent_dbus_new_signal = true;
359 log_error("Failed to allocate job change signal.");
362 void bus_job_send_removed_signal(Job *j) {
365 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients)
368 if (!j->sent_dbus_new_signal)
369 bus_job_send_change_signal(j);
371 if (job_send_message(j, new_removed_signal_message) < 0)
377 log_error("Failed to allocate job remove signal.");