chiark / gitweb /
socket: properly serialize/desrialize mqueue fds
[elogind.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_ACCESS_CHECK(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 };