chiark / gitweb /
user-sessions: rely on PID 1 to kill sessions
[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 #include "cgroup-util.h"
31 #include "strv.h"
32 #include "path-util.h"
33 #include "fileio.h"
34
35 const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
36
37 #define INVALIDATING_PROPERTIES                 \
38         "LoadState\0"                           \
39         "ActiveState\0"                         \
40         "SubState\0"                            \
41         "InactiveExitTimestamp\0"               \
42         "ActiveEnterTimestamp\0"                \
43         "ActiveExitTimestamp\0"                 \
44         "InactiveEnterTimestamp\0"              \
45         "Job\0"                                 \
46         "NeedDaemonReload\0"
47
48 static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
49         char *t;
50         Iterator j;
51         DBusMessageIter sub;
52         Unit *u = data;
53
54         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
55                 return -ENOMEM;
56
57         SET_FOREACH(t, u->names, j)
58                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
59                         return -ENOMEM;
60
61         if (!dbus_message_iter_close_container(i, &sub))
62                 return -ENOMEM;
63
64         return 0;
65 }
66
67 static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
68         Unit *u = data, *f;
69         const char *d;
70
71         assert(i);
72         assert(property);
73         assert(u);
74
75         f = unit_following(u);
76         d = f ? f->id : "";
77
78         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
79                 return -ENOMEM;
80
81         return 0;
82 }
83
84 static int bus_unit_append_slice(DBusMessageIter *i, const char *property, void *data) {
85         Unit *u = data;
86         const char *d;
87
88         assert(i);
89         assert(property);
90         assert(u);
91
92         d = strempty(unit_slice_name(u));
93
94         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
95                 return -ENOMEM;
96
97         return 0;
98 }
99
100 static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
101         Unit *u;
102         Iterator j;
103         DBusMessageIter sub;
104         Set *s = data;
105
106         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
107                 return -ENOMEM;
108
109         SET_FOREACH(u, s, j)
110                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
111                         return -ENOMEM;
112
113         if (!dbus_message_iter_close_container(i, &sub))
114                 return -ENOMEM;
115
116         return 0;
117 }
118
119 static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
120         Unit *u = data;
121         const char *d;
122
123         assert(i);
124         assert(property);
125         assert(u);
126
127         d = unit_description(u);
128
129         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
130                 return -ENOMEM;
131
132         return 0;
133 }
134
135 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
136
137 static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
138         Unit *u = data;
139         const char *state;
140
141         assert(i);
142         assert(property);
143         assert(u);
144
145         state = unit_active_state_to_string(unit_active_state(u));
146
147         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
148                 return -ENOMEM;
149
150         return 0;
151 }
152
153 static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
154         Unit *u = data;
155         const char *state;
156
157         assert(i);
158         assert(property);
159         assert(u);
160
161         state = unit_sub_state_to_string(u);
162
163         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
164                 return -ENOMEM;
165
166         return 0;
167 }
168
169 static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
170         Unit *u = data;
171         const char *state;
172
173         assert(i);
174         assert(property);
175         assert(u);
176
177         state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
178
179         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
180                 return -ENOMEM;
181
182         return 0;
183 }
184
185 static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
186         Unit *u = data;
187         dbus_bool_t b;
188
189         assert(i);
190         assert(property);
191         assert(u);
192
193         b = unit_can_start(u) &&
194                 !u->refuse_manual_start;
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_stop(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         /* On the lower levels we assume that every unit we can start
211          * we can also stop */
212
213         b = unit_can_start(u) &&
214                 !u->refuse_manual_stop;
215
216         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
217                 return -ENOMEM;
218
219         return 0;
220 }
221
222 static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
223         Unit *u = data;
224         dbus_bool_t b;
225
226         assert(i);
227         assert(property);
228         assert(u);
229
230         b = unit_can_reload(u);
231
232         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
233                 return -ENOMEM;
234
235         return 0;
236 }
237
238 static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
239         Unit *u = data;
240         dbus_bool_t b;
241
242         assert(i);
243         assert(property);
244         assert(u);
245
246         b = unit_can_isolate(u) &&
247                 !u->refuse_manual_start;
248
249         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
250                 return -ENOMEM;
251
252         return 0;
253 }
254
255 static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
256         Unit *u = data;
257         DBusMessageIter sub;
258         _cleanup_free_ char *p = NULL;
259
260         assert(i);
261         assert(property);
262         assert(u);
263
264         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
265                 return -ENOMEM;
266
267         if (u->job) {
268
269                 p = job_dbus_path(u->job);
270                 if (!p)
271                         return -ENOMEM;
272
273                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
274                     !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
275                         return -ENOMEM;
276         } else {
277                 uint32_t id = 0;
278
279                 /* No job, so let's fill in some placeholder
280                  * data. Since we need to fill in a valid path we
281                  * simple point to ourselves. */
282
283                 p = unit_dbus_path(u);
284                 if (!p)
285                         return -ENOMEM;
286
287                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
288                     !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
289                         return -ENOMEM;
290         }
291
292         if (!dbus_message_iter_close_container(i, &sub))
293                 return -ENOMEM;
294
295         return 0;
296 }
297
298 static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
299         Unit *u = data;
300         dbus_bool_t b;
301
302         assert(i);
303         assert(property);
304         assert(u);
305
306         b = unit_need_daemon_reload(u);
307
308         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
309                 return -ENOMEM;
310
311         return 0;
312 }
313
314 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
315         Unit *u = data;
316         const char *name, *message;
317         DBusMessageIter sub;
318
319         assert(i);
320         assert(property);
321         assert(u);
322
323         if (u->load_error != 0) {
324                 name = bus_errno_to_dbus(u->load_error);
325                 message = strempty(strerror(-u->load_error));
326         } else
327                 name = message = "";
328
329         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
330             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
331             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
332             !dbus_message_iter_close_container(i, &sub))
333                 return -ENOMEM;
334
335         return 0;
336 }
337
338 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
339         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
340         DBusError error;
341         JobType job_type = _JOB_TYPE_INVALID;
342         bool reload_if_possible = false;
343         int r;
344
345         dbus_error_init(&error);
346
347         if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
348                 job_type = JOB_START;
349         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
350                 job_type = JOB_STOP;
351         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
352                 job_type = JOB_RELOAD;
353         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
354                 job_type = JOB_RESTART;
355         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
356                 job_type = JOB_TRY_RESTART;
357         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
358                 reload_if_possible = true;
359                 job_type = JOB_RESTART;
360         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
361                 reload_if_possible = true;
362                 job_type = JOB_TRY_RESTART;
363         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
364                 const char *swho;
365                 int32_t signo;
366                 KillWho who;
367
368                 if (!dbus_message_get_args(
369                                     message,
370                                     &error,
371                                     DBUS_TYPE_STRING, &swho,
372                                     DBUS_TYPE_INT32, &signo,
373                                     DBUS_TYPE_INVALID))
374                         return bus_send_error_reply(connection, message, &error, -EINVAL);
375
376                 if (isempty(swho))
377                         who = KILL_ALL;
378                 else {
379                         who = kill_who_from_string(swho);
380                         if (who < 0)
381                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
382                 }
383
384                 if (signo <= 0 || signo >= _NSIG)
385                         return bus_send_error_reply(connection, message, &error, -EINVAL);
386
387                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
388
389                 r = unit_kill(u, who, signo, &error);
390                 if (r < 0)
391                         return bus_send_error_reply(connection, message, &error, r);
392
393                 reply = dbus_message_new_method_return(message);
394                 if (!reply)
395                         goto oom;
396
397         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
398
399                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
400
401                 unit_reset_failed(u);
402
403                 reply = dbus_message_new_method_return(message);
404                 if (!reply)
405                         goto oom;
406         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "SetProperties")) {
407                 DBusMessageIter iter;
408                 dbus_bool_t runtime;
409
410                 if (!dbus_message_iter_init(message, &iter))
411                         goto oom;
412
413                 if (bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0)
414                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
415
416                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
417
418                 r = bus_unit_set_properties(u, &iter, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, &error);
419                 if (r < 0)
420                         return bus_send_error_reply(connection, message, &error, r);
421
422                 reply = dbus_message_new_method_return(message);
423                 if (!reply)
424                         goto oom;
425
426         } else if (UNIT_VTABLE(u)->bus_message_handler)
427                 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
428         else
429                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
430
431         if (job_type != _JOB_TYPE_INVALID) {
432                 const char *smode;
433                 JobMode mode;
434
435                 if (!dbus_message_get_args(
436                                     message,
437                                     &error,
438                                     DBUS_TYPE_STRING, &smode,
439                                     DBUS_TYPE_INVALID))
440                         return bus_send_error_reply(connection, message, &error, -EINVAL);
441
442                 mode = job_mode_from_string(smode);
443                 if (mode < 0) {
444                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
445                         return bus_send_error_reply(connection, message, &error, -EINVAL);
446                 }
447
448                 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
449         }
450
451         if (reply)
452                 if (!bus_maybe_send_reply(connection, message, reply))
453                         goto oom;
454
455         return DBUS_HANDLER_RESULT_HANDLED;
456
457 oom:
458         dbus_error_free(&error);
459         return DBUS_HANDLER_RESULT_NEED_MEMORY;
460 }
461
462 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage  *message, void *data) {
463         Manager *m = data;
464         Unit *u;
465         int r;
466         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
467         DBusError error;
468
469         assert(connection);
470         assert(message);
471         assert(m);
472
473         dbus_error_init(&error);
474
475         if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
476                 /* Be nice to gdbus and return introspection data for our mid-level paths */
477
478                 SELINUX_ACCESS_CHECK(connection, message, "status");
479
480                 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
481                         char *introspection = NULL;
482                         FILE *f;
483                         Iterator i;
484                         const char *k;
485                         size_t size;
486
487                         reply = dbus_message_new_method_return(message);
488                         if (!reply)
489                                 goto oom;
490
491                         /* We roll our own introspection code here, instead of
492                          * relying on bus_default_message_handler() because we
493                          * need to generate our introspection string
494                          * dynamically. */
495
496                         f = open_memstream(&introspection, &size);
497                         if (!f)
498                                 goto oom;
499
500                         fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
501                               "<node>\n", f);
502
503                         fputs(BUS_INTROSPECTABLE_INTERFACE, f);
504                         fputs(BUS_PEER_INTERFACE, f);
505
506                         HASHMAP_FOREACH_KEY(u, k, m->units, i) {
507                                 char *p;
508
509                                 if (k != u->id)
510                                         continue;
511
512                                 p = bus_path_escape(k);
513                                 if (!p) {
514                                         fclose(f);
515                                         free(introspection);
516                                         goto oom;
517                                 }
518
519                                 fprintf(f, "<node name=\"%s\"/>", p);
520                                 free(p);
521                         }
522
523                         fputs("</node>\n", f);
524
525                         if (ferror(f)) {
526                                 fclose(f);
527                                 free(introspection);
528                                 goto oom;
529                         }
530
531                         fclose(f);
532
533                         if (!introspection)
534                                 goto oom;
535
536                         if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
537                                 free(introspection);
538                                 goto oom;
539                         }
540
541                         free(introspection);
542
543                         if (!bus_maybe_send_reply(connection, message, reply))
544                                 goto oom;
545
546                         return DBUS_HANDLER_RESULT_HANDLED;
547                 }
548
549                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
550         }
551
552         r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
553         if (r == -ENOMEM)
554                 goto oom;
555         if (r < 0)
556                 return bus_send_error_reply(connection, message, &error, r);
557
558         return bus_unit_message_dispatch(u, connection, message);
559
560 oom:
561         dbus_error_free(&error);
562
563         return DBUS_HANDLER_RESULT_NEED_MEMORY;
564 }
565
566 const DBusObjectPathVTable bus_unit_vtable = {
567         .message_function = bus_unit_message_handler
568 };
569
570 void bus_unit_send_change_signal(Unit *u) {
571         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
572         _cleanup_free_ char *p = NULL;
573         int r;
574
575         assert(u);
576
577         if (u->in_dbus_queue) {
578                 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
579                 u->in_dbus_queue = false;
580         }
581
582         if (!u->id)
583                 return;
584
585         if (!bus_has_subscriber(u->manager)) {
586                 u->sent_dbus_new_signal = true;
587                 return;
588         }
589
590         p = unit_dbus_path(u);
591         if (!p) {
592                 log_oom();
593                 return;
594         }
595
596         if (u->sent_dbus_new_signal) {
597                 /* Send a properties changed signal. First for the
598                  * specific type, then for the generic unit. The
599                  * clients may rely on this order to get atomic
600                  * behavior if needed. */
601
602                 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
603
604                         m = bus_properties_changed_new(p,
605                                                        UNIT_VTABLE(u)->bus_interface,
606                                                        UNIT_VTABLE(u)->bus_invalidating_properties);
607                         if (!m) {
608                                 log_oom();
609                                 return;
610                         }
611
612                         r = bus_broadcast(u->manager, m);
613                         if (r < 0) {
614                                 log_error("Failed to broadcast change message: %s", strerror(-r));
615                                 return;
616                         }
617
618                         dbus_message_unref(m);
619                 }
620
621                 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
622                                                INVALIDATING_PROPERTIES);
623                 if (!m) {
624                         log_oom();
625                         return;
626                 }
627
628         } else {
629                 /* Send a new signal */
630
631                 m = dbus_message_new_signal("/org/freedesktop/systemd1",
632                                             "org.freedesktop.systemd1.Manager",
633                                             "UnitNew");
634                 if (!m) {
635                         log_oom();
636                         return;
637                 }
638
639                 if (!dbus_message_append_args(m,
640                                               DBUS_TYPE_STRING, &u->id,
641                                               DBUS_TYPE_OBJECT_PATH, &p,
642                                               DBUS_TYPE_INVALID)) {
643                         log_oom();
644                         return;
645                 }
646         }
647
648         r = bus_broadcast(u->manager, m);
649         if (r < 0) {
650                 log_error("Failed to broadcast UnitNew/PropertiesChanged message.");
651                 return;
652         }
653
654         u->sent_dbus_new_signal = true;
655 }
656
657 void bus_unit_send_removed_signal(Unit *u) {
658         _cleanup_free_ char *p = NULL;
659         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
660
661         assert(u);
662
663         if (!bus_has_subscriber(u->manager))
664                 return;
665
666         if (!u->sent_dbus_new_signal)
667                 bus_unit_send_change_signal(u);
668
669         if (!u->id)
670                 return;
671
672         p = unit_dbus_path(u);
673         if (!p)
674                 goto oom;
675
676         m = dbus_message_new_signal("/org/freedesktop/systemd1",
677                                     "org.freedesktop.systemd1.Manager",
678                                     "UnitRemoved");
679         if (!m)
680                 goto oom;
681
682         if (!dbus_message_append_args(m,
683                                       DBUS_TYPE_STRING, &u->id,
684                                       DBUS_TYPE_OBJECT_PATH, &p,
685                                       DBUS_TYPE_INVALID))
686                 goto oom;
687
688         if (bus_broadcast(u->manager, m) < 0)
689                 goto oom;
690
691         return;
692
693 oom:
694         log_oom();
695 }
696
697 DBusHandlerResult bus_unit_queue_job(
698                 DBusConnection *connection,
699                 DBusMessage *message,
700                 Unit *u,
701                 JobType type,
702                 JobMode mode,
703                 bool reload_if_possible) {
704
705         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
706         _cleanup_free_ char *path = NULL;
707         Job *j;
708         JobBusClient *cl;
709         DBusError error;
710         int r;
711
712         assert(connection);
713         assert(message);
714         assert(u);
715         assert(type >= 0 && type < _JOB_TYPE_MAX);
716         assert(mode >= 0 && mode < _JOB_MODE_MAX);
717
718         dbus_error_init(&error);
719
720         if (reload_if_possible && unit_can_reload(u)) {
721                 if (type == JOB_RESTART)
722                         type = JOB_RELOAD_OR_START;
723                 else if (type == JOB_TRY_RESTART)
724                         type = JOB_RELOAD;
725         }
726
727         SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
728                                   (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
729                                   type == JOB_STOP ? "stop" : "reload");
730
731         if (type == JOB_STOP && (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && unit_active_state(u) == UNIT_INACTIVE) {
732                 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
733                 return bus_send_error_reply(connection, message, &error, -EPERM);
734         }
735
736         if ((type == JOB_START && u->refuse_manual_start) ||
737             (type == JOB_STOP && u->refuse_manual_stop) ||
738             ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
739                 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
740                                "Operation refused, unit %s may be requested by dependency only.", u->id);
741                 return bus_send_error_reply(connection, message, &error, -EPERM);
742         }
743
744         r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
745         if (r < 0)
746                 return bus_send_error_reply(connection, message, &error, r);
747
748         cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
749         if (!cl)
750                 goto oom;
751
752         LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
753
754         reply = dbus_message_new_method_return(message);
755         if (!reply)
756                 goto oom;
757
758         path = job_dbus_path(j);
759         if (!path)
760                 goto oom;
761
762         if (!dbus_message_append_args(
763                             reply,
764                             DBUS_TYPE_OBJECT_PATH, &path,
765                             DBUS_TYPE_INVALID))
766                 goto oom;
767
768         if (!bus_maybe_send_reply(connection, message, reply))
769                 goto oom;
770
771         return DBUS_HANDLER_RESULT_HANDLED;
772
773 oom:
774         dbus_error_free(&error);
775
776         return DBUS_HANDLER_RESULT_NEED_MEMORY;
777 }
778
779 static int bus_unit_set_transient_property(
780                 Unit *u,
781                 const char *name,
782                 DBusMessageIter *i,
783                 UnitSetPropertiesMode mode,
784                 DBusError *error) {
785
786         int r;
787
788         assert(u);
789         assert(name);
790         assert(i);
791
792         if (streq(name, "Description")) {
793                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
794                         return -EINVAL;
795
796                 if (mode != UNIT_CHECK) {
797                         _cleanup_free_ char *contents = NULL;
798                         const char *description;
799
800                         dbus_message_iter_get_basic(i, &description);
801
802                         r = unit_set_description(u, description);
803                         if (r < 0)
804                                 return r;
805
806                         contents = strjoin("[Unit]\nDescription=", description, "\n", NULL);
807                         if (!contents)
808                                 return -ENOMEM;
809
810                         unit_write_drop_in(u, mode, "Description", contents);
811                 }
812
813                 return 1;
814
815         } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) {
816                 const char *s;
817                 Unit *slice;
818
819                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
820                         return -EINVAL;
821
822                 dbus_message_iter_get_basic(i, &s);
823
824                 if (isempty(s)) {
825                         if (mode != UNIT_CHECK)
826                                 unit_ref_unset(&u->slice);
827                 } else {
828                         _cleanup_free_ char *contents = NULL;
829
830                         r = manager_load_unit(u->manager, s, NULL, error, &slice);
831                         if (r < 0)
832                                 return r;
833
834                         if (slice->type != UNIT_SLICE)
835                                 return -EINVAL;
836
837                         if (mode != UNIT_CHECK)
838                                 unit_ref_set(&u->slice, slice);
839
840                         contents = strjoin("[", UNIT_VTABLE(u)->private_section, "]\nSlice=", s, NULL);
841                         if (!contents)
842                                 return -ENOMEM;
843
844                         unit_write_drop_in(u, mode, "Slice", contents);
845                 }
846                 return 1;
847
848         } else if (streq(name, "Requires") ||
849                    streq(name, "RequiresOverridable") ||
850                    streq(name, "Requisite") ||
851                    streq(name, "RequisiteOverridable") ||
852                    streq(name, "Wants") ||
853                    streq(name, "BindsTo") ||
854                    streq(name, "Conflicts") ||
855                    streq(name, "Before") ||
856                    streq(name, "After") ||
857                    streq(name, "OnFailure") ||
858                    streq(name, "PropagatesReloadTo") ||
859                    streq(name, "ReloadPropagatedFrom") ||
860                    streq(name, "PartOf")) {
861
862                 UnitDependency d;
863                 DBusMessageIter sub;
864
865                 d = unit_dependency_from_string(name);
866                 if (d < 0)
867                         return -EINVAL;
868
869                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
870                     dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRING)
871                         return -EINVAL;
872
873                 dbus_message_iter_recurse(i, &sub);
874                 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
875                         const char *other;
876
877                         dbus_message_iter_get_basic(&sub, &other);
878
879                         if (!unit_name_is_valid(other, false))
880                                 return -EINVAL;
881
882                         if (mode != UNIT_CHECK) {
883                                 _cleanup_free_ char *label = NULL, *contents = NULL;
884
885                                 r = unit_add_dependency_by_name(u, d, other, NULL, true);
886                                 if (r < 0)
887                                         return r;
888
889                                 label = strjoin(name, "-", other, NULL);
890                                 if (!label)
891                                         return -ENOMEM;
892
893                                 contents = strjoin("[Unit]\n", name, "=", other, "\n", NULL);
894                                 if (!contents)
895                                         return -ENOMEM;
896
897                                 unit_write_drop_in(u, mode, label, contents);
898                         }
899
900                         dbus_message_iter_next(&sub);
901                 }
902
903                 return 1;
904         }
905
906         return 0;
907 }
908
909 int bus_unit_set_properties(
910                 Unit *u,
911                 DBusMessageIter *iter,
912                 UnitSetPropertiesMode mode,
913                 bool commit,
914                 DBusError *error) {
915
916         bool for_real = false;
917         DBusMessageIter sub;
918         unsigned n = 0;
919         int r;
920
921         assert(u);
922         assert(iter);
923
924         if (u->transient)
925                 mode &= UNIT_RUNTIME;
926
927         /* We iterate through the array twice. First run we just check
928          * if all passed data is valid, second run actually applies
929          * it. This is to implement transaction-like behaviour without
930          * actually providing full transactions. */
931
932         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
933             dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
934                 return -EINVAL;
935
936         dbus_message_iter_recurse(iter, &sub);
937         for (;;) {
938                 DBusMessageIter sub2, sub3;
939                 const char *name;
940
941                 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_INVALID) {
942
943                         if (for_real)
944                                 break;
945
946                         /* Reached EOF. Let's try again, and this time for realz... */
947                         dbus_message_iter_recurse(iter, &sub);
948                         for_real = true;
949                         continue;
950                 }
951
952                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT)
953                         return -EINVAL;
954
955                 dbus_message_iter_recurse(&sub, &sub2);
956
957                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
958                     dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)
959                         return -EINVAL;
960
961                 if (!UNIT_VTABLE(u)->bus_set_property) {
962                         dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties.");
963                         return -ENOENT;
964                 }
965
966                 dbus_message_iter_recurse(&sub2, &sub3);
967                 r = UNIT_VTABLE(u)->bus_set_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
968                 if (r == 0 && u->transient && u->load_state == UNIT_STUB)
969                         r = bus_unit_set_transient_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
970                 if (r < 0)
971                         return r;
972                 if (r == 0) {
973                         dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name);
974                         return -ENOENT;
975                 }
976
977                 dbus_message_iter_next(&sub);
978
979                 n += for_real;
980         }
981
982         if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties)
983                 UNIT_VTABLE(u)->bus_commit_properties(u);
984
985         return n;
986 }
987
988 const BusProperty bus_unit_properties[] = {
989         { "Id",                   bus_property_append_string,         "s", offsetof(Unit, id),                                         true },
990         { "Names",                bus_unit_append_names,             "as", 0 },
991         { "Following",            bus_unit_append_following,          "s", 0 },
992         { "Requires",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES]),                true },
993         { "RequiresOverridable",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]),    true },
994         { "Requisite",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE]),               true },
995         { "RequisiteOverridable", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]),   true },
996         { "Wants",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTS]),                   true },
997         { "BindsTo",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]),                true },
998         { "PartOf",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PART_OF]),                 true },
999         { "RequiredBy",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]),             true },
1000         { "RequiredByOverridable",bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
1001         { "WantedBy",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]),               true },
1002         { "BoundBy",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]),                true },
1003         { "ConsistsOf",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]),             true },
1004         { "Conflicts",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]),               true },
1005         { "ConflictedBy",         bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]),           true },
1006         { "Before",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BEFORE]),                  true },
1007         { "After",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_AFTER]),                   true },
1008         { "OnFailure",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]),              true },
1009         { "Triggers",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]),                true },
1010         { "TriggeredBy",          bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]),            true },
1011         { "PropagatesReloadTo",   bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]),    true },
1012         { "ReloadPropagatedFrom", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]),  true },
1013         { "RequiresMountsFor",    bus_property_append_strv,          "as", offsetof(Unit, requires_mounts_for),                        true },
1014         { "Documentation",        bus_property_append_strv,          "as", offsetof(Unit, documentation),                              true },
1015         { "Description",          bus_unit_append_description,        "s", 0 },
1016         { "LoadState",            bus_unit_append_load_state,         "s", offsetof(Unit, load_state)                         },
1017         { "ActiveState",          bus_unit_append_active_state,       "s", 0 },
1018         { "SubState",             bus_unit_append_sub_state,          "s", 0 },
1019         { "FragmentPath",         bus_property_append_string,         "s", offsetof(Unit, fragment_path),                              true },
1020         { "SourcePath",           bus_property_append_string,         "s", offsetof(Unit, source_path),                                true },
1021         { "DropInPaths",          bus_property_append_strv,          "as", offsetof(Unit, dropin_paths),                               true },
1022         { "UnitFileState",        bus_unit_append_file_state,         "s", 0 },
1023         { "InactiveExitTimestamp",bus_property_append_usec,           "t", offsetof(Unit, inactive_exit_timestamp.realtime)   },
1024         { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic)  },
1025         { "ActiveEnterTimestamp", bus_property_append_usec,           "t", offsetof(Unit, active_enter_timestamp.realtime)    },
1026         { "ActiveEnterTimestampMonotonic", bus_property_append_usec,  "t", offsetof(Unit, active_enter_timestamp.monotonic)   },
1027         { "ActiveExitTimestamp",  bus_property_append_usec,           "t", offsetof(Unit, active_exit_timestamp.realtime)     },
1028         { "ActiveExitTimestampMonotonic",  bus_property_append_usec,  "t", offsetof(Unit, active_exit_timestamp.monotonic)    },
1029         { "InactiveEnterTimestamp", bus_property_append_usec,         "t", offsetof(Unit, inactive_enter_timestamp.realtime)  },
1030         { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
1031         { "CanStart",             bus_unit_append_can_start,          "b", 0 },
1032         { "CanStop",              bus_unit_append_can_stop,           "b", 0 },
1033         { "CanReload",            bus_unit_append_can_reload,         "b", 0 },
1034         { "CanIsolate",           bus_unit_append_can_isolate,        "b", 0 },
1035         { "Job",                  bus_unit_append_job,             "(uo)", 0 },
1036         { "StopWhenUnneeded",     bus_property_append_bool,           "b", offsetof(Unit, stop_when_unneeded)                 },
1037         { "RefuseManualStart",    bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_start)                },
1038         { "RefuseManualStop",     bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_stop)                 },
1039         { "AllowIsolate",         bus_property_append_bool,           "b", offsetof(Unit, allow_isolate)                      },
1040         { "DefaultDependencies",  bus_property_append_bool,           "b", offsetof(Unit, default_dependencies)               },
1041         { "OnFailureIsolate",     bus_property_append_bool,           "b", offsetof(Unit, on_failure_isolate)                 },
1042         { "IgnoreOnIsolate",      bus_property_append_bool,           "b", offsetof(Unit, ignore_on_isolate)                  },
1043         { "IgnoreOnSnapshot",     bus_property_append_bool,           "b", offsetof(Unit, ignore_on_snapshot)                 },
1044         { "NeedDaemonReload",     bus_unit_append_need_daemon_reload, "b", 0 },
1045         { "JobTimeoutUSec",       bus_property_append_usec,           "t", offsetof(Unit, job_timeout)                        },
1046         { "ConditionTimestamp",   bus_property_append_usec,           "t", offsetof(Unit, condition_timestamp.realtime)       },
1047         { "ConditionTimestampMonotonic", bus_property_append_usec,    "t", offsetof(Unit, condition_timestamp.monotonic)      },
1048         { "ConditionResult",      bus_property_append_bool,           "b", offsetof(Unit, condition_result)                   },
1049         { "LoadError",            bus_unit_append_load_error,      "(ss)", 0 },
1050         { "Transient",            bus_property_append_bool,           "b", offsetof(Unit, transient)                          },
1051         { NULL, }
1052 };
1053
1054 const BusProperty bus_unit_cgroup_properties[] = {
1055         { "Slice",                bus_unit_append_slice,              "s", 0 },
1056         { "ControlGroup",         bus_property_append_string,         "s", offsetof(Unit, cgroup_path),                                true },
1057 };