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