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[] = 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) {
63 _cleanup_free_ char *p = NULL;
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)) {
81 if (!dbus_message_iter_close_container(i, &sub))
87 static const BusProperty bus_job_properties[] = {
88 { "Id", bus_property_append_uint32, "u", offsetof(Job, id) },
89 { "State", bus_job_append_state, "s", offsetof(Job, state) },
90 { "JobType", bus_job_append_type, "s", offsetof(Job, type) },
91 { "Unit", bus_job_append_unit, "(so)", 0 },
95 static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connection, DBusMessage *message) {
96 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
98 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) {
100 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop");
101 job_finish_and_invalidate(j, JOB_CANCELED, true);
103 reply = dbus_message_new_method_return(message);
105 return DBUS_HANDLER_RESULT_NEED_MEMORY;
107 const BusBoundProperties bps[] = {
108 { "org.freedesktop.systemd1.Job", bus_job_properties, j },
112 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status");
113 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
116 if (!bus_maybe_send_reply(connection, message, reply))
117 return DBUS_HANDLER_RESULT_NEED_MEMORY;
119 return DBUS_HANDLER_RESULT_HANDLED;
122 static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
126 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
132 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/job")) {
133 /* Be nice to gdbus and return introspection data for our mid-level paths */
135 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
136 _cleanup_free_ char *introspection = NULL;
141 SELINUX_ACCESS_CHECK(connection, message, "status");
143 reply = dbus_message_new_method_return(message);
147 /* We roll our own introspection code here, instead of
148 * relying on bus_default_message_handler() because we
149 * need to generate our introspection string
152 f = open_memstream(&introspection, &size);
156 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
159 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
160 fputs(BUS_PEER_INTERFACE, f);
162 HASHMAP_FOREACH(j, m->jobs, i)
163 fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
165 fputs("</node>\n", f);
177 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
181 if (!bus_maybe_send_reply(connection, message, reply))
184 return DBUS_HANDLER_RESULT_HANDLED;
187 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
190 r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j);
197 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown job");
198 return bus_send_error_reply(connection, message, &e, r);
201 return bus_send_error_reply(connection, message, NULL, r);
203 return bus_job_message_dispatch(j, connection, message);
206 return DBUS_HANDLER_RESULT_NEED_MEMORY;
209 const DBusObjectPathVTable bus_job_vtable = {
210 .message_function = bus_job_message_handler
213 static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
214 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
220 if (bus_has_subscriber(j->manager) || j->forgot_bus_clients) {
225 r = bus_broadcast(j->manager, m);
230 /* If nobody is subscribed, we just send the message
231 * to the client(s) which created the job */
233 assert(j->bus_client_list);
235 LIST_FOREACH(client, cl, j->bus_client_list) {
242 if (!dbus_message_set_destination(m, cl->name))
245 if (!dbus_connection_send(cl->bus, m, NULL))
248 dbus_message_unref(m);
256 static DBusMessage* new_change_signal_message(Job *j) {
257 _cleanup_free_ char *p = NULL;
260 p = job_dbus_path(j);
264 if (j->sent_dbus_new_signal) {
265 /* Send a properties changed signal */
266 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Job", INVALIDATING_PROPERTIES);
271 /* Send a new signal */
273 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew");
277 if (!dbus_message_append_args(m,
278 DBUS_TYPE_UINT32, &j->id,
279 DBUS_TYPE_OBJECT_PATH, &p,
280 DBUS_TYPE_STRING, &j->unit->id,
281 DBUS_TYPE_INVALID)) {
282 dbus_message_unref(m);
290 static DBusMessage* new_removed_signal_message(Job *j) {
291 _cleanup_free_ char *p = NULL;
295 p = job_dbus_path(j);
299 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved");
303 r = job_result_to_string(j->result);
305 if (!dbus_message_append_args(m,
306 DBUS_TYPE_UINT32, &j->id,
307 DBUS_TYPE_OBJECT_PATH, &p,
308 DBUS_TYPE_STRING, &j->unit->id,
309 DBUS_TYPE_STRING, &r,
310 DBUS_TYPE_INVALID)) {
311 dbus_message_unref(m);
318 void bus_job_send_change_signal(Job *j) {
321 if (j->in_dbus_queue) {
322 LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j);
323 j->in_dbus_queue = false;
326 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients) {
327 j->sent_dbus_new_signal = true;
331 if (job_send_message(j, new_change_signal_message) < 0)
334 j->sent_dbus_new_signal = true;
339 log_error("Failed to allocate job change signal.");
342 void bus_job_send_removed_signal(Job *j) {
345 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients)
348 if (!j->sent_dbus_new_signal)
349 bus_job_send_change_signal(j);
351 if (job_send_message(j, new_removed_signal_message) < 0)
357 log_error("Failed to allocate job remove signal.");