chiark / gitweb /
machined: relax access to GetMachine()
[elogind.git] / src / machine / machined-dbus.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 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 #include <string.h>
24 #include <unistd.h>
25 #include <pwd.h>
26
27 #include <systemd/sd-id128.h>
28 #include <systemd/sd-messages.h>
29
30 #include "machined.h"
31 #include "dbus-common.h"
32 #include "strv.h"
33 #include "mkdir.h"
34 #include "path-util.h"
35 #include "special.h"
36 #include "sleep-config.h"
37 #include "fileio-label.h"
38 #include "label.h"
39 #include "utf8.h"
40 #include "unit-name.h"
41 #include "bus-errors.h"
42 #include "virt.h"
43
44 #define BUS_MANAGER_INTERFACE                                           \
45         " <interface name=\"org.freedesktop.machine1.Manager\">\n"      \
46         "  <method name=\"GetMachine\">\n"                              \
47         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
48         "   <arg name=\"machine\" type=\"o\" direction=\"out\"/>\n"     \
49         "  </method>\n"                                                 \
50         "  <method name=\"GetMachineByPID\">\n"                         \
51         "   <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n"          \
52         "   <arg name=\"machine\" type=\"o\" direction=\"out\"/>\n"     \
53         "  </method>\n"                                                 \
54         "  <method name=\"ListMachines\">\n"                            \
55         "   <arg name=\"machines\" type=\"a(ssso)\" direction=\"out\"/>\n" \
56         "  </method>\n"                                                 \
57         "  <method name=\"CreateMachine\">\n"                           \
58         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
59         "   <arg name=\"id\" type=\"ay\" direction=\"in\"/>\n"          \
60         "   <arg name=\"service\" type=\"s\" direction=\"in\"/>\n"      \
61         "   <arg name=\"class\" type=\"s\" direction=\"in\"/>\n"        \
62         "   <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n"       \
63         "   <arg name=\"root_directory\" type=\"s\" direction=\"in\"/>\n" \
64         "   <arg name=\"scope_properties\" type=\"a(sv)\" direction=\"in\"/>\n" \
65         "   <arg name=\"path\" type=\"o\" direction=\"out\"/>\n"        \
66         "  </method>\n"                                                 \
67         "  <method name=\"KillMachine\">\n"                             \
68         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
69         "   <arg name=\"who\" type=\"s\" direction=\"in\"/>\n"          \
70         "   <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n"       \
71         "  </method>\n"                                                 \
72         "  <method name=\"TerminateMachine\">\n"                        \
73         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
74         "  </method>\n"                                                 \
75         "  <signal name=\"MachineNew\">\n"                              \
76         "   <arg name=\"machine\" type=\"s\"/>\n"                       \
77         "   <arg name=\"path\" type=\"o\"/>\n"                          \
78         "  </signal>\n"                                                 \
79         "  <signal name=\"MachineRemoved\">\n"                          \
80         "   <arg name=\"machine\" type=\"s\"/>\n"                       \
81         "   <arg name=\"path\" type=\"o\"/>\n"                          \
82         "  </signal>\n"                                                 \
83         " </interface>\n"
84
85 #define INTROSPECTION_BEGIN                                             \
86         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
87         "<node>\n"                                                      \
88         BUS_MANAGER_INTERFACE                                           \
89         BUS_PROPERTIES_INTERFACE                                        \
90         BUS_PEER_INTERFACE                                              \
91         BUS_INTROSPECTABLE_INTERFACE
92
93 #define INTROSPECTION_END                                               \
94         "</node>\n"
95
96 #define INTERFACES_LIST                              \
97         BUS_GENERIC_INTERFACES_LIST                  \
98         "org.freedesktop.machine1.Manager\0"
99
100 static bool valid_machine_name(const char *p) {
101         size_t l;
102
103         if (!filename_is_safe(p))
104                 return false;
105
106         if (!ascii_is_valid(p))
107                 return false;
108
109         l = strlen(p);
110
111         if (l < 1 || l> 64)
112                 return false;
113
114         return true;
115 }
116
117 static int bus_manager_create_machine(Manager *manager, DBusMessage *message) {
118
119         const char *name, *service, *class, *slice, *root_directory;
120         _cleanup_free_ char *p = NULL;
121         DBusMessageIter iter, sub;
122         MachineClass c;
123         uint32_t leader;
124         sd_id128_t id;
125         Machine *m;
126         int n, r;
127         void *v;
128
129         assert(manager);
130         assert(message);
131
132         if (!dbus_message_iter_init(message, &iter) ||
133             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
134                 return -EINVAL;
135
136         dbus_message_iter_get_basic(&iter, &name);
137
138         if (!valid_machine_name(name) ||
139             !dbus_message_iter_next(&iter) ||
140             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
141             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE)
142                 return -EINVAL;
143
144         dbus_message_iter_recurse(&iter, &sub);
145         dbus_message_iter_get_fixed_array(&sub, &v, &n);
146
147         if (n == 0)
148                 id = SD_ID128_NULL;
149         else if (n == 16)
150                 memcpy(&id, v, n);
151         else
152                 return -EINVAL;
153
154         if (!dbus_message_iter_next(&iter) ||
155             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
156                 return -EINVAL;
157
158         dbus_message_iter_get_basic(&iter, &service);
159
160         if (!dbus_message_iter_next(&iter) ||
161             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
162                 return -EINVAL;
163
164         dbus_message_iter_get_basic(&iter, &class);
165
166         if (isempty(class))
167                 c = _MACHINE_CLASS_INVALID;
168         else {
169                 c = machine_class_from_string(class);
170                 if (c < 0)
171                         return -EINVAL;
172         }
173
174         if (!dbus_message_iter_next(&iter) ||
175             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
176                 return -EINVAL;
177
178         dbus_message_iter_get_basic(&iter, &leader);
179         if (!dbus_message_iter_next(&iter) ||
180             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
181                 return -EINVAL;
182
183         dbus_message_iter_get_basic(&iter, &root_directory);
184
185         if (!(isempty(root_directory) || path_is_absolute(root_directory)))
186                 return -EINVAL;
187
188         if (hashmap_get(manager->machines, name))
189                 return -EEXIST;
190
191         if (leader <= 0) {
192                 leader = bus_get_unix_process_id(manager->bus, dbus_message_get_sender(message), NULL);
193                 if (leader == 0)
194                         return -EINVAL;
195         }
196
197         r = manager_add_machine(manager, name, &m);
198         if (r < 0)
199                 goto fail;
200
201         m->leader = leader;
202         m->class = c;
203         m->id = id;
204
205         if (!isempty(service)) {
206                 m->service = strdup(service);
207                 if (!m->service) {
208                         r = -ENOMEM;
209                         goto fail;
210                 }
211         }
212
213         if (!isempty(root_directory)) {
214                 m->root_directory = strdup(root_directory);
215                 if (!m->root_directory) {
216                         r = -ENOMEM;
217                         goto fail;
218                 }
219         }
220
221         r = machine_start(m);
222         if (r < 0)
223                 goto fail;
224
225         m->create_message = dbus_message_ref(message);
226
227         return 0;
228
229 fail:
230         if (m)
231                 machine_add_to_gc_queue(m);
232
233         return r;
234 }
235
236 static DBusHandlerResult manager_message_handler(
237                 DBusConnection *connection,
238                 DBusMessage *message,
239                 void *userdata) {
240
241         Manager *m = userdata;
242
243         DBusError error;
244         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
245         int r;
246
247         assert(connection);
248         assert(message);
249         assert(m);
250
251         dbus_error_init(&error);
252
253         if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachine")) {
254                 Machine *machine;
255                 const char *name;
256                 char *p;
257                 bool b;
258
259                 if (!dbus_message_get_args(
260                                     message,
261                                     &error,
262                                     DBUS_TYPE_STRING, &name,
263                                     DBUS_TYPE_INVALID))
264                         return bus_send_error_reply(connection, message, &error, -EINVAL);
265
266                 machine = hashmap_get(m->machines, name);
267                 if (!machine)
268                         return bus_send_error_reply(connection, message, &error, -ENOENT);
269
270                 reply = dbus_message_new_method_return(message);
271                 if (!reply)
272                         goto oom;
273
274                 p = machine_bus_path(machine);
275                 if (!p)
276                         goto oom;
277
278                 b = dbus_message_append_args(
279                                 reply,
280                                 DBUS_TYPE_OBJECT_PATH, &p,
281                                 DBUS_TYPE_INVALID);
282                 free(p);
283
284                 if (!b)
285                         goto oom;
286
287         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachineByPID")) {
288                 uint32_t pid;
289                 char *p;
290                 Machine *machine;
291                 bool b;
292
293                 if (!dbus_message_get_args(
294                                     message,
295                                     &error,
296                                     DBUS_TYPE_UINT32, &pid,
297                                     DBUS_TYPE_INVALID))
298                         return bus_send_error_reply(connection, message, &error, -EINVAL);
299
300                 r = manager_get_machine_by_pid(m, pid, &machine);
301                 if (r <= 0)
302                         return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
303
304                 reply = dbus_message_new_method_return(message);
305                 if (!reply)
306                         goto oom;
307
308                 p = machine_bus_path(machine);
309                 if (!p)
310                         goto oom;
311
312                 b = dbus_message_append_args(
313                                 reply,
314                                 DBUS_TYPE_OBJECT_PATH, &p,
315                                 DBUS_TYPE_INVALID);
316                 free(p);
317
318                 if (!b)
319                         goto oom;
320
321         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "ListMachines")) {
322                 Machine *machine;
323                 Iterator i;
324                 DBusMessageIter iter, sub;
325
326                 reply = dbus_message_new_method_return(message);
327                 if (!reply)
328                         goto oom;
329
330                 dbus_message_iter_init_append(reply, &iter);
331
332                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssso)", &sub))
333                         goto oom;
334
335                 HASHMAP_FOREACH(machine, m->machines, i) {
336                         _cleanup_free_ char *p = NULL;
337                         DBusMessageIter sub2;
338                         const char *class;
339
340                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
341                                 goto oom;
342
343                         p = machine_bus_path(machine);
344                         if (!p)
345                                 goto oom;
346
347                         class = strempty(machine_class_to_string(machine->class));
348
349                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->name) ||
350                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &class) ||
351                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->service) ||
352                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
353                                 free(p);
354                                 goto oom;
355                         }
356
357                         if (!dbus_message_iter_close_container(&sub, &sub2))
358                                 goto oom;
359                 }
360
361                 if (!dbus_message_iter_close_container(&iter, &sub))
362                         goto oom;
363
364         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "CreateMachine")) {
365
366                 r = bus_manager_create_machine(m, message);
367                 if (r < 0)
368                         return bus_send_error_reply(connection, message, NULL, r);
369
370         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "KillMachine")) {
371                 const char *swho;
372                 int32_t signo;
373                 KillWho who;
374                 const char *name;
375                 Machine *machine;
376
377                 if (!dbus_message_get_args(
378                                     message,
379                                     &error,
380                                     DBUS_TYPE_STRING, &name,
381                                     DBUS_TYPE_STRING, &swho,
382                                     DBUS_TYPE_INT32, &signo,
383                                     DBUS_TYPE_INVALID))
384                         return bus_send_error_reply(connection, message, &error, -EINVAL);
385
386                 if (isempty(swho))
387                         who = KILL_ALL;
388                 else {
389                         who = kill_who_from_string(swho);
390                         if (who < 0)
391                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
392                 }
393
394                 if (signo <= 0 || signo >= _NSIG)
395                         return bus_send_error_reply(connection, message, &error, -EINVAL);
396
397                 machine = hashmap_get(m->machines, name);
398                 if (!machine)
399                         return bus_send_error_reply(connection, message, &error, -ENOENT);
400
401                 r = machine_kill(machine, who, signo);
402                 if (r < 0)
403                         return bus_send_error_reply(connection, message, NULL, r);
404
405                 reply = dbus_message_new_method_return(message);
406                 if (!reply)
407                         goto oom;
408
409         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "TerminateMachine")) {
410                 const char *name;
411                 Machine *machine;
412
413                 if (!dbus_message_get_args(
414                                     message,
415                                     &error,
416                                     DBUS_TYPE_STRING, &name,
417                                     DBUS_TYPE_INVALID))
418                         return bus_send_error_reply(connection, message, &error, -EINVAL);
419
420                 machine = hashmap_get(m->machines, name);
421                 if (!machine)
422                         return bus_send_error_reply(connection, message, &error, -ENOENT);
423
424                 r = machine_stop(machine);
425                 if (r < 0)
426                         return bus_send_error_reply(connection, message, NULL, r);
427
428                 reply = dbus_message_new_method_return(message);
429                 if (!reply)
430                         goto oom;
431
432         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
433                 char *introspection = NULL;
434                 FILE *f;
435                 Iterator i;
436                 Machine *machine;
437                 size_t size;
438                 char *p;
439
440                 reply = dbus_message_new_method_return(message);
441                 if (!reply)
442                         goto oom;
443
444                 /* We roll our own introspection code here, instead of
445                  * relying on bus_default_message_handler() because we
446                  * need to generate our introspection string
447                  * dynamically. */
448
449                 f = open_memstream(&introspection, &size);
450                 if (!f)
451                         goto oom;
452
453                 fputs(INTROSPECTION_BEGIN, f);
454
455                 HASHMAP_FOREACH(machine, m->machines, i) {
456                         p = bus_path_escape(machine->name);
457
458                         if (p) {
459                                 fprintf(f, "<node name=\"machine/%s\"/>", p);
460                                 free(p);
461                         }
462                 }
463
464                 fputs(INTROSPECTION_END, f);
465
466                 if (ferror(f)) {
467                         fclose(f);
468                         free(introspection);
469                         goto oom;
470                 }
471
472                 fclose(f);
473
474                 if (!introspection)
475                         goto oom;
476
477                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
478                         free(introspection);
479                         goto oom;
480                 }
481
482                 free(introspection);
483         } else
484                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, NULL);
485
486         if (reply) {
487                 if (!bus_maybe_send_reply(connection, message, reply))
488                         goto oom;
489         }
490
491         return DBUS_HANDLER_RESULT_HANDLED;
492
493 oom:
494         dbus_error_free(&error);
495
496         return DBUS_HANDLER_RESULT_NEED_MEMORY;
497 }
498
499 const DBusObjectPathVTable bus_manager_vtable = {
500         .message_function = manager_message_handler
501 };
502
503 DBusHandlerResult bus_message_filter(
504                 DBusConnection *connection,
505                 DBusMessage *message,
506                 void *userdata) {
507
508         Manager *m = userdata;
509         DBusError error;
510
511         assert(m);
512         assert(connection);
513         assert(message);
514
515         dbus_error_init(&error);
516
517         log_debug("Got message: %s %s %s", strna(dbus_message_get_sender(message)), strna(dbus_message_get_interface(message)), strna(dbus_message_get_member(message)));
518
519         if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
520                 const char *path, *result, *unit;
521                 Machine *mm;
522                 uint32_t id;
523
524                 if (!dbus_message_get_args(message, &error,
525                                            DBUS_TYPE_UINT32, &id,
526                                            DBUS_TYPE_OBJECT_PATH, &path,
527                                            DBUS_TYPE_STRING, &unit,
528                                            DBUS_TYPE_STRING, &result,
529                                            DBUS_TYPE_INVALID)) {
530                         log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error));
531                         goto finish;
532                 }
533
534
535                 mm = hashmap_get(m->machine_units, unit);
536                 if (mm) {
537                         if (streq_ptr(path, mm->scope_job)) {
538                                 free(mm->scope_job);
539                                 mm->scope_job = NULL;
540
541                                 if (mm->started) {
542                                         if (streq(result, "done"))
543                                                 machine_send_create_reply(mm, NULL);
544                                         else {
545                                                 dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
546                                                 machine_send_create_reply(mm, &error);
547                                         }
548                                 }
549                         }
550
551                         machine_add_to_gc_queue(mm);
552                 }
553
554         } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
555
556                 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
557                 _cleanup_free_ char *unit = NULL;
558                 const char *path;
559
560                 path = dbus_message_get_path(message);
561                 if (!path)
562                         goto finish;
563
564                 unit_name_from_dbus_path(path, &unit);
565                 if (unit) {
566                         Machine *mm;
567
568                         mm = hashmap_get(m->machine_units, unit);
569                         if (mm)
570                                 machine_add_to_gc_queue(mm);
571                 }
572         }
573
574 finish:
575         dbus_error_free(&error);
576
577         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
578 }
579
580 int manager_start_scope(
581                 Manager *manager,
582                 const char *scope,
583                 pid_t pid,
584                 const char *slice,
585                 const char *description,
586                 DBusError *error,
587                 char **job) {
588
589         _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
590         DBusMessageIter iter, sub, sub2, sub3, sub4;
591         const char *timeout_stop_property = "TimeoutStopUSec";
592         const char *pids_property = "PIDs";
593         uint64_t timeout = 500 * USEC_PER_MSEC;
594         const char *fail = "fail";
595         uint32_t u;
596
597         assert(manager);
598         assert(scope);
599         assert(pid > 1);
600
601         if (!slice)
602                 slice = "";
603
604         m = dbus_message_new_method_call(
605                         "org.freedesktop.systemd1",
606                         "/org/freedesktop/systemd1",
607                         "org.freedesktop.systemd1.Manager",
608                         "StartTransientUnit");
609         if (!m)
610                 return log_oom();
611
612         dbus_message_iter_init_append(m, &iter);
613
614         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
615             !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
616             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
617                 return log_oom();
618
619         if (!isempty(slice)) {
620                 const char *slice_property = "Slice";
621
622                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
623                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
624                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
625                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
626                     !dbus_message_iter_close_container(&sub2, &sub3) ||
627                     !dbus_message_iter_close_container(&sub, &sub2))
628                         return log_oom();
629         }
630
631         if (!isempty(description)) {
632                 const char *description_property = "Description";
633
634                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
635                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
636                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
637                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
638                     !dbus_message_iter_close_container(&sub2, &sub3) ||
639                     !dbus_message_iter_close_container(&sub, &sub2))
640                         return log_oom();
641         }
642
643         /* cgroup empty notification is not available in containers
644          * currently. To make this less problematic, let's shorten the
645          * stop timeout for sessions, so that we don't wait
646          * forever. */
647
648         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
649             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
650             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
651             !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
652             !dbus_message_iter_close_container(&sub2, &sub3) ||
653             !dbus_message_iter_close_container(&sub, &sub2))
654                 return log_oom();
655
656         u = pid;
657         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
658             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
659             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
660             !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
661             !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
662             !dbus_message_iter_close_container(&sub3, &sub4) ||
663             !dbus_message_iter_close_container(&sub2, &sub3) ||
664             !dbus_message_iter_close_container(&sub, &sub2) ||
665             !dbus_message_iter_close_container(&iter, &sub))
666                 return log_oom();
667
668         reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
669         if (!reply)
670                 return -EIO;
671
672         if (job) {
673                 const char *j;
674                 char *copy;
675
676                 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
677                         return -EIO;
678
679                 copy = strdup(j);
680                 if (!copy)
681                         return -ENOMEM;
682
683                 *job = copy;
684         }
685
686         return 0;
687 }
688
689 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
690         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
691         const char *fail = "fail";
692         int r;
693
694         assert(manager);
695         assert(unit);
696
697         r = bus_method_call_with_reply(
698                         manager->bus,
699                         "org.freedesktop.systemd1",
700                         "/org/freedesktop/systemd1",
701                         "org.freedesktop.systemd1.Manager",
702                         "StopUnit",
703                         &reply,
704                         error,
705                         DBUS_TYPE_STRING, &unit,
706                         DBUS_TYPE_STRING, &fail,
707                         DBUS_TYPE_INVALID);
708         if (r < 0) {
709                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
710                 return r;
711         }
712
713         if (job) {
714                 const char *j;
715                 char *copy;
716
717                 if (!dbus_message_get_args(reply, error,
718                                            DBUS_TYPE_OBJECT_PATH, &j,
719                                            DBUS_TYPE_INVALID)) {
720                         log_error("Failed to parse reply.");
721                         return -EIO;
722                 }
723
724                 copy = strdup(j);
725                 if (!copy)
726                         return -ENOMEM;
727
728                 *job = copy;
729         }
730
731         return 0;
732 }
733
734 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
735         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
736         const char *w;
737         int r;
738
739         assert(manager);
740         assert(unit);
741
742         w = who == KILL_LEADER ? "process" : "cgroup";
743         assert_cc(sizeof(signo) == sizeof(int32_t));
744
745         r = bus_method_call_with_reply(
746                         manager->bus,
747                         "org.freedesktop.systemd1",
748                         "/org/freedesktop/systemd1",
749                         "org.freedesktop.systemd1.Manager",
750                         "KillUnit",
751                         &reply,
752                         error,
753                         DBUS_TYPE_STRING, &unit,
754                         DBUS_TYPE_STRING, &w,
755                         DBUS_TYPE_INT32, &signo,
756                         DBUS_TYPE_INVALID);
757         if (r < 0) {
758                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
759                 return r;
760         }
761
762         return 0;
763 }
764
765 int manager_unit_is_active(Manager *manager, const char *unit) {
766
767         const char *interface = "org.freedesktop.systemd1.Unit";
768         const char *property = "ActiveState";
769         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
770         _cleanup_free_ char *path = NULL;
771         DBusMessageIter iter, sub;
772         const char *state;
773         DBusError error;
774         int r;
775
776         assert(manager);
777         assert(unit);
778
779         dbus_error_init(&error);
780
781         path = unit_dbus_path_from_name(unit);
782         if (!path)
783                 return -ENOMEM;
784
785         r = bus_method_call_with_reply(
786                         manager->bus,
787                         "org.freedesktop.systemd1",
788                         path,
789                         "org.freedesktop.DBus.Properties",
790                         "Get",
791                         &reply,
792                         &error,
793                         DBUS_TYPE_STRING, &interface,
794                         DBUS_TYPE_STRING, &property,
795                         DBUS_TYPE_INVALID);
796
797         if (r < 0) {
798                 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
799                 dbus_error_free(&error);
800                 return r;
801         }
802
803         if (!dbus_message_iter_init(reply, &iter) ||
804             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
805                 log_error("Failed to parse reply.");
806                 return -EINVAL;
807         }
808
809         dbus_message_iter_recurse(&iter, &sub);
810         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
811                 log_error("Failed to parse reply.");
812                 return -EINVAL;
813         }
814
815         dbus_message_iter_get_basic(&sub, &state);
816
817         return !streq(state, "inactive") && !streq(state, "failed");
818 }