chiark / gitweb /
8433a720b2a6f41fc898fe855476dddc25fa53c7
[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         _cleanup_free_ char *p = NULL;
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                 p = job_dbus_path(u->job);
250                 if (!p)
251                         return -ENOMEM;
252
253                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
254                     !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
255                         return -ENOMEM;
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
263                 p = unit_dbus_path(u);
264                 if (!p)
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                         return -ENOMEM;
270         }
271
272         if (!dbus_message_iter_close_container(i, &sub))
273                 return -ENOMEM;
274
275         return 0;
276 }
277
278 static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
279         Unit *u = data;
280         char *t;
281         CGroupBonding *cgb;
282         bool success;
283
284         assert(i);
285         assert(property);
286         assert(u);
287
288         cgb = unit_get_default_cgroup(u);
289         if (cgb) {
290                 t = cgroup_bonding_to_string(cgb);
291                 if (!t)
292                         return -ENOMEM;
293         } else
294                 t = (char*) "";
295
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
304 static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
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
312         LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
313                 char _cleanup_free_ *t = NULL;
314                 bool success;
315
316                 t = cgroup_bonding_to_string(cgb);
317                 if (!t)
318                         return -ENOMEM;
319
320                 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
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
331 static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
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
339         LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
340                 char _cleanup_free_ *v = NULL;
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);
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
362 static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
363         Unit *u = data;
364         dbus_bool_t b;
365
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
378 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
379         Unit *u = data;
380         const char *name, *message;
381         DBusMessageIter sub;
382
383         assert(i);
384         assert(property);
385         assert(u);
386
387         if (u->load_error != 0) {
388                 name = bus_errno_to_dbus(u->load_error);
389                 message = strempty(strerror(-u->load_error));
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
402 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
403         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
404         DBusError error;
405         JobType job_type = _JOB_TYPE_INVALID;
406         bool reload_if_possible = false;
407         int r;
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;
419         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
420                 job_type = JOB_TRY_RESTART;
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;
427         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
428                 const char *swho;
429                 int32_t signo;
430                 KillWho who;
431
432                 if (!dbus_message_get_args(
433                                     message,
434                                     &error,
435                                     DBUS_TYPE_STRING, &swho,
436                                     DBUS_TYPE_INT32, &signo,
437                                     DBUS_TYPE_INVALID))
438                         return bus_send_error_reply(connection, message, &error, -EINVAL);
439
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
448                 if (signo <= 0 || signo >= _NSIG)
449                         return bus_send_error_reply(connection, message, &error, -EINVAL);
450
451                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
452
453                 r = unit_kill(u, who, signo, &error);
454                 if (r < 0)
455                         return bus_send_error_reply(connection, message, &error, r);
456
457                 reply = dbus_message_new_method_return(message);
458                 if (!reply)
459                         goto oom;
460
461         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
462
463                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
464
465                 unit_reset_failed(u);
466
467                 reply = dbus_message_new_method_return(message);
468                 if (!reply)
469                         goto oom;
470
471         } else if (UNIT_VTABLE(u)->bus_message_handler)
472                 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
473         else
474                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
475
476         if (job_type != _JOB_TYPE_INVALID) {
477                 const char *smode;
478                 JobMode mode;
479
480                 if (!dbus_message_get_args(
481                                     message,
482                                     &error,
483                                     DBUS_TYPE_STRING, &smode,
484                                     DBUS_TYPE_INVALID))
485                         return bus_send_error_reply(connection, message, &error, -EINVAL);
486
487                 mode = job_mode_from_string(smode);
488                 if (mode < 0) {
489                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
490                         return bus_send_error_reply(connection, message, &error, -EINVAL);
491                 }
492
493                 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
494         }
495
496         if (reply)
497                 if (!dbus_connection_send(connection, reply, NULL))
498                         goto oom;
499
500         return DBUS_HANDLER_RESULT_HANDLED;
501
502 oom:
503         dbus_error_free(&error);
504         return DBUS_HANDLER_RESULT_NEED_MEMORY;
505 }
506
507 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage  *message, void *data) {
508         Manager *m = data;
509         Unit *u;
510         int r;
511         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
512         DBusError error;
513
514         assert(connection);
515         assert(message);
516         assert(m);
517
518         dbus_error_init(&error);
519
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
523                 SELINUX_ACCESS_CHECK(connection, message, "status");
524
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
532                         reply = dbus_message_new_method_return(message);
533                         if (!reply)
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
541                         f = open_memstream(&introspection, &size);
542                         if (!f)
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
554                                 if (k != u->id)
555                                         continue;
556
557                                 p = bus_path_escape(k);
558                                 if (!p) {
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
591                         return DBUS_HANDLER_RESULT_HANDLED;
592                 }
593
594                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
595         }
596
597         r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
598         if (r == -ENOMEM)
599                 goto oom;
600         if (r < 0)
601                 return bus_send_error_reply(connection, message, &error, r);
602
603         return bus_unit_message_dispatch(u, connection, message);
604
605 oom:
606         dbus_error_free(&error);
607
608         return DBUS_HANDLER_RESULT_NEED_MEMORY;
609 }
610
611 const DBusObjectPathVTable bus_unit_vtable = {
612         .message_function = bus_unit_message_handler
613 };
614
615 void bus_unit_send_change_signal(Unit *u) {
616         _cleanup_free_ char *p = NULL;
617         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
618
619         assert(u);
620
621         if (u->in_dbus_queue) {
622                 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
623                 u->in_dbus_queue = false;
624         }
625
626         if (!u->id)
627                 return;
628
629         if (!bus_has_subscriber(u->manager)) {
630                 u->sent_dbus_new_signal = true;
631                 return;
632         }
633
634         p = unit_dbus_path(u);
635         if (!p)
636                 goto oom;
637
638         if (u->sent_dbus_new_signal) {
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
642                  * behavior if needed. */
643
644                 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
645
646                         m = bus_properties_changed_new(p,
647                                                        UNIT_VTABLE(u)->bus_interface,
648                                                        UNIT_VTABLE(u)->bus_invalidating_properties);
649                         if (!m)
650                                 goto oom;
651
652                         if (bus_broadcast(u->manager, m) < 0)
653                                 goto oom;
654
655                         dbus_message_unref(m);
656                 }
657
658                 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
659                                                INVALIDATING_PROPERTIES);
660                 if (!m)
661                         goto oom;
662
663         } else {
664                 /* Send a new signal */
665
666                 m = dbus_message_new_signal("/org/freedesktop/systemd1",
667                                             "org.freedesktop.systemd1.Manager",
668                                             "UnitNew");
669                 if (!m)
670                         goto oom;
671
672                 if (!dbus_message_append_args(m,
673                                               DBUS_TYPE_STRING, &u->id,
674                                               DBUS_TYPE_OBJECT_PATH, &p,
675                                               DBUS_TYPE_INVALID))
676                         goto oom;
677         }
678
679         if (bus_broadcast(u->manager, m) < 0)
680                 goto oom;
681
682         u->sent_dbus_new_signal = true;
683
684         return;
685
686 oom:
687         log_oom();
688 }
689
690 void bus_unit_send_removed_signal(Unit *u) {
691         _cleanup_free_ char *p = NULL;
692         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
693
694         assert(u);
695
696         if (!bus_has_subscriber(u->manager))
697                 return;
698
699         if (!u->sent_dbus_new_signal)
700                 bus_unit_send_change_signal(u);
701
702         if (!u->id)
703                 return;
704
705         p = unit_dbus_path(u);
706         if (!p)
707                 goto oom;
708
709         m = dbus_message_new_signal("/org/freedesktop/systemd1",
710                                     "org.freedesktop.systemd1.Manager",
711                                     "UnitRemoved");
712         if (!m)
713                 goto oom;
714
715         if (!dbus_message_append_args(m,
716                                       DBUS_TYPE_STRING, &u->id,
717                                       DBUS_TYPE_OBJECT_PATH, &p,
718                                       DBUS_TYPE_INVALID))
719                 goto oom;
720
721         if (bus_broadcast(u->manager, m) < 0)
722                 goto oom;
723
724         return;
725
726 oom:
727         log_oom();
728 }
729
730 DBusHandlerResult 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))) {
772                 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
773                                "Operation refused, unit %s may be requested by dependency only.", u->id);
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
806 oom:
807         dbus_error_free(&error);
808
809         return DBUS_HANDLER_RESULT_NEED_MEMORY;
810 }
811
812 const 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 },
821         { "BindsTo",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]),                true },
822         { "PartOf",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PART_OF]),                 true },
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 },
827         { "ConsistsOf",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]),             true },
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 },
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 },
837         { "RequiresMountsFor",    bus_property_append_strv,          "as", offsetof(Unit, requires_mounts_for),                        true },
838         { "Documentation",        bus_property_append_strv,          "as", offsetof(Unit, documentation),                              true },
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 },
844         { "SourcePath",           bus_property_append_string,         "s", offsetof(Unit, source_path),                                true },
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 };