]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/dbus-job.c
man: clarify why User=/Group= don't work with mount units
[thirdparty/systemd.git] / src / core / dbus-job.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
ea430986 2
a7334b09
LP
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
a7334b09
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
86fbf370
LP
22#include <errno.h>
23
ea430986
LP
24#include "dbus.h"
25#include "log.h"
4139c1b2 26#include "dbus-job.h"
bfebab7f 27#include "dbus-common.h"
cad45ba1 28#include "selinux-access.h"
ea430986 29
4288f619
LP
30#define BUS_JOB_INTERFACE \
31 " <interface name=\"org.freedesktop.systemd1.Job\">\n" \
32 " <method name=\"Cancel\"/>\n" \
4288f619
LP
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" \
37 " </interface>\n"
38
39#define INTROSPECTION \
40 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
41 "<node>\n" \
42 BUS_JOB_INTERFACE \
43 BUS_PROPERTIES_INTERFACE \
c4e2ceae 44 BUS_PEER_INTERFACE \
4288f619
LP
45 BUS_INTROSPECTABLE_INTERFACE \
46 "</node>\n"
47
9a60da28 48const char bus_job_interface[] _introspect_("Job") = BUS_JOB_INTERFACE;
ea430986 49
05feefe0
LP
50#define INTERFACES_LIST \
51 BUS_GENERIC_INTERFACES_LIST \
52 "org.freedesktop.systemd1.Job\0"
53
c4e2ceae 54#define INVALIDATING_PROPERTIES \
34df5a34 55 "State\0"
c4e2ceae 56
4139c1b2
LP
57static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_state, job_state, JobState);
58static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_type, job_type, JobType);
86fbf370 59
bfebab7f 60static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *data) {
86fbf370
LP
61 Job *j = data;
62 DBusMessageIter sub;
63 char *p;
86fbf370 64
86fbf370
LP
65 assert(i);
66 assert(property);
67 assert(j);
68
69 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
70 return -ENOMEM;
71
cad45ba1
LP
72 p = unit_dbus_path(j->unit);
73 if (!p)
86fbf370
LP
74 return -ENOMEM;
75
ac155bb8 76 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->id) ||
86fbf370
LP
77 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
78 free(p);
79 return -ENOMEM;
80 }
81
82 free(p);
83
84 if (!dbus_message_iter_close_container(i, &sub))
85 return -ENOMEM;
86
87 return 0;
88}
89
d200735e
MS
90static 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 },
95 { NULL, }
96};
86fbf370 97
d200735e 98static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connection, DBusMessage *message) {
cad45ba1 99 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
b548631a
LP
100
101 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) {
b548631a 102
cad45ba1
LP
103 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop");
104
105 reply = dbus_message_new_method_return(message);
106 if (!reply)
107 return DBUS_HANDLER_RESULT_NEED_MEMORY;
b548631a 108
cad45ba1 109 job_finish_and_invalidate(j, JOB_CANCELED, true);
d200735e
MS
110 } else {
111 const BusBoundProperties bps[] = {
112 { "org.freedesktop.systemd1.Job", bus_job_properties, j },
113 { NULL, }
114 };
b548631a 115
cad45ba1 116 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status");
b548631a 117
cad45ba1 118 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
b548631a
LP
119 }
120
cad45ba1
LP
121 if (!dbus_connection_send(connection, reply, NULL))
122 return DBUS_HANDLER_RESULT_NEED_MEMORY;
b548631a 123
cad45ba1 124 return DBUS_HANDLER_RESULT_HANDLED;
86fbf370
LP
125}
126
5e8d1c9a 127static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
ea430986 128 Manager *m = data;
86fbf370
LP
129 Job *j;
130 int r;
cad45ba1 131 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
ea430986
LP
132
133 assert(connection);
134 assert(message);
135 assert(m);
136
2cccbca4
LP
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 */
139
140 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
141 char *introspection = NULL;
142 FILE *f;
143 Iterator i;
144 size_t size;
145
ffc227c9 146 SELINUX_ACCESS_CHECK(connection, message, "status");
cad45ba1
LP
147
148 reply = dbus_message_new_method_return(message);
149 if (!reply)
2cccbca4
LP
150 goto oom;
151
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
155 * dynamically. */
156
cad45ba1
LP
157 f = open_memstream(&introspection, &size);
158 if (!f)
2cccbca4
LP
159 goto oom;
160
161 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
162 "<node>\n", f);
163
164 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
165 fputs(BUS_PEER_INTERFACE, f);
166
167 HASHMAP_FOREACH(j, m->jobs, i)
168 fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
169
170 fputs("</node>\n", f);
171
172 if (ferror(f)) {
173 fclose(f);
174 free(introspection);
175 goto oom;
176 }
177
178 fclose(f);
179
180 if (!introspection)
181 goto oom;
182
183 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
184 free(introspection);
185 goto oom;
186 }
187
188 free(introspection);
189
190 if (!dbus_connection_send(connection, reply, NULL))
191 goto oom;
192
2cccbca4
LP
193 return DBUS_HANDLER_RESULT_HANDLED;
194 }
195
196 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
197 }
198
cad45ba1
LP
199 r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j);
200 if (r == -ENOMEM)
201 goto oom;
202 if (r == -ENOENT) {
203 DBusError e;
86fbf370 204
cad45ba1
LP
205 dbus_error_init(&e);
206 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown job");
207 return bus_send_error_reply(connection, message, &e, r);
86fbf370 208 }
cad45ba1
LP
209 if (r < 0)
210 return bus_send_error_reply(connection, message, NULL, r);
86fbf370 211
5e8d1c9a 212 return bus_job_message_dispatch(j, connection, message);
2cccbca4
LP
213
214oom:
2cccbca4 215 return DBUS_HANDLER_RESULT_NEED_MEMORY;
ea430986
LP
216}
217
218const DBusObjectPathVTable bus_job_vtable = {
219 .message_function = bus_job_message_handler
220};
c1e1601e 221
97e6a119
MS
222static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
223 DBusMessage *m = NULL;
a567261a
LP
224 int r;
225
226 assert(j);
97e6a119 227 assert(new_message);
a567261a 228
39a18c60 229 if (bus_has_subscriber(j->manager) || j->forgot_bus_clients) {
97e6a119
MS
230 m = new_message(j);
231 if (!m)
232 goto oom;
233 r = bus_broadcast(j->manager, m);
234 dbus_message_unref(m);
235 if (r < 0)
a567261a
LP
236 return r;
237
97e6a119 238 } else {
a567261a 239 /* If nobody is subscribed, we just send the message
97e6a119
MS
240 * to the client(s) which created the job */
241 JobBusClient *cl;
242 assert(j->bus_client_list);
243 LIST_FOREACH(client, cl, j->bus_client_list) {
244 assert(cl->bus);
245
246 m = new_message(j);
247 if (!m)
248 goto oom;
a567261a 249
97e6a119
MS
250 if (!dbus_message_set_destination(m, cl->name))
251 goto oom;
a567261a 252
97e6a119
MS
253 if (!dbus_connection_send(cl->bus, m, NULL))
254 goto oom;
a567261a 255
97e6a119
MS
256 dbus_message_unref(m);
257 m = NULL;
258 }
a567261a
LP
259 }
260
261 return 0;
97e6a119
MS
262oom:
263 if (m)
264 dbus_message_unref(m);
265 return -ENOMEM;
a567261a
LP
266}
267
97e6a119 268static DBusMessage* new_change_signal_message(Job *j) {
c1e1601e 269 DBusMessage *m = NULL;
97e6a119 270 char *p = NULL;
c1e1601e 271
97e6a119
MS
272 p = job_dbus_path(j);
273 if (!p)
c1e1601e
LP
274 goto oom;
275
276 if (j->sent_dbus_new_signal) {
c4e2ceae 277 /* Send a properties changed signal */
97e6a119
MS
278 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Job", INVALIDATING_PROPERTIES);
279 if (!m)
c1e1601e 280 goto oom;
c4e2ceae 281
c1e1601e
LP
282 } else {
283 /* Send a new signal */
284
97e6a119
MS
285 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew");
286 if (!m)
c1e1601e
LP
287 goto oom;
288
289 if (!dbus_message_append_args(m,
290 DBUS_TYPE_UINT32, &j->id,
291 DBUS_TYPE_OBJECT_PATH, &p,
06dab8e1 292 DBUS_TYPE_STRING, &j->unit->id,
c1e1601e
LP
293 DBUS_TYPE_INVALID))
294 goto oom;
295 }
296
97e6a119 297 return m;
c1e1601e
LP
298
299oom:
c1e1601e
LP
300 if (m)
301 dbus_message_unref(m);
97e6a119
MS
302 free(p);
303 return NULL;
c1e1601e
LP
304}
305
97e6a119 306static DBusMessage* new_removed_signal_message(Job *j) {
c1e1601e 307 DBusMessage *m = NULL;
97e6a119 308 char *p = NULL;
5d44db4a 309 const char *r;
c1e1601e 310
97e6a119
MS
311 p = job_dbus_path(j);
312 if (!p)
c1e1601e
LP
313 goto oom;
314
97e6a119
MS
315 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved");
316 if (!m)
c1e1601e
LP
317 goto oom;
318
5d44db4a
LP
319 r = job_result_to_string(j->result);
320
c1e1601e
LP
321 if (!dbus_message_append_args(m,
322 DBUS_TYPE_UINT32, &j->id,
323 DBUS_TYPE_OBJECT_PATH, &p,
06dab8e1 324 DBUS_TYPE_STRING, &j->unit->id,
5d44db4a 325 DBUS_TYPE_STRING, &r,
c1e1601e
LP
326 DBUS_TYPE_INVALID))
327 goto oom;
328
97e6a119 329 return m;
c1e1601e 330
97e6a119
MS
331oom:
332 if (m)
333 dbus_message_unref(m);
c1e1601e 334 free(p);
97e6a119
MS
335 return NULL;
336}
337
338void bus_job_send_change_signal(Job *j) {
339 assert(j);
340
341 if (j->in_dbus_queue) {
342 LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
343 j->in_dbus_queue = false;
344 }
345
39a18c60 346 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients) {
97e6a119
MS
347 j->sent_dbus_new_signal = true;
348 return;
349 }
350
351 if (job_send_message(j, new_change_signal_message) < 0)
352 goto oom;
353
354 j->sent_dbus_new_signal = true;
c1e1601e
LP
355
356 return;
357
358oom:
97e6a119
MS
359 log_error("Failed to allocate job change signal.");
360}
c1e1601e 361
97e6a119
MS
362void bus_job_send_removed_signal(Job *j) {
363 assert(j);
c1e1601e 364
39a18c60 365 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients)
97e6a119
MS
366 return;
367
368 if (!j->sent_dbus_new_signal)
369 bus_job_send_change_signal(j);
370
371 if (job_send_message(j, new_removed_signal_message) < 0)
372 goto oom;
373
374 return;
375
376oom:
c1e1601e
LP
377 log_error("Failed to allocate job remove signal.");
378}