chiark / gitweb /
bus: remove static introspection file export
[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[] = 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_property_append_condition(DBusMessageIter *i, const char *property, void *data) {
315         Condition **cp = data;
316         Condition *c;
317         const char *name, *param;
318         dbus_bool_t trigger, negate;
319         dbus_int32_t state;
320         DBusMessageIter sub;
321
322         assert(i);
323         assert(property);
324         assert(cp);
325
326         c = *cp;
327         assert(c);
328
329         name = condition_type_to_string(c->type);
330         param = c->parameter;
331         trigger = c->trigger;
332         negate = c->negate;
333         state = c->state;
334
335         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
336             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
337             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &trigger) ||
338             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &negate) ||
339             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &param) ||
340             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &state) ||
341             !dbus_message_iter_close_container(i, &sub))
342                 return -ENOMEM;
343
344         return 0;
345 }
346
347 static int bus_property_append_condition_list(DBusMessageIter *i, const char *property, void *data) {
348         Condition **first = data, *c;
349         DBusMessageIter sub;
350
351         assert(i);
352         assert(data);
353
354         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sbbsi)", &sub))
355                 return -ENOMEM;
356
357         LIST_FOREACH(conditions, c, *first)
358                 bus_property_append_condition(&sub, property, &c);
359
360         if (!dbus_message_iter_close_container(i, &sub))
361                 return -ENOMEM;
362
363         return 0;
364 }
365
366 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
367         Unit *u = data;
368         const char *name, *message;
369         DBusMessageIter sub;
370
371         assert(i);
372         assert(property);
373         assert(u);
374
375         if (u->load_error != 0) {
376                 name = bus_errno_to_dbus(u->load_error);
377                 message = strempty(strerror(-u->load_error));
378         } else
379                 name = message = "";
380
381         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
382             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
383             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
384             !dbus_message_iter_close_container(i, &sub))
385                 return -ENOMEM;
386
387         return 0;
388 }
389
390 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
391         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
392         DBusError error;
393         JobType job_type = _JOB_TYPE_INVALID;
394         bool reload_if_possible = false;
395         int r;
396
397         dbus_error_init(&error);
398
399         if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
400                 job_type = JOB_START;
401         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
402                 job_type = JOB_STOP;
403         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
404                 job_type = JOB_RELOAD;
405         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
406                 job_type = JOB_RESTART;
407         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
408                 job_type = JOB_TRY_RESTART;
409         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
410                 reload_if_possible = true;
411                 job_type = JOB_RESTART;
412         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
413                 reload_if_possible = true;
414                 job_type = JOB_TRY_RESTART;
415         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
416                 const char *swho;
417                 int32_t signo;
418                 KillWho who;
419
420                 if (!dbus_message_get_args(
421                                     message,
422                                     &error,
423                                     DBUS_TYPE_STRING, &swho,
424                                     DBUS_TYPE_INT32, &signo,
425                                     DBUS_TYPE_INVALID))
426                         return bus_send_error_reply(connection, message, &error, -EINVAL);
427
428                 if (isempty(swho))
429                         who = KILL_ALL;
430                 else {
431                         who = kill_who_from_string(swho);
432                         if (who < 0)
433                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
434                 }
435
436                 if (signo <= 0 || signo >= _NSIG)
437                         return bus_send_error_reply(connection, message, &error, -EINVAL);
438
439                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
440
441                 r = unit_kill(u, who, signo, &error);
442                 if (r < 0)
443                         return bus_send_error_reply(connection, message, &error, r);
444
445                 reply = dbus_message_new_method_return(message);
446                 if (!reply)
447                         goto oom;
448
449         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
450
451                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
452
453                 unit_reset_failed(u);
454
455                 reply = dbus_message_new_method_return(message);
456                 if (!reply)
457                         goto oom;
458         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "SetProperties")) {
459                 DBusMessageIter iter;
460                 dbus_bool_t runtime;
461
462                 if (!dbus_message_iter_init(message, &iter))
463                         goto oom;
464
465                 if (bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0)
466                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
467
468                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
469
470                 r = bus_unit_set_properties(u, &iter, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, &error);
471                 if (r < 0)
472                         return bus_send_error_reply(connection, message, &error, r);
473
474                 reply = dbus_message_new_method_return(message);
475                 if (!reply)
476                         goto oom;
477
478         } else if (UNIT_VTABLE(u)->bus_message_handler)
479                 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
480         else
481                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
482
483         if (job_type != _JOB_TYPE_INVALID) {
484                 const char *smode;
485                 JobMode mode;
486
487                 if (!dbus_message_get_args(
488                                     message,
489                                     &error,
490                                     DBUS_TYPE_STRING, &smode,
491                                     DBUS_TYPE_INVALID))
492                         return bus_send_error_reply(connection, message, &error, -EINVAL);
493
494                 mode = job_mode_from_string(smode);
495                 if (mode < 0) {
496                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
497                         return bus_send_error_reply(connection, message, &error, -EINVAL);
498                 }
499
500                 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
501         }
502
503         if (reply)
504                 if (!bus_maybe_send_reply(connection, message, reply))
505                         goto oom;
506
507         return DBUS_HANDLER_RESULT_HANDLED;
508
509 oom:
510         dbus_error_free(&error);
511         return DBUS_HANDLER_RESULT_NEED_MEMORY;
512 }
513
514 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage  *message, void *data) {
515         Manager *m = data;
516         Unit *u;
517         int r;
518         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
519         DBusError error;
520
521         assert(connection);
522         assert(message);
523         assert(m);
524
525         dbus_error_init(&error);
526
527         if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
528                 /* Be nice to gdbus and return introspection data for our mid-level paths */
529
530                 SELINUX_ACCESS_CHECK(connection, message, "status");
531
532                 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
533                         char *introspection = NULL;
534                         FILE *f;
535                         Iterator i;
536                         const char *k;
537                         size_t size;
538
539                         reply = dbus_message_new_method_return(message);
540                         if (!reply)
541                                 goto oom;
542
543                         /* We roll our own introspection code here, instead of
544                          * relying on bus_default_message_handler() because we
545                          * need to generate our introspection string
546                          * dynamically. */
547
548                         f = open_memstream(&introspection, &size);
549                         if (!f)
550                                 goto oom;
551
552                         fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
553                               "<node>\n", f);
554
555                         fputs(BUS_INTROSPECTABLE_INTERFACE, f);
556                         fputs(BUS_PEER_INTERFACE, f);
557
558                         HASHMAP_FOREACH_KEY(u, k, m->units, i) {
559                                 char *p;
560
561                                 if (k != u->id)
562                                         continue;
563
564                                 p = bus_path_escape(k);
565                                 if (!p) {
566                                         fclose(f);
567                                         free(introspection);
568                                         goto oom;
569                                 }
570
571                                 fprintf(f, "<node name=\"%s\"/>", p);
572                                 free(p);
573                         }
574
575                         fputs("</node>\n", f);
576
577                         if (ferror(f)) {
578                                 fclose(f);
579                                 free(introspection);
580                                 goto oom;
581                         }
582
583                         fclose(f);
584
585                         if (!introspection)
586                                 goto oom;
587
588                         if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
589                                 free(introspection);
590                                 goto oom;
591                         }
592
593                         free(introspection);
594
595                         if (!bus_maybe_send_reply(connection, message, reply))
596                                 goto oom;
597
598                         return DBUS_HANDLER_RESULT_HANDLED;
599                 }
600
601                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
602         }
603
604         r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
605         if (r == -ENOMEM)
606                 goto oom;
607         if (r < 0)
608                 return bus_send_error_reply(connection, message, &error, r);
609
610         return bus_unit_message_dispatch(u, connection, message);
611
612 oom:
613         dbus_error_free(&error);
614
615         return DBUS_HANDLER_RESULT_NEED_MEMORY;
616 }
617
618 const DBusObjectPathVTable bus_unit_vtable = {
619         .message_function = bus_unit_message_handler
620 };
621
622 void bus_unit_send_change_signal(Unit *u) {
623         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
624         _cleanup_free_ char *p = NULL;
625         int r;
626
627         assert(u);
628
629         if (u->in_dbus_queue) {
630                 LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u);
631                 u->in_dbus_queue = false;
632         }
633
634         if (!u->id)
635                 return;
636
637         if (!bus_has_subscriber(u->manager)) {
638                 u->sent_dbus_new_signal = true;
639                 return;
640         }
641
642         p = unit_dbus_path(u);
643         if (!p) {
644                 log_oom();
645                 return;
646         }
647
648         if (u->sent_dbus_new_signal) {
649                 /* Send a properties changed signal. First for the
650                  * specific type, then for the generic unit. The
651                  * clients may rely on this order to get atomic
652                  * behavior if needed. */
653
654                 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
655
656                         m = bus_properties_changed_new(p,
657                                                        UNIT_VTABLE(u)->bus_interface,
658                                                        UNIT_VTABLE(u)->bus_invalidating_properties);
659                         if (!m) {
660                                 log_oom();
661                                 return;
662                         }
663
664                         r = bus_broadcast(u->manager, m);
665                         if (r < 0) {
666                                 log_error("Failed to broadcast change message: %s", strerror(-r));
667                                 return;
668                         }
669
670                         dbus_message_unref(m);
671                 }
672
673                 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
674                                                INVALIDATING_PROPERTIES);
675                 if (!m) {
676                         log_oom();
677                         return;
678                 }
679
680         } else {
681                 /* Send a new signal */
682
683                 m = dbus_message_new_signal("/org/freedesktop/systemd1",
684                                             "org.freedesktop.systemd1.Manager",
685                                             "UnitNew");
686                 if (!m) {
687                         log_oom();
688                         return;
689                 }
690
691                 if (!dbus_message_append_args(m,
692                                               DBUS_TYPE_STRING, &u->id,
693                                               DBUS_TYPE_OBJECT_PATH, &p,
694                                               DBUS_TYPE_INVALID)) {
695                         log_oom();
696                         return;
697                 }
698         }
699
700         r = bus_broadcast(u->manager, m);
701         if (r < 0) {
702                 log_error("Failed to broadcast UnitNew/PropertiesChanged message.");
703                 return;
704         }
705
706         u->sent_dbus_new_signal = true;
707 }
708
709 void bus_unit_send_removed_signal(Unit *u) {
710         _cleanup_free_ char *p = NULL;
711         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
712
713         assert(u);
714
715         if (!bus_has_subscriber(u->manager))
716                 return;
717
718         if (!u->sent_dbus_new_signal)
719                 bus_unit_send_change_signal(u);
720
721         if (!u->id)
722                 return;
723
724         p = unit_dbus_path(u);
725         if (!p)
726                 goto oom;
727
728         m = dbus_message_new_signal("/org/freedesktop/systemd1",
729                                     "org.freedesktop.systemd1.Manager",
730                                     "UnitRemoved");
731         if (!m)
732                 goto oom;
733
734         if (!dbus_message_append_args(m,
735                                       DBUS_TYPE_STRING, &u->id,
736                                       DBUS_TYPE_OBJECT_PATH, &p,
737                                       DBUS_TYPE_INVALID))
738                 goto oom;
739
740         if (bus_broadcast(u->manager, m) < 0)
741                 goto oom;
742
743         return;
744
745 oom:
746         log_oom();
747 }
748
749 DBusHandlerResult bus_unit_queue_job(
750                 DBusConnection *connection,
751                 DBusMessage *message,
752                 Unit *u,
753                 JobType type,
754                 JobMode mode,
755                 bool reload_if_possible) {
756
757         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
758         _cleanup_free_ char *path = NULL;
759         Job *j;
760         JobBusClient *cl;
761         DBusError error;
762         int r;
763
764         assert(connection);
765         assert(message);
766         assert(u);
767         assert(type >= 0 && type < _JOB_TYPE_MAX);
768         assert(mode >= 0 && mode < _JOB_MODE_MAX);
769
770         dbus_error_init(&error);
771
772         if (reload_if_possible && unit_can_reload(u)) {
773                 if (type == JOB_RESTART)
774                         type = JOB_RELOAD_OR_START;
775                 else if (type == JOB_TRY_RESTART)
776                         type = JOB_RELOAD;
777         }
778
779         SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
780                                   (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
781                                   type == JOB_STOP ? "stop" : "reload");
782
783         if (type == JOB_STOP && (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && unit_active_state(u) == UNIT_INACTIVE) {
784                 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
785                 return bus_send_error_reply(connection, message, &error, -EPERM);
786         }
787
788         if ((type == JOB_START && u->refuse_manual_start) ||
789             (type == JOB_STOP && u->refuse_manual_stop) ||
790             ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
791                 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
792                                "Operation refused, unit %s may be requested by dependency only.", u->id);
793                 return bus_send_error_reply(connection, message, &error, -EPERM);
794         }
795
796         r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
797         if (r < 0)
798                 return bus_send_error_reply(connection, message, &error, r);
799
800         cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
801         if (!cl)
802                 goto oom;
803
804         LIST_PREPEND(client, j->bus_client_list, cl);
805
806         reply = dbus_message_new_method_return(message);
807         if (!reply)
808                 goto oom;
809
810         path = job_dbus_path(j);
811         if (!path)
812                 goto oom;
813
814         if (!dbus_message_append_args(
815                             reply,
816                             DBUS_TYPE_OBJECT_PATH, &path,
817                             DBUS_TYPE_INVALID))
818                 goto oom;
819
820         if (!bus_maybe_send_reply(connection, message, reply))
821                 goto oom;
822
823         return DBUS_HANDLER_RESULT_HANDLED;
824
825 oom:
826         dbus_error_free(&error);
827
828         return DBUS_HANDLER_RESULT_NEED_MEMORY;
829 }
830
831 static int bus_unit_set_transient_property(
832                 Unit *u,
833                 const char *name,
834                 DBusMessageIter *i,
835                 UnitSetPropertiesMode mode,
836                 DBusError *error) {
837
838         int r;
839
840         assert(u);
841         assert(name);
842         assert(i);
843
844         if (streq(name, "Description")) {
845                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
846                         return -EINVAL;
847
848                 if (mode != UNIT_CHECK) {
849                         const char *description;
850
851                         dbus_message_iter_get_basic(i, &description);
852
853                         r = unit_set_description(u, description);
854                         if (r < 0)
855                                 return r;
856
857                         unit_write_drop_in_format(u, mode, name, "[Unit]\nDescription=%s\n", description);
858                 }
859
860                 return 1;
861
862         } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) {
863                 const char *s;
864
865                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
866                         return -EINVAL;
867
868                 dbus_message_iter_get_basic(i, &s);
869
870                 if (isempty(s)) {
871                         if (mode != UNIT_CHECK) {
872                                 unit_ref_unset(&u->slice);
873                                 unit_remove_drop_in(u, mode, name);
874                         }
875                 } else {
876                         Unit *slice;
877
878                         r = manager_load_unit(u->manager, s, NULL, error, &slice);
879                         if (r < 0)
880                                 return r;
881
882                         if (slice->type != UNIT_SLICE)
883                                 return -EINVAL;
884
885                         if (mode != UNIT_CHECK) {
886                                 unit_ref_set(&u->slice, slice);
887                                 unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s);
888                         }
889                 }
890
891                 return 1;
892
893         } else if (streq(name, "Requires") ||
894                    streq(name, "RequiresOverridable") ||
895                    streq(name, "Requisite") ||
896                    streq(name, "RequisiteOverridable") ||
897                    streq(name, "Wants") ||
898                    streq(name, "BindsTo") ||
899                    streq(name, "Conflicts") ||
900                    streq(name, "Before") ||
901                    streq(name, "After") ||
902                    streq(name, "OnFailure") ||
903                    streq(name, "PropagatesReloadTo") ||
904                    streq(name, "ReloadPropagatedFrom") ||
905                    streq(name, "PartOf")) {
906
907                 UnitDependency d;
908                 DBusMessageIter sub;
909
910                 d = unit_dependency_from_string(name);
911                 if (d < 0)
912                         return -EINVAL;
913
914                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
915                     dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRING)
916                         return -EINVAL;
917
918                 dbus_message_iter_recurse(i, &sub);
919                 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
920                         const char *other;
921
922                         dbus_message_iter_get_basic(&sub, &other);
923
924                         if (!unit_name_is_valid(other, false))
925                                 return -EINVAL;
926
927                         if (mode != UNIT_CHECK) {
928                                 _cleanup_free_ char *label = NULL;
929
930                                 r = unit_add_dependency_by_name(u, d, other, NULL, true);
931                                 if (r < 0)
932                                         return r;
933
934                                 label = strjoin(name, "-", other, NULL);
935                                 if (!label)
936                                         return -ENOMEM;
937
938                                 unit_write_drop_in_format(u, mode, label, "[Unit]\n%s=%s\n", name, other);
939                         }
940
941                         dbus_message_iter_next(&sub);
942                 }
943
944                 return 1;
945         }
946
947         return 0;
948 }
949
950 int bus_unit_set_properties(
951                 Unit *u,
952                 DBusMessageIter *iter,
953                 UnitSetPropertiesMode mode,
954                 bool commit,
955                 DBusError *error) {
956
957         bool for_real = false;
958         DBusMessageIter sub;
959         unsigned n = 0;
960         int r;
961
962         assert(u);
963         assert(iter);
964
965         if (u->transient)
966                 mode &= UNIT_RUNTIME;
967
968         /* We iterate through the array twice. First run we just check
969          * if all passed data is valid, second run actually applies
970          * it. This is to implement transaction-like behaviour without
971          * actually providing full transactions. */
972
973         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
974             dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
975                 return -EINVAL;
976
977         dbus_message_iter_recurse(iter, &sub);
978         for (;;) {
979                 DBusMessageIter sub2, sub3;
980                 const char *name;
981
982                 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_INVALID) {
983
984                         if (for_real || mode == UNIT_CHECK)
985                                 break;
986
987                         /* Reached EOF. Let's try again, and this time for realz... */
988                         dbus_message_iter_recurse(iter, &sub);
989                         for_real = true;
990                         continue;
991                 }
992
993                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT)
994                         return -EINVAL;
995
996                 dbus_message_iter_recurse(&sub, &sub2);
997
998                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
999                     dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)
1000                         return -EINVAL;
1001
1002                 if (!UNIT_VTABLE(u)->bus_set_property) {
1003                         dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties.");
1004                         return -ENOENT;
1005                 }
1006
1007                 dbus_message_iter_recurse(&sub2, &sub3);
1008                 r = UNIT_VTABLE(u)->bus_set_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
1009                 if (r == 0 && u->transient && u->load_state == UNIT_STUB)
1010                         r = bus_unit_set_transient_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
1011                 if (r < 0)
1012                         return r;
1013                 if (r == 0) {
1014                         dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name);
1015                         return -ENOENT;
1016                 }
1017
1018                 dbus_message_iter_next(&sub);
1019
1020                 n += for_real;
1021         }
1022
1023         if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties)
1024                 UNIT_VTABLE(u)->bus_commit_properties(u);
1025
1026         return n;
1027 }
1028
1029 const BusProperty bus_unit_properties[] = {
1030         { "Id",                              bus_property_append_string,                "s", offsetof(Unit, id),                                         true },
1031         { "Names",                           bus_unit_append_names,                    "as", 0                                                                },
1032         { "Following",                       bus_unit_append_following,                 "s", 0                                                                },
1033         { "Requires",                        bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_REQUIRES]),                true },
1034         { "RequiresOverridable",             bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]),    true },
1035         { "Requisite",                       bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_REQUISITE]),               true },
1036         { "RequisiteOverridable",            bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]),   true },
1037         { "Wants",                           bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_WANTS]),                   true },
1038         { "BindsTo",                         bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]),                true },
1039         { "PartOf",                          bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_PART_OF]),                 true },
1040         { "RequiredBy",                      bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]),             true },
1041         { "RequiredByOverridable",           bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
1042         { "WantedBy",                        bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]),               true },
1043         { "BoundBy",                         bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]),                true },
1044         { "ConsistsOf",                      bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]),             true },
1045         { "Conflicts",                       bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]),               true },
1046         { "ConflictedBy",                    bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]),           true },
1047         { "Before",                          bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_BEFORE]),                  true },
1048         { "After",                           bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_AFTER]),                   true },
1049         { "OnFailure",                       bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]),              true },
1050         { "Triggers",                        bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]),                true },
1051         { "TriggeredBy",                     bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]),            true },
1052         { "PropagatesReloadTo",              bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]),    true },
1053         { "ReloadPropagatedFrom",            bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]),  true },
1054         { "RequiresMountsFor",               bus_property_append_strv,                 "as", offsetof(Unit, requires_mounts_for),                        true },
1055         { "Documentation",                   bus_property_append_strv,                 "as", offsetof(Unit, documentation),                              true },
1056         { "Description",                     bus_unit_append_description,               "s", 0                                                                },
1057         { "LoadState",                       bus_unit_append_load_state,                "s", offsetof(Unit, load_state)                                       },
1058         { "ActiveState",                     bus_unit_append_active_state,              "s", 0                                                                },
1059         { "SubState",                        bus_unit_append_sub_state,                 "s", 0                                                                },
1060         { "FragmentPath",                    bus_property_append_string,                "s", offsetof(Unit, fragment_path),                              true },
1061         { "SourcePath",                      bus_property_append_string,                "s", offsetof(Unit, source_path),                                true },
1062         { "DropInPaths",                     bus_property_append_strv,                 "as", offsetof(Unit, dropin_paths),                               true },
1063         { "UnitFileState",                   bus_unit_append_file_state,                "s", 0                                                                },
1064         { "InactiveExitTimestamp",           bus_property_append_usec,                  "t", offsetof(Unit, inactive_exit_timestamp.realtime)                 },
1065         { "InactiveExitTimestampMonotonic",  bus_property_append_usec,                  "t", offsetof(Unit, inactive_exit_timestamp.monotonic)                },
1066         { "ActiveEnterTimestamp",            bus_property_append_usec,                  "t", offsetof(Unit, active_enter_timestamp.realtime)                  },
1067         { "ActiveEnterTimestampMonotonic",   bus_property_append_usec,                  "t", offsetof(Unit, active_enter_timestamp.monotonic)                 },
1068         { "ActiveExitTimestamp",             bus_property_append_usec,                  "t", offsetof(Unit, active_exit_timestamp.realtime)                   },
1069         { "ActiveExitTimestampMonotonic",    bus_property_append_usec,                  "t", offsetof(Unit, active_exit_timestamp.monotonic)                  },
1070         { "InactiveEnterTimestamp",          bus_property_append_usec,                  "t", offsetof(Unit, inactive_enter_timestamp.realtime)                },
1071         { "InactiveEnterTimestampMonotonic", bus_property_append_usec,                  "t", offsetof(Unit, inactive_enter_timestamp.monotonic)               },
1072         { "CanStart",                        bus_unit_append_can_start,                 "b", 0                                                                },
1073         { "CanStop",                         bus_unit_append_can_stop,                  "b", 0                                                                },
1074         { "CanReload",                       bus_unit_append_can_reload,                "b", 0                                                                },
1075         { "CanIsolate",                      bus_unit_append_can_isolate,               "b", 0                                                                },
1076         { "Job",                             bus_unit_append_job,                    "(uo)", 0                                                                },
1077         { "StopWhenUnneeded",                bus_property_append_bool,                  "b", offsetof(Unit, stop_when_unneeded)                               },
1078         { "RefuseManualStart",               bus_property_append_bool,                  "b", offsetof(Unit, refuse_manual_start)                              },
1079         { "RefuseManualStop",                bus_property_append_bool,                  "b", offsetof(Unit, refuse_manual_stop)                               },
1080         { "AllowIsolate",                    bus_property_append_bool,                  "b", offsetof(Unit, allow_isolate)                                    },
1081         { "DefaultDependencies",             bus_property_append_bool,                  "b", offsetof(Unit, default_dependencies)                             },
1082         { "OnFailureIsolate",                bus_property_append_bool,                  "b", offsetof(Unit, on_failure_isolate)                               },
1083         { "IgnoreOnIsolate",                 bus_property_append_bool,                  "b", offsetof(Unit, ignore_on_isolate)                                },
1084         { "IgnoreOnSnapshot",                bus_property_append_bool,                  "b", offsetof(Unit, ignore_on_snapshot)                               },
1085         { "NeedDaemonReload",                bus_unit_append_need_daemon_reload,        "b", 0                                                                },
1086         { "JobTimeoutUSec",                  bus_property_append_usec,                  "t", offsetof(Unit, job_timeout)                                      },
1087         { "ConditionTimestamp",              bus_property_append_usec,                  "t", offsetof(Unit, condition_timestamp.realtime)                     },
1088         { "ConditionTimestampMonotonic",     bus_property_append_usec,                  "t", offsetof(Unit, condition_timestamp.monotonic)                    },
1089         { "ConditionResult",                 bus_property_append_bool,                  "b", offsetof(Unit, condition_result)                                 },
1090         { "Conditions",                      bus_property_append_condition_list, "a(sbbsi)", offsetof(Unit, conditions)                                       },
1091         { "LoadError",                       bus_unit_append_load_error,             "(ss)", 0                                                                },
1092         { "Transient",                       bus_property_append_bool,                  "b", offsetof(Unit, transient)                                        },
1093         {}
1094 };
1095
1096 const BusProperty bus_unit_cgroup_properties[] = {
1097         { "Slice",                bus_unit_append_slice,              "s", 0 },
1098         { "ControlGroup",         bus_property_append_string,         "s", offsetof(Unit, cgroup_path),                                true },
1099         {}
1100 };