]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/dbus-unit.c
systemctl: honour inhibitors only when running unprivileged
[thirdparty/systemd.git] / src / core / dbus-unit.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
ea430986
LP
22#include <errno.h>
23
24#include "dbus.h"
25#include "log.h"
4139c1b2 26#include "dbus-unit.h"
398ef8ba 27#include "bus-errors.h"
bfebab7f 28#include "dbus-common.h"
e2417e41 29#include "selinux-access.h"
ea430986 30
9a60da28 31const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
4288f619 32
c4e2ceae
LP
33#define INVALIDATING_PROPERTIES \
34 "LoadState\0" \
35 "ActiveState\0" \
36 "SubState\0" \
37 "InactiveExitTimestamp\0" \
38 "ActiveEnterTimestamp\0" \
39 "ActiveExitTimestamp\0" \
40 "InactiveEnterTimestamp\0" \
41 "Job\0" \
34df5a34 42 "NeedDaemonReload\0"
c4e2ceae 43
e2110e5d 44static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
4139c1b2
LP
45 char *t;
46 Iterator j;
47 DBusMessageIter sub;
48 Unit *u = data;
49
50 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
51 return -ENOMEM;
52
ac155bb8 53 SET_FOREACH(t, u->names, j)
4139c1b2
LP
54 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
55 return -ENOMEM;
56
57 if (!dbus_message_iter_close_container(i, &sub))
58 return -ENOMEM;
59
60 return 0;
61}
62
e2110e5d 63static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
a7f241db 64 Unit *u = data, *f;
8fe914ec
LP
65 const char *d;
66
8fe914ec
LP
67 assert(i);
68 assert(property);
69 assert(u);
70
a7f241db 71 f = unit_following(u);
ac155bb8 72 d = f ? f->id : "";
8fe914ec
LP
73
74 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
75 return -ENOMEM;
76
77 return 0;
78}
79
e2110e5d 80static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
5301be81
LP
81 Unit *u;
82 Iterator j;
83 DBusMessageIter sub;
84 Set *s = data;
85
86 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
87 return -ENOMEM;
88
89 SET_FOREACH(u, s, j)
ac155bb8 90 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
5301be81
LP
91 return -ENOMEM;
92
93 if (!dbus_message_iter_close_container(i, &sub))
94 return -ENOMEM;
95
96 return 0;
97}
98
e2110e5d 99static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
ea430986
LP
100 Unit *u = data;
101 const char *d;
102
ea430986
LP
103 assert(i);
104 assert(property);
105 assert(u);
106
107 d = unit_description(u);
108
109 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
110 return -ENOMEM;
111
112 return 0;
113}
114
e2110e5d 115static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
ea430986 116
e2110e5d 117static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
ea430986
LP
118 Unit *u = data;
119 const char *state;
120
ea430986
LP
121 assert(i);
122 assert(property);
123 assert(u);
124
125 state = unit_active_state_to_string(unit_active_state(u));
126
127 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
128 return -ENOMEM;
129
130 return 0;
131}
132
e2110e5d 133static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
10a94420
LP
134 Unit *u = data;
135 const char *state;
136
10a94420
LP
137 assert(i);
138 assert(property);
139 assert(u);
140
141 state = unit_sub_state_to_string(u);
142
143 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
144 return -ENOMEM;
145
146 return 0;
147}
148
e2110e5d 149static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
a4375746
LP
150 Unit *u = data;
151 const char *state;
152
153 assert(i);
154 assert(property);
155 assert(u);
156
157 state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
158
159 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
160 return -ENOMEM;
161
162 return 0;
163}
164
e2110e5d 165static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
38131695
LP
166 Unit *u = data;
167 dbus_bool_t b;
168
38131695
LP
169 assert(i);
170 assert(property);
171 assert(u);
172
064f51fa 173 b = unit_can_start(u) &&
ac155bb8 174 !u->refuse_manual_start;
b5e9dba8
LP
175
176 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
177 return -ENOMEM;
178
179 return 0;
180}
181
e2110e5d 182static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
b5e9dba8
LP
183 Unit *u = data;
184 dbus_bool_t b;
185
b5e9dba8
LP
186 assert(i);
187 assert(property);
188 assert(u);
189
190 /* On the lower levels we assume that every unit we can start
191 * we can also stop */
192
193 b = unit_can_start(u) &&
ac155bb8 194 !u->refuse_manual_stop;
38131695
LP
195
196 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
197 return -ENOMEM;
198
199 return 0;
200}
201
e2110e5d 202static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
38131695
LP
203 Unit *u = data;
204 dbus_bool_t b;
205
38131695
LP
206 assert(i);
207 assert(property);
208 assert(u);
209
4139c1b2 210 b = unit_can_reload(u);
38131695
LP
211
212 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
213 return -ENOMEM;
214
215 return 0;
216}
217
e2110e5d 218static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
2528a7a6
LP
219 Unit *u = data;
220 dbus_bool_t b;
221
2528a7a6
LP
222 assert(i);
223 assert(property);
224 assert(u);
225
226 b = unit_can_isolate(u) &&
ac155bb8 227 !u->refuse_manual_start;
2528a7a6
LP
228
229 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
230 return -ENOMEM;
231
232 return 0;
233}
234
e2110e5d 235static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
38131695
LP
236 Unit *u = data;
237 DBusMessageIter sub;
f93891f3 238 _cleanup_free_ char *p = NULL;
38131695 239
38131695
LP
240 assert(i);
241 assert(property);
242 assert(u);
243
244 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
245 return -ENOMEM;
246
ac155bb8 247 if (u->job) {
38131695 248
f93891f3
ZJS
249 p = job_dbus_path(u->job);
250 if (!p)
38131695
LP
251 return -ENOMEM;
252
ac155bb8 253 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
f93891f3 254 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
38131695 255 return -ENOMEM;
38131695
LP
256 } else {
257 uint32_t id = 0;
258
259 /* No job, so let's fill in some placeholder
260 * data. Since we need to fill in a valid path we
261 * simple point to ourselves. */
262
f93891f3
ZJS
263 p = unit_dbus_path(u);
264 if (!p)
38131695
LP
265 return -ENOMEM;
266
267 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
f93891f3 268 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
38131695 269 return -ENOMEM;
38131695
LP
270 }
271
38131695
LP
272 if (!dbus_message_iter_close_container(i, &sub))
273 return -ENOMEM;
274
275 return 0;
276}
277
e2110e5d 278static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
4139c1b2
LP
279 Unit *u = data;
280 char *t;
281 CGroupBonding *cgb;
282 bool success;
ea430986 283
4139c1b2
LP
284 assert(i);
285 assert(property);
286 assert(u);
287
f93891f3
ZJS
288 cgb = unit_get_default_cgroup(u);
289 if (cgb) {
290 t = cgroup_bonding_to_string(cgb);
291 if (!t)
4139c1b2
LP
292 return -ENOMEM;
293 } else
294 t = (char*) "";
ea430986 295
4139c1b2
LP
296 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
297
298 if (cgb)
299 free(t);
300
301 return success ? 0 : -ENOMEM;
302}
303
e2110e5d 304static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
4139c1b2
LP
305 Unit *u = data;
306 CGroupBonding *cgb;
307 DBusMessageIter sub;
308
309 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
310 return -ENOMEM;
311
ac155bb8 312 LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
f93891f3 313 char _cleanup_free_ *t = NULL;
4139c1b2
LP
314 bool success;
315
f93891f3
ZJS
316 t = cgroup_bonding_to_string(cgb);
317 if (!t)
4139c1b2
LP
318 return -ENOMEM;
319
320 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
4139c1b2
LP
321 if (!success)
322 return -ENOMEM;
323 }
324
325 if (!dbus_message_iter_close_container(i, &sub))
326 return -ENOMEM;
327
328 return 0;
329}
330
e2110e5d 331static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
d8bbda91
LP
332 Unit *u = data;
333 CGroupAttribute *a;
334 DBusMessageIter sub, sub2;
335
336 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
337 return -ENOMEM;
338
ac155bb8 339 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
f93891f3 340 char _cleanup_free_ *v = NULL;
d8bbda91
LP
341 bool success;
342
343 if (a->map_callback)
344 a->map_callback(a->controller, a->name, a->value, &v);
345
346 success =
347 dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
348 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
349 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
350 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
351 dbus_message_iter_close_container(&sub, &sub2);
d8bbda91
LP
352 if (!success)
353 return -ENOMEM;
354 }
355
356 if (!dbus_message_iter_close_container(i, &sub))
357 return -ENOMEM;
358
359 return 0;
360}
361
e2110e5d 362static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
45fb0699
LP
363 Unit *u = data;
364 dbus_bool_t b;
365
45fb0699
LP
366 assert(i);
367 assert(property);
368 assert(u);
369
370 b = unit_need_daemon_reload(u);
371
372 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
373 return -ENOMEM;
374
375 return 0;
376}
377
e2110e5d 378static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
9f39404c
LP
379 Unit *u = data;
380 const char *name, *message;
381 DBusMessageIter sub;
382
383 assert(i);
384 assert(property);
385 assert(u);
386
ac155bb8
MS
387 if (u->load_error != 0) {
388 name = bus_errno_to_dbus(u->load_error);
389 message = strempty(strerror(-u->load_error));
9f39404c
LP
390 } else
391 name = message = "";
392
393 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
394 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
395 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
396 !dbus_message_iter_close_container(i, &sub))
397 return -ENOMEM;
398
399 return 0;
400}
401
5e8d1c9a 402static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
cad45ba1 403 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
b548631a
LP
404 DBusError error;
405 JobType job_type = _JOB_TYPE_INVALID;
6f28c033 406 bool reload_if_possible = false;
e2417e41 407 int r;
b548631a
LP
408
409 dbus_error_init(&error);
410
411 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
412 job_type = JOB_START;
413 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
414 job_type = JOB_STOP;
415 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
416 job_type = JOB_RELOAD;
417 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
418 job_type = JOB_RESTART;
9a1ac7b9
LP
419 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
420 job_type = JOB_TRY_RESTART;
6f28c033
LP
421 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
422 reload_if_possible = true;
423 job_type = JOB_RESTART;
424 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
425 reload_if_possible = true;
426 job_type = JOB_TRY_RESTART;
8a0867d6 427 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
c74f17d9 428 const char *swho;
8a0867d6 429 int32_t signo;
8a0867d6 430 KillWho who;
8a0867d6
LP
431
432 if (!dbus_message_get_args(
433 message,
434 &error,
435 DBUS_TYPE_STRING, &swho,
8a0867d6
LP
436 DBUS_TYPE_INT32, &signo,
437 DBUS_TYPE_INVALID))
bfebab7f 438 return bus_send_error_reply(connection, message, &error, -EINVAL);
8a0867d6 439
0a524ba7
LP
440 if (isempty(swho))
441 who = KILL_ALL;
442 else {
443 who = kill_who_from_string(swho);
444 if (who < 0)
445 return bus_send_error_reply(connection, message, &error, -EINVAL);
446 }
447
0a524ba7 448 if (signo <= 0 || signo >= _NSIG)
bfebab7f 449 return bus_send_error_reply(connection, message, &error, -EINVAL);
8a0867d6 450
cad45ba1
LP
451 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
452
c74f17d9
LP
453 r = unit_kill(u, who, signo, &error);
454 if (r < 0)
bfebab7f 455 return bus_send_error_reply(connection, message, &error, r);
8a0867d6 456
c74f17d9
LP
457 reply = dbus_message_new_method_return(message);
458 if (!reply)
8a0867d6
LP
459 goto oom;
460
fdf20a31 461 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
5632e374 462
cad45ba1
LP
463 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
464
fdf20a31 465 unit_reset_failed(u);
5632e374 466
cad45ba1
LP
467 reply = dbus_message_new_method_return(message);
468 if (!reply)
5632e374
LP
469 goto oom;
470
6f28c033 471 } else if (UNIT_VTABLE(u)->bus_message_handler)
5e8d1c9a 472 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
b548631a 473 else
4139c1b2 474 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
b548631a
LP
475
476 if (job_type != _JOB_TYPE_INVALID) {
477 const char *smode;
478 JobMode mode;
bc0f8771 479
b548631a
LP
480 if (!dbus_message_get_args(
481 message,
482 &error,
483 DBUS_TYPE_STRING, &smode,
484 DBUS_TYPE_INVALID))
bfebab7f 485 return bus_send_error_reply(connection, message, &error, -EINVAL);
b548631a 486
cad45ba1
LP
487 mode = job_mode_from_string(smode);
488 if (mode < 0) {
398ef8ba 489 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
bfebab7f 490 return bus_send_error_reply(connection, message, &error, -EINVAL);
398ef8ba 491 }
b548631a 492
cad45ba1 493 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
b548631a
LP
494 }
495
cad45ba1 496 if (reply)
5e8d1c9a 497 if (!dbus_connection_send(connection, reply, NULL))
b548631a
LP
498 goto oom;
499
b548631a
LP
500 return DBUS_HANDLER_RESULT_HANDLED;
501
502oom:
b548631a 503 dbus_error_free(&error);
b548631a 504 return DBUS_HANDLER_RESULT_NEED_MEMORY;
ea430986
LP
505}
506
5e8d1c9a 507static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
ea430986
LP
508 Manager *m = data;
509 Unit *u;
510 int r;
cad45ba1 511 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
80fbf05e 512 DBusError error;
ea430986
LP
513
514 assert(connection);
515 assert(message);
516 assert(m);
517
80fbf05e
MS
518 dbus_error_init(&error);
519
2cccbca4
LP
520 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
521 /* Be nice to gdbus and return introspection data for our mid-level paths */
522
ffc227c9 523 SELINUX_ACCESS_CHECK(connection, message, "status");
cad45ba1 524
2cccbca4
LP
525 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
526 char *introspection = NULL;
527 FILE *f;
528 Iterator i;
529 const char *k;
530 size_t size;
531
cad45ba1
LP
532 reply = dbus_message_new_method_return(message);
533 if (!reply)
2cccbca4
LP
534 goto oom;
535
536 /* We roll our own introspection code here, instead of
537 * relying on bus_default_message_handler() because we
538 * need to generate our introspection string
539 * dynamically. */
540
cad45ba1
LP
541 f = open_memstream(&introspection, &size);
542 if (!f)
2cccbca4
LP
543 goto oom;
544
545 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
546 "<node>\n", f);
547
548 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
549 fputs(BUS_PEER_INTERFACE, f);
550
551 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
552 char *p;
553
ac155bb8 554 if (k != u->id)
2cccbca4
LP
555 continue;
556
cad45ba1
LP
557 p = bus_path_escape(k);
558 if (!p) {
2cccbca4
LP
559 fclose(f);
560 free(introspection);
561 goto oom;
562 }
563
564 fprintf(f, "<node name=\"%s\"/>", p);
565 free(p);
566 }
567
568 fputs("</node>\n", f);
569
570 if (ferror(f)) {
571 fclose(f);
572 free(introspection);
573 goto oom;
574 }
575
576 fclose(f);
577
578 if (!introspection)
579 goto oom;
580
581 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
582 free(introspection);
583 goto oom;
584 }
585
586 free(introspection);
587
588 if (!dbus_connection_send(connection, reply, NULL))
589 goto oom;
590
2cccbca4
LP
591 return DBUS_HANDLER_RESULT_HANDLED;
592 }
593
594 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
595 }
596
80fbf05e 597 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
cad45ba1
LP
598 if (r == -ENOMEM)
599 goto oom;
600 if (r < 0)
80fbf05e 601 return bus_send_error_reply(connection, message, &error, r);
ea430986 602
5e8d1c9a 603 return bus_unit_message_dispatch(u, connection, message);
2cccbca4
LP
604
605oom:
80fbf05e
MS
606 dbus_error_free(&error);
607
2cccbca4 608 return DBUS_HANDLER_RESULT_NEED_MEMORY;
ea430986
LP
609}
610
611const DBusObjectPathVTable bus_unit_vtable = {
612 .message_function = bus_unit_message_handler
613};
c1e1601e
LP
614
615void bus_unit_send_change_signal(Unit *u) {
f93891f3
ZJS
616 _cleanup_free_ char *p = NULL;
617 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
c1e1601e
LP
618
619 assert(u);
c1e1601e 620
ac155bb8
MS
621 if (u->in_dbus_queue) {
622 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
623 u->in_dbus_queue = false;
c0bd0cf7 624 }
c1e1601e 625
ac155bb8 626 if (!u->id)
04ade7d2
LP
627 return;
628
ac155bb8
MS
629 if (!bus_has_subscriber(u->manager)) {
630 u->sent_dbus_new_signal = true;
c1e1601e 631 return;
94b6dfa2 632 }
c1e1601e 633
f93891f3
ZJS
634 p = unit_dbus_path(u);
635 if (!p)
c1e1601e
LP
636 goto oom;
637
ac155bb8 638 if (u->sent_dbus_new_signal) {
c4e2ceae
LP
639 /* Send a properties changed signal. First for the
640 * specific type, then for the generic unit. The
641 * clients may rely on this order to get atomic
c5315881 642 * behavior if needed. */
c4e2ceae
LP
643
644 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
645
f93891f3
ZJS
646 m = bus_properties_changed_new(p,
647 UNIT_VTABLE(u)->bus_interface,
648 UNIT_VTABLE(u)->bus_invalidating_properties);
649 if (!m)
c4e2ceae
LP
650 goto oom;
651
ac155bb8 652 if (bus_broadcast(u->manager, m) < 0)
c4e2ceae 653 goto oom;
c1e1601e 654
c4e2ceae
LP
655 dbus_message_unref(m);
656 }
657
f93891f3
ZJS
658 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
659 INVALIDATING_PROPERTIES);
660 if (!m)
c1e1601e 661 goto oom;
c4e2ceae 662
c1e1601e 663 } else {
c1e1601e
LP
664 /* Send a new signal */
665
f93891f3
ZJS
666 m = dbus_message_new_signal("/org/freedesktop/systemd1",
667 "org.freedesktop.systemd1.Manager",
668 "UnitNew");
669 if (!m)
c1e1601e
LP
670 goto oom;
671
c1e1601e 672 if (!dbus_message_append_args(m,
ac155bb8 673 DBUS_TYPE_STRING, &u->id,
c1e1601e
LP
674 DBUS_TYPE_OBJECT_PATH, &p,
675 DBUS_TYPE_INVALID))
676 goto oom;
677 }
678
ac155bb8 679 if (bus_broadcast(u->manager, m) < 0)
c1e1601e
LP
680 goto oom;
681
ac155bb8 682 u->sent_dbus_new_signal = true;
c1e1601e
LP
683
684 return;
685
686oom:
f93891f3 687 log_oom();
c1e1601e
LP
688}
689
690void bus_unit_send_removed_signal(Unit *u) {
f93891f3
ZJS
691 _cleanup_free_ char *p = NULL;
692 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
c1e1601e
LP
693
694 assert(u);
695
ac155bb8 696 if (!bus_has_subscriber(u->manager))
c1e1601e
LP
697 return;
698
ac155bb8 699 if (!u->sent_dbus_new_signal)
7535cc78
LP
700 bus_unit_send_change_signal(u);
701
ac155bb8 702 if (!u->id)
04ade7d2
LP
703 return;
704
f93891f3
ZJS
705 p = unit_dbus_path(u);
706 if (!p)
c1e1601e
LP
707 goto oom;
708
f93891f3
ZJS
709 m = dbus_message_new_signal("/org/freedesktop/systemd1",
710 "org.freedesktop.systemd1.Manager",
711 "UnitRemoved");
712 if (!m)
c1e1601e
LP
713 goto oom;
714
c1e1601e 715 if (!dbus_message_append_args(m,
ac155bb8 716 DBUS_TYPE_STRING, &u->id,
c1e1601e
LP
717 DBUS_TYPE_OBJECT_PATH, &p,
718 DBUS_TYPE_INVALID))
719 goto oom;
720
ac155bb8 721 if (bus_broadcast(u->manager, m) < 0)
c1e1601e
LP
722 goto oom;
723
c1e1601e
LP
724 return;
725
726oom:
f93891f3 727 log_oom();
c1e1601e 728}
e2110e5d 729
cad45ba1
LP
730DBusHandlerResult bus_unit_queue_job(
731 DBusConnection *connection,
732 DBusMessage *message,
733 Unit *u,
734 JobType type,
735 JobMode mode,
736 bool reload_if_possible) {
737
738 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
739 _cleanup_free_ char *path = NULL;
740 Job *j;
741 JobBusClient *cl;
742 DBusError error;
743 int r;
744
745 assert(connection);
746 assert(message);
747 assert(u);
748 assert(type >= 0 && type < _JOB_TYPE_MAX);
749 assert(mode >= 0 && mode < _JOB_MODE_MAX);
750
751 dbus_error_init(&error);
752
753 if (reload_if_possible && unit_can_reload(u)) {
754 if (type == JOB_RESTART)
755 type = JOB_RELOAD_OR_START;
756 else if (type == JOB_TRY_RESTART)
757 type = JOB_RELOAD;
758 }
759
760 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
761 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
762 type == JOB_STOP ? "stop" : "reload");
763
764 if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
765 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
766 return bus_send_error_reply(connection, message, &error, -EPERM);
767 }
768
769 if ((type == JOB_START && u->refuse_manual_start) ||
770 (type == JOB_STOP && u->refuse_manual_stop) ||
771 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
f93891f3
ZJS
772 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
773 "Operation refused, unit %s may be requested by dependency only.", u->id);
cad45ba1
LP
774 return bus_send_error_reply(connection, message, &error, -EPERM);
775 }
776
777 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
778 if (r < 0)
779 return bus_send_error_reply(connection, message, &error, r);
780
781 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
782 if (!cl)
783 goto oom;
784
785 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
786
787 reply = dbus_message_new_method_return(message);
788 if (!reply)
789 goto oom;
790
791 path = job_dbus_path(j);
792 if (!path)
793 goto oom;
794
795 if (!dbus_message_append_args(
796 reply,
797 DBUS_TYPE_OBJECT_PATH, &path,
798 DBUS_TYPE_INVALID))
799 goto oom;
800
801 if (!dbus_connection_send(connection, reply, NULL))
802 goto oom;
803
804 return DBUS_HANDLER_RESULT_HANDLED;
805
806oom:
807 dbus_error_free(&error);
808
809 return DBUS_HANDLER_RESULT_NEED_MEMORY;
810}
811
e2110e5d
MS
812const BusProperty bus_unit_properties[] = {
813 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
814 { "Names", bus_unit_append_names, "as", 0 },
815 { "Following", bus_unit_append_following, "s", 0 },
816 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
817 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
818 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
819 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
820 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
7f2cddae 821 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
b05afff1 822 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
e2110e5d
MS
823 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
824 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
825 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
826 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
b05afff1 827 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
e2110e5d
MS
828 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
829 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
830 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
831 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
832 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
833 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
834 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
7f2cddae
LP
835 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
836 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
7c8fa05c 837 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
49dbfa7b 838 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
e2110e5d
MS
839 { "Description", bus_unit_append_description, "s", 0 },
840 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
841 { "ActiveState", bus_unit_append_active_state, "s", 0 },
842 { "SubState", bus_unit_append_sub_state, "s", 0 },
843 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
1b64d026 844 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
e2110e5d
MS
845 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
846 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
847 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
848 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
849 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
850 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
851 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
852 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
853 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
854 { "CanStart", bus_unit_append_can_start, "b", 0 },
855 { "CanStop", bus_unit_append_can_stop, "b", 0 },
856 { "CanReload", bus_unit_append_can_reload, "b", 0 },
857 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
858 { "Job", bus_unit_append_job, "(uo)", 0 },
859 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
860 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
861 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
862 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
863 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
864 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
865 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
866 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
867 { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
868 { "ControlGroup", bus_unit_append_cgroups, "as", 0 },
869 { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
870 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
871 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
872 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
873 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
874 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
875 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
876 { NULL, }
877};