chiark / gitweb /
core: general cgroup rework
[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
407         } else if (UNIT_VTABLE(u)->bus_message_handler)
408                 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
409         else
410                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
411
412         if (job_type != _JOB_TYPE_INVALID) {
413                 const char *smode;
414                 JobMode mode;
415
416                 if (!dbus_message_get_args(
417                                     message,
418                                     &error,
419                                     DBUS_TYPE_STRING, &smode,
420                                     DBUS_TYPE_INVALID))
421                         return bus_send_error_reply(connection, message, &error, -EINVAL);
422
423                 mode = job_mode_from_string(smode);
424                 if (mode < 0) {
425                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
426                         return bus_send_error_reply(connection, message, &error, -EINVAL);
427                 }
428
429                 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
430         }
431
432         if (reply)
433                 if (!bus_maybe_send_reply(connection, message, reply))
434                         goto oom;
435
436         return DBUS_HANDLER_RESULT_HANDLED;
437
438 oom:
439         dbus_error_free(&error);
440         return DBUS_HANDLER_RESULT_NEED_MEMORY;
441 }
442
443 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage  *message, void *data) {
444         Manager *m = data;
445         Unit *u;
446         int r;
447         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
448         DBusError error;
449
450         assert(connection);
451         assert(message);
452         assert(m);
453
454         dbus_error_init(&error);
455
456         if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
457                 /* Be nice to gdbus and return introspection data for our mid-level paths */
458
459                 SELINUX_ACCESS_CHECK(connection, message, "status");
460
461                 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
462                         char *introspection = NULL;
463                         FILE *f;
464                         Iterator i;
465                         const char *k;
466                         size_t size;
467
468                         reply = dbus_message_new_method_return(message);
469                         if (!reply)
470                                 goto oom;
471
472                         /* We roll our own introspection code here, instead of
473                          * relying on bus_default_message_handler() because we
474                          * need to generate our introspection string
475                          * dynamically. */
476
477                         f = open_memstream(&introspection, &size);
478                         if (!f)
479                                 goto oom;
480
481                         fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
482                               "<node>\n", f);
483
484                         fputs(BUS_INTROSPECTABLE_INTERFACE, f);
485                         fputs(BUS_PEER_INTERFACE, f);
486
487                         HASHMAP_FOREACH_KEY(u, k, m->units, i) {
488                                 char *p;
489
490                                 if (k != u->id)
491                                         continue;
492
493                                 p = bus_path_escape(k);
494                                 if (!p) {
495                                         fclose(f);
496                                         free(introspection);
497                                         goto oom;
498                                 }
499
500                                 fprintf(f, "<node name=\"%s\"/>", p);
501                                 free(p);
502                         }
503
504                         fputs("</node>\n", f);
505
506                         if (ferror(f)) {
507                                 fclose(f);
508                                 free(introspection);
509                                 goto oom;
510                         }
511
512                         fclose(f);
513
514                         if (!introspection)
515                                 goto oom;
516
517                         if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
518                                 free(introspection);
519                                 goto oom;
520                         }
521
522                         free(introspection);
523
524                         if (!bus_maybe_send_reply(connection, message, reply))
525                                 goto oom;
526
527                         return DBUS_HANDLER_RESULT_HANDLED;
528                 }
529
530                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
531         }
532
533         r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
534         if (r == -ENOMEM)
535                 goto oom;
536         if (r < 0)
537                 return bus_send_error_reply(connection, message, &error, r);
538
539         return bus_unit_message_dispatch(u, connection, message);
540
541 oom:
542         dbus_error_free(&error);
543
544         return DBUS_HANDLER_RESULT_NEED_MEMORY;
545 }
546
547 const DBusObjectPathVTable bus_unit_vtable = {
548         .message_function = bus_unit_message_handler
549 };
550
551 void bus_unit_send_change_signal(Unit *u) {
552         _cleanup_free_ char *p = NULL;
553         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
554
555         assert(u);
556
557         if (u->in_dbus_queue) {
558                 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
559                 u->in_dbus_queue = false;
560         }
561
562         if (!u->id)
563                 return;
564
565         if (!bus_has_subscriber(u->manager)) {
566                 u->sent_dbus_new_signal = true;
567                 return;
568         }
569
570         p = unit_dbus_path(u);
571         if (!p)
572                 goto oom;
573
574         if (u->sent_dbus_new_signal) {
575                 /* Send a properties changed signal. First for the
576                  * specific type, then for the generic unit. The
577                  * clients may rely on this order to get atomic
578                  * behavior if needed. */
579
580                 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
581
582                         m = bus_properties_changed_new(p,
583                                                        UNIT_VTABLE(u)->bus_interface,
584                                                        UNIT_VTABLE(u)->bus_invalidating_properties);
585                         if (!m)
586                                 goto oom;
587
588                         if (bus_broadcast(u->manager, m) < 0)
589                                 goto oom;
590
591                         dbus_message_unref(m);
592                 }
593
594                 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
595                                                INVALIDATING_PROPERTIES);
596                 if (!m)
597                         goto oom;
598
599         } else {
600                 /* Send a new signal */
601
602                 m = dbus_message_new_signal("/org/freedesktop/systemd1",
603                                             "org.freedesktop.systemd1.Manager",
604                                             "UnitNew");
605                 if (!m)
606                         goto oom;
607
608                 if (!dbus_message_append_args(m,
609                                               DBUS_TYPE_STRING, &u->id,
610                                               DBUS_TYPE_OBJECT_PATH, &p,
611                                               DBUS_TYPE_INVALID))
612                         goto oom;
613         }
614
615         if (bus_broadcast(u->manager, m) < 0)
616                 goto oom;
617
618         u->sent_dbus_new_signal = true;
619
620         return;
621
622 oom:
623         log_oom();
624 }
625
626 void bus_unit_send_removed_signal(Unit *u) {
627         _cleanup_free_ char *p = NULL;
628         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
629
630         assert(u);
631
632         if (!bus_has_subscriber(u->manager))
633                 return;
634
635         if (!u->sent_dbus_new_signal)
636                 bus_unit_send_change_signal(u);
637
638         if (!u->id)
639                 return;
640
641         p = unit_dbus_path(u);
642         if (!p)
643                 goto oom;
644
645         m = dbus_message_new_signal("/org/freedesktop/systemd1",
646                                     "org.freedesktop.systemd1.Manager",
647                                     "UnitRemoved");
648         if (!m)
649                 goto oom;
650
651         if (!dbus_message_append_args(m,
652                                       DBUS_TYPE_STRING, &u->id,
653                                       DBUS_TYPE_OBJECT_PATH, &p,
654                                       DBUS_TYPE_INVALID))
655                 goto oom;
656
657         if (bus_broadcast(u->manager, m) < 0)
658                 goto oom;
659
660         return;
661
662 oom:
663         log_oom();
664 }
665
666 DBusHandlerResult bus_unit_queue_job(
667                 DBusConnection *connection,
668                 DBusMessage *message,
669                 Unit *u,
670                 JobType type,
671                 JobMode mode,
672                 bool reload_if_possible) {
673
674         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
675         _cleanup_free_ char *path = NULL;
676         Job *j;
677         JobBusClient *cl;
678         DBusError error;
679         int r;
680
681         assert(connection);
682         assert(message);
683         assert(u);
684         assert(type >= 0 && type < _JOB_TYPE_MAX);
685         assert(mode >= 0 && mode < _JOB_MODE_MAX);
686
687         dbus_error_init(&error);
688
689         if (reload_if_possible && unit_can_reload(u)) {
690                 if (type == JOB_RESTART)
691                         type = JOB_RELOAD_OR_START;
692                 else if (type == JOB_TRY_RESTART)
693                         type = JOB_RELOAD;
694         }
695
696         SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
697                                   (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
698                                   type == JOB_STOP ? "stop" : "reload");
699
700         if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
701                 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
702                 return bus_send_error_reply(connection, message, &error, -EPERM);
703         }
704
705         if ((type == JOB_START && u->refuse_manual_start) ||
706             (type == JOB_STOP && u->refuse_manual_stop) ||
707             ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
708                 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
709                                "Operation refused, unit %s may be requested by dependency only.", u->id);
710                 return bus_send_error_reply(connection, message, &error, -EPERM);
711         }
712
713         r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
714         if (r < 0)
715                 return bus_send_error_reply(connection, message, &error, r);
716
717         cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
718         if (!cl)
719                 goto oom;
720
721         LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
722
723         reply = dbus_message_new_method_return(message);
724         if (!reply)
725                 goto oom;
726
727         path = job_dbus_path(j);
728         if (!path)
729                 goto oom;
730
731         if (!dbus_message_append_args(
732                             reply,
733                             DBUS_TYPE_OBJECT_PATH, &path,
734                             DBUS_TYPE_INVALID))
735                 goto oom;
736
737         if (!bus_maybe_send_reply(connection, message, reply))
738                 goto oom;
739
740         return DBUS_HANDLER_RESULT_HANDLED;
741
742 oom:
743         dbus_error_free(&error);
744
745         return DBUS_HANDLER_RESULT_NEED_MEMORY;
746 }
747
748 const BusProperty bus_unit_properties[] = {
749         { "Id",                   bus_property_append_string,         "s", offsetof(Unit, id),                                         true },
750         { "Names",                bus_unit_append_names,             "as", 0 },
751         { "Following",            bus_unit_append_following,          "s", 0 },
752         { "Slice",                bus_unit_append_slice,              "s", 0 },
753         { "Requires",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES]),                true },
754         { "RequiresOverridable",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]),    true },
755         { "Requisite",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE]),               true },
756         { "RequisiteOverridable", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]),   true },
757         { "Wants",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTS]),                   true },
758         { "BindsTo",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]),                true },
759         { "PartOf",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PART_OF]),                 true },
760         { "RequiredBy",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]),             true },
761         { "RequiredByOverridable",bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
762         { "WantedBy",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]),               true },
763         { "BoundBy",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]),                true },
764         { "ConsistsOf",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]),             true },
765         { "Conflicts",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]),               true },
766         { "ConflictedBy",         bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]),           true },
767         { "Before",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BEFORE]),                  true },
768         { "After",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_AFTER]),                   true },
769         { "OnFailure",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]),              true },
770         { "Triggers",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]),                true },
771         { "TriggeredBy",          bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]),            true },
772         { "PropagatesReloadTo",   bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]),    true },
773         { "ReloadPropagatedFrom", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]),  true },
774         { "RequiresMountsFor",    bus_property_append_strv,          "as", offsetof(Unit, requires_mounts_for),                        true },
775         { "Documentation",        bus_property_append_strv,          "as", offsetof(Unit, documentation),                              true },
776         { "Description",          bus_unit_append_description,        "s", 0 },
777         { "LoadState",            bus_unit_append_load_state,         "s", offsetof(Unit, load_state)                         },
778         { "ActiveState",          bus_unit_append_active_state,       "s", 0 },
779         { "SubState",             bus_unit_append_sub_state,          "s", 0 },
780         { "FragmentPath",         bus_property_append_string,         "s", offsetof(Unit, fragment_path),                              true },
781         { "SourcePath",           bus_property_append_string,         "s", offsetof(Unit, source_path),                                true },
782         { "DropInPaths",          bus_property_append_strv,          "as", offsetof(Unit, dropin_paths),                               true },
783         { "UnitFileState",        bus_unit_append_file_state,         "s", 0 },
784         { "InactiveExitTimestamp",bus_property_append_usec,           "t", offsetof(Unit, inactive_exit_timestamp.realtime)   },
785         { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic)  },
786         { "ActiveEnterTimestamp", bus_property_append_usec,           "t", offsetof(Unit, active_enter_timestamp.realtime)    },
787         { "ActiveEnterTimestampMonotonic", bus_property_append_usec,  "t", offsetof(Unit, active_enter_timestamp.monotonic)   },
788         { "ActiveExitTimestamp",  bus_property_append_usec,           "t", offsetof(Unit, active_exit_timestamp.realtime)     },
789         { "ActiveExitTimestampMonotonic",  bus_property_append_usec,  "t", offsetof(Unit, active_exit_timestamp.monotonic)    },
790         { "InactiveEnterTimestamp", bus_property_append_usec,         "t", offsetof(Unit, inactive_enter_timestamp.realtime)  },
791         { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
792         { "CanStart",             bus_unit_append_can_start,          "b", 0 },
793         { "CanStop",              bus_unit_append_can_stop,           "b", 0 },
794         { "CanReload",            bus_unit_append_can_reload,         "b", 0 },
795         { "CanIsolate",           bus_unit_append_can_isolate,        "b", 0 },
796         { "Job",                  bus_unit_append_job,             "(uo)", 0 },
797         { "StopWhenUnneeded",     bus_property_append_bool,           "b", offsetof(Unit, stop_when_unneeded)                 },
798         { "RefuseManualStart",    bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_start)                },
799         { "RefuseManualStop",     bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_stop)                 },
800         { "AllowIsolate",         bus_property_append_bool,           "b", offsetof(Unit, allow_isolate)                      },
801         { "DefaultDependencies",  bus_property_append_bool,           "b", offsetof(Unit, default_dependencies)               },
802         { "OnFailureIsolate",     bus_property_append_bool,           "b", offsetof(Unit, on_failure_isolate)                 },
803         { "IgnoreOnIsolate",      bus_property_append_bool,           "b", offsetof(Unit, ignore_on_isolate)                  },
804         { "IgnoreOnSnapshot",     bus_property_append_bool,           "b", offsetof(Unit, ignore_on_snapshot)                 },
805         { "NeedDaemonReload",     bus_unit_append_need_daemon_reload, "b", 0 },
806         { "JobTimeoutUSec",       bus_property_append_usec,           "t", offsetof(Unit, job_timeout)                        },
807         { "ConditionTimestamp",   bus_property_append_usec,           "t", offsetof(Unit, condition_timestamp.realtime)       },
808         { "ConditionTimestampMonotonic", bus_property_append_usec,    "t", offsetof(Unit, condition_timestamp.monotonic)      },
809         { "ConditionResult",      bus_property_append_bool,           "b", offsetof(Unit, condition_result)                   },
810         { "LoadError",            bus_unit_append_load_error,      "(ss)", 0 },
811         { "ControlGroup",         bus_property_append_string,         "s", offsetof(Unit, cgroup_path),                                true },
812         { NULL, }
813 };