chiark / gitweb /
6273e4627432e4d8cdba832583de3c4736b2c04b
[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_free_ char *p = NULL;
572         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
573
574         assert(u);
575
576         if (u->in_dbus_queue) {
577                 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
578                 u->in_dbus_queue = false;
579         }
580
581         if (!u->id)
582                 return;
583
584         if (!bus_has_subscriber(u->manager)) {
585                 u->sent_dbus_new_signal = true;
586                 return;
587         }
588
589         p = unit_dbus_path(u);
590         if (!p)
591                 goto oom;
592
593         if (u->sent_dbus_new_signal) {
594                 /* Send a properties changed signal. First for the
595                  * specific type, then for the generic unit. The
596                  * clients may rely on this order to get atomic
597                  * behavior if needed. */
598
599                 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
600
601                         m = bus_properties_changed_new(p,
602                                                        UNIT_VTABLE(u)->bus_interface,
603                                                        UNIT_VTABLE(u)->bus_invalidating_properties);
604                         if (!m)
605                                 goto oom;
606
607                         if (bus_broadcast(u->manager, m) < 0)
608                                 goto oom;
609
610                         dbus_message_unref(m);
611                 }
612
613                 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
614                                                INVALIDATING_PROPERTIES);
615                 if (!m)
616                         goto oom;
617
618         } else {
619                 /* Send a new signal */
620
621                 m = dbus_message_new_signal("/org/freedesktop/systemd1",
622                                             "org.freedesktop.systemd1.Manager",
623                                             "UnitNew");
624                 if (!m)
625                         goto oom;
626
627                 if (!dbus_message_append_args(m,
628                                               DBUS_TYPE_STRING, &u->id,
629                                               DBUS_TYPE_OBJECT_PATH, &p,
630                                               DBUS_TYPE_INVALID))
631                         goto oom;
632         }
633
634         if (bus_broadcast(u->manager, m) < 0)
635                 goto oom;
636
637         u->sent_dbus_new_signal = true;
638
639         return;
640
641 oom:
642         log_oom();
643 }
644
645 void bus_unit_send_removed_signal(Unit *u) {
646         _cleanup_free_ char *p = NULL;
647         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
648
649         assert(u);
650
651         if (!bus_has_subscriber(u->manager))
652                 return;
653
654         if (!u->sent_dbus_new_signal)
655                 bus_unit_send_change_signal(u);
656
657         if (!u->id)
658                 return;
659
660         p = unit_dbus_path(u);
661         if (!p)
662                 goto oom;
663
664         m = dbus_message_new_signal("/org/freedesktop/systemd1",
665                                     "org.freedesktop.systemd1.Manager",
666                                     "UnitRemoved");
667         if (!m)
668                 goto oom;
669
670         if (!dbus_message_append_args(m,
671                                       DBUS_TYPE_STRING, &u->id,
672                                       DBUS_TYPE_OBJECT_PATH, &p,
673                                       DBUS_TYPE_INVALID))
674                 goto oom;
675
676         if (bus_broadcast(u->manager, m) < 0)
677                 goto oom;
678
679         return;
680
681 oom:
682         log_oom();
683 }
684
685 DBusHandlerResult bus_unit_queue_job(
686                 DBusConnection *connection,
687                 DBusMessage *message,
688                 Unit *u,
689                 JobType type,
690                 JobMode mode,
691                 bool reload_if_possible) {
692
693         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
694         _cleanup_free_ char *path = NULL;
695         Job *j;
696         JobBusClient *cl;
697         DBusError error;
698         int r;
699
700         assert(connection);
701         assert(message);
702         assert(u);
703         assert(type >= 0 && type < _JOB_TYPE_MAX);
704         assert(mode >= 0 && mode < _JOB_MODE_MAX);
705
706         dbus_error_init(&error);
707
708         if (reload_if_possible && unit_can_reload(u)) {
709                 if (type == JOB_RESTART)
710                         type = JOB_RELOAD_OR_START;
711                 else if (type == JOB_TRY_RESTART)
712                         type = JOB_RELOAD;
713         }
714
715         SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
716                                   (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
717                                   type == JOB_STOP ? "stop" : "reload");
718
719         if (type == JOB_STOP && (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && unit_active_state(u) == UNIT_INACTIVE) {
720                 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
721                 return bus_send_error_reply(connection, message, &error, -EPERM);
722         }
723
724         if ((type == JOB_START && u->refuse_manual_start) ||
725             (type == JOB_STOP && u->refuse_manual_stop) ||
726             ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
727                 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
728                                "Operation refused, unit %s may be requested by dependency only.", u->id);
729                 return bus_send_error_reply(connection, message, &error, -EPERM);
730         }
731
732         r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
733         if (r < 0)
734                 return bus_send_error_reply(connection, message, &error, r);
735
736         cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
737         if (!cl)
738                 goto oom;
739
740         LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
741
742         reply = dbus_message_new_method_return(message);
743         if (!reply)
744                 goto oom;
745
746         path = job_dbus_path(j);
747         if (!path)
748                 goto oom;
749
750         if (!dbus_message_append_args(
751                             reply,
752                             DBUS_TYPE_OBJECT_PATH, &path,
753                             DBUS_TYPE_INVALID))
754                 goto oom;
755
756         if (!bus_maybe_send_reply(connection, message, reply))
757                 goto oom;
758
759         return DBUS_HANDLER_RESULT_HANDLED;
760
761 oom:
762         dbus_error_free(&error);
763
764         return DBUS_HANDLER_RESULT_NEED_MEMORY;
765 }
766
767 static int bus_unit_set_transient_property(
768                 Unit *u,
769                 const char *name,
770                 DBusMessageIter *i,
771                 UnitSetPropertiesMode mode,
772                 DBusError *error) {
773
774         int r;
775
776         assert(u);
777         assert(name);
778         assert(i);
779
780         if (streq(name, "Description")) {
781                 const char *description;
782
783                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
784                         return -EINVAL;
785
786                 dbus_message_iter_get_basic(i, &description);
787
788                 r = unit_set_description(u, description);
789                 if (r < 0)
790                         return r;
791
792                 return 1;
793         }
794
795         return 0;
796 }
797
798 int bus_unit_set_properties(
799                 Unit *u,
800                 DBusMessageIter *iter,
801                 UnitSetPropertiesMode mode,
802                 bool commit,
803                 DBusError *error) {
804
805         bool for_real = false;
806         DBusMessageIter sub;
807         unsigned n = 0;
808         int r;
809
810         assert(u);
811         assert(iter);
812
813         if (u->transient)
814                 mode &= UNIT_RUNTIME;
815
816         /* We iterate through the array twice. First run we just check
817          * if all passed data is valid, second run actually applies
818          * it. This is to implement transaction-like behaviour without
819          * actually providing full transactions. */
820
821         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
822             dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
823                 return -EINVAL;
824
825         dbus_message_iter_recurse(iter, &sub);
826         for (;;) {
827                 DBusMessageIter sub2, sub3;
828                 const char *name;
829
830                 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_INVALID) {
831
832                         if (for_real)
833                                 break;
834
835                         /* Reached EOF. Let's try again, and this time for realz... */
836                         dbus_message_iter_recurse(iter, &sub);
837                         for_real = true;
838                         continue;
839                 }
840
841                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT)
842                         return -EINVAL;
843
844                 dbus_message_iter_recurse(&sub, &sub2);
845
846                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
847                     dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)
848                         return -EINVAL;
849
850                 if (!UNIT_VTABLE(u)->bus_set_property) {
851                         dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties.");
852                         return -ENOENT;
853                 }
854
855                 dbus_message_iter_recurse(&sub2, &sub3);
856                 r = UNIT_VTABLE(u)->bus_set_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
857                 if (r == 0 && u->transient && u->load_state == UNIT_STUB)
858                         r = bus_unit_set_transient_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
859                 if (r < 0)
860                         return r;
861                 if (r == 0) {
862                         dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name);
863                         return -ENOENT;
864                 }
865
866                 dbus_message_iter_next(&sub);
867
868                 n += for_real;
869         }
870
871         if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties)
872                 UNIT_VTABLE(u)->bus_commit_properties(u);
873
874         return n;
875 }
876
877 const BusProperty bus_unit_properties[] = {
878         { "Id",                   bus_property_append_string,         "s", offsetof(Unit, id),                                         true },
879         { "Names",                bus_unit_append_names,             "as", 0 },
880         { "Following",            bus_unit_append_following,          "s", 0 },
881         { "Requires",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES]),                true },
882         { "RequiresOverridable",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]),    true },
883         { "Requisite",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE]),               true },
884         { "RequisiteOverridable", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]),   true },
885         { "Wants",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTS]),                   true },
886         { "BindsTo",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]),                true },
887         { "PartOf",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PART_OF]),                 true },
888         { "RequiredBy",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]),             true },
889         { "RequiredByOverridable",bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
890         { "WantedBy",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]),               true },
891         { "BoundBy",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]),                true },
892         { "ConsistsOf",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]),             true },
893         { "Conflicts",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]),               true },
894         { "ConflictedBy",         bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]),           true },
895         { "Before",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BEFORE]),                  true },
896         { "After",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_AFTER]),                   true },
897         { "OnFailure",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]),              true },
898         { "Triggers",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]),                true },
899         { "TriggeredBy",          bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]),            true },
900         { "PropagatesReloadTo",   bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]),    true },
901         { "ReloadPropagatedFrom", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]),  true },
902         { "RequiresMountsFor",    bus_property_append_strv,          "as", offsetof(Unit, requires_mounts_for),                        true },
903         { "Documentation",        bus_property_append_strv,          "as", offsetof(Unit, documentation),                              true },
904         { "Description",          bus_unit_append_description,        "s", 0 },
905         { "LoadState",            bus_unit_append_load_state,         "s", offsetof(Unit, load_state)                         },
906         { "ActiveState",          bus_unit_append_active_state,       "s", 0 },
907         { "SubState",             bus_unit_append_sub_state,          "s", 0 },
908         { "FragmentPath",         bus_property_append_string,         "s", offsetof(Unit, fragment_path),                              true },
909         { "SourcePath",           bus_property_append_string,         "s", offsetof(Unit, source_path),                                true },
910         { "DropInPaths",          bus_property_append_strv,          "as", offsetof(Unit, dropin_paths),                               true },
911         { "UnitFileState",        bus_unit_append_file_state,         "s", 0 },
912         { "InactiveExitTimestamp",bus_property_append_usec,           "t", offsetof(Unit, inactive_exit_timestamp.realtime)   },
913         { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic)  },
914         { "ActiveEnterTimestamp", bus_property_append_usec,           "t", offsetof(Unit, active_enter_timestamp.realtime)    },
915         { "ActiveEnterTimestampMonotonic", bus_property_append_usec,  "t", offsetof(Unit, active_enter_timestamp.monotonic)   },
916         { "ActiveExitTimestamp",  bus_property_append_usec,           "t", offsetof(Unit, active_exit_timestamp.realtime)     },
917         { "ActiveExitTimestampMonotonic",  bus_property_append_usec,  "t", offsetof(Unit, active_exit_timestamp.monotonic)    },
918         { "InactiveEnterTimestamp", bus_property_append_usec,         "t", offsetof(Unit, inactive_enter_timestamp.realtime)  },
919         { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
920         { "CanStart",             bus_unit_append_can_start,          "b", 0 },
921         { "CanStop",              bus_unit_append_can_stop,           "b", 0 },
922         { "CanReload",            bus_unit_append_can_reload,         "b", 0 },
923         { "CanIsolate",           bus_unit_append_can_isolate,        "b", 0 },
924         { "Job",                  bus_unit_append_job,             "(uo)", 0 },
925         { "StopWhenUnneeded",     bus_property_append_bool,           "b", offsetof(Unit, stop_when_unneeded)                 },
926         { "RefuseManualStart",    bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_start)                },
927         { "RefuseManualStop",     bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_stop)                 },
928         { "AllowIsolate",         bus_property_append_bool,           "b", offsetof(Unit, allow_isolate)                      },
929         { "DefaultDependencies",  bus_property_append_bool,           "b", offsetof(Unit, default_dependencies)               },
930         { "OnFailureIsolate",     bus_property_append_bool,           "b", offsetof(Unit, on_failure_isolate)                 },
931         { "IgnoreOnIsolate",      bus_property_append_bool,           "b", offsetof(Unit, ignore_on_isolate)                  },
932         { "IgnoreOnSnapshot",     bus_property_append_bool,           "b", offsetof(Unit, ignore_on_snapshot)                 },
933         { "NeedDaemonReload",     bus_unit_append_need_daemon_reload, "b", 0 },
934         { "JobTimeoutUSec",       bus_property_append_usec,           "t", offsetof(Unit, job_timeout)                        },
935         { "ConditionTimestamp",   bus_property_append_usec,           "t", offsetof(Unit, condition_timestamp.realtime)       },
936         { "ConditionTimestampMonotonic", bus_property_append_usec,    "t", offsetof(Unit, condition_timestamp.monotonic)      },
937         { "ConditionResult",      bus_property_append_bool,           "b", offsetof(Unit, condition_result)                   },
938         { "LoadError",            bus_unit_append_load_error,      "(ss)", 0 },
939         { "Transient",            bus_property_append_bool,           "b", offsetof(Unit, transient)                          },
940         { NULL, }
941 };
942
943 const BusProperty bus_unit_cgroup_properties[] = {
944         { "Slice",                bus_unit_append_slice,              "s", 0 },
945         { "ControlGroup",         bus_property_append_string,         "s", offsetof(Unit, cgroup_path),                                true },
946 };