chiark / gitweb /
machined: split out machine registration stuff from logind
[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, &slice);
184         if (!(isempty(slice) || (unit_name_is_valid(slice, false) && endswith(slice, ".slice"))) ||
185             !dbus_message_iter_next(&iter) ||
186             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
187                 return -EINVAL;
188
189         dbus_message_iter_get_basic(&iter, &root_directory);
190
191         if (!(isempty(root_directory) || path_is_absolute(root_directory)))
192                 return -EINVAL;
193
194         if (hashmap_get(manager->machines, name))
195                 return -EEXIST;
196
197         if (leader <= 0) {
198                 leader = bus_get_unix_process_id(manager->bus, dbus_message_get_sender(message), NULL);
199                 if (leader == 0)
200                         return -EINVAL;
201         }
202
203         r = manager_add_machine(manager, name, &m);
204         if (r < 0)
205                 goto fail;
206
207         m->leader = leader;
208         m->class = c;
209         m->id = id;
210
211         if (!isempty(service)) {
212                 m->service = strdup(service);
213                 if (!m->service) {
214                         r = -ENOMEM;
215                         goto fail;
216                 }
217         }
218
219         if (!isempty(root_directory)) {
220                 m->root_directory = strdup(root_directory);
221                 if (!m->root_directory) {
222                         r = -ENOMEM;
223                         goto fail;
224                 }
225         }
226
227         r = machine_start(m);
228         if (r < 0)
229                 goto fail;
230
231         m->create_message = dbus_message_ref(message);
232
233         return 0;
234
235 fail:
236         if (m)
237                 machine_add_to_gc_queue(m);
238
239         return r;
240 }
241
242 static DBusHandlerResult manager_message_handler(
243                 DBusConnection *connection,
244                 DBusMessage *message,
245                 void *userdata) {
246
247         Manager *m = userdata;
248
249         DBusError error;
250         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
251         int r;
252
253         assert(connection);
254         assert(message);
255         assert(m);
256
257         dbus_error_init(&error);
258
259         if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachine")) {
260                 Machine *machine;
261                 const char *name;
262                 char *p;
263                 bool b;
264
265                 if (!dbus_message_get_args(
266                                     message,
267                                     &error,
268                                     DBUS_TYPE_STRING, &name,
269                                     DBUS_TYPE_INVALID))
270                         return bus_send_error_reply(connection, message, &error, -EINVAL);
271
272                 machine = hashmap_get(m->machines, name);
273                 if (!machine)
274                         return bus_send_error_reply(connection, message, &error, -ENOENT);
275
276                 reply = dbus_message_new_method_return(message);
277                 if (!reply)
278                         goto oom;
279
280                 p = machine_bus_path(machine);
281                 if (!p)
282                         goto oom;
283
284                 b = dbus_message_append_args(
285                                 reply,
286                                 DBUS_TYPE_OBJECT_PATH, &p,
287                                 DBUS_TYPE_INVALID);
288                 free(p);
289
290                 if (!b)
291                         goto oom;
292
293         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachineByPID")) {
294                 uint32_t pid;
295                 char *p;
296                 Machine *machine;
297                 bool b;
298
299                 if (!dbus_message_get_args(
300                                     message,
301                                     &error,
302                                     DBUS_TYPE_UINT32, &pid,
303                                     DBUS_TYPE_INVALID))
304                         return bus_send_error_reply(connection, message, &error, -EINVAL);
305
306                 r = manager_get_machine_by_pid(m, pid, &machine);
307                 if (r <= 0)
308                         return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
309
310                 reply = dbus_message_new_method_return(message);
311                 if (!reply)
312                         goto oom;
313
314                 p = machine_bus_path(machine);
315                 if (!p)
316                         goto oom;
317
318                 b = dbus_message_append_args(
319                                 reply,
320                                 DBUS_TYPE_OBJECT_PATH, &p,
321                                 DBUS_TYPE_INVALID);
322                 free(p);
323
324                 if (!b)
325                         goto oom;
326
327         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "ListMachines")) {
328                 Machine *machine;
329                 Iterator i;
330                 DBusMessageIter iter, sub;
331
332                 reply = dbus_message_new_method_return(message);
333                 if (!reply)
334                         goto oom;
335
336                 dbus_message_iter_init_append(reply, &iter);
337
338                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssso)", &sub))
339                         goto oom;
340
341                 HASHMAP_FOREACH(machine, m->machines, i) {
342                         _cleanup_free_ char *p = NULL;
343                         DBusMessageIter sub2;
344                         const char *class;
345
346                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
347                                 goto oom;
348
349                         p = machine_bus_path(machine);
350                         if (!p)
351                                 goto oom;
352
353                         class = strempty(machine_class_to_string(machine->class));
354
355                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->name) ||
356                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &class) ||
357                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->service) ||
358                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
359                                 free(p);
360                                 goto oom;
361                         }
362
363                         if (!dbus_message_iter_close_container(&sub, &sub2))
364                                 goto oom;
365                 }
366
367                 if (!dbus_message_iter_close_container(&iter, &sub))
368                         goto oom;
369
370         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "CreateMachine")) {
371
372                 r = bus_manager_create_machine(m, message);
373                 if (r < 0)
374                         return bus_send_error_reply(connection, message, NULL, r);
375
376         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "KillMachine")) {
377                 const char *swho;
378                 int32_t signo;
379                 KillWho who;
380                 const char *name;
381                 Machine *machine;
382
383                 if (!dbus_message_get_args(
384                                     message,
385                                     &error,
386                                     DBUS_TYPE_STRING, &name,
387                                     DBUS_TYPE_STRING, &swho,
388                                     DBUS_TYPE_INT32, &signo,
389                                     DBUS_TYPE_INVALID))
390                         return bus_send_error_reply(connection, message, &error, -EINVAL);
391
392                 if (isempty(swho))
393                         who = KILL_ALL;
394                 else {
395                         who = kill_who_from_string(swho);
396                         if (who < 0)
397                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
398                 }
399
400                 if (signo <= 0 || signo >= _NSIG)
401                         return bus_send_error_reply(connection, message, &error, -EINVAL);
402
403                 machine = hashmap_get(m->machines, name);
404                 if (!machine)
405                         return bus_send_error_reply(connection, message, &error, -ENOENT);
406
407                 r = machine_kill(machine, who, signo);
408                 if (r < 0)
409                         return bus_send_error_reply(connection, message, NULL, r);
410
411                 reply = dbus_message_new_method_return(message);
412                 if (!reply)
413                         goto oom;
414
415         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "TerminateMachine")) {
416                 const char *name;
417                 Machine *machine;
418
419                 if (!dbus_message_get_args(
420                                     message,
421                                     &error,
422                                     DBUS_TYPE_STRING, &name,
423                                     DBUS_TYPE_INVALID))
424                         return bus_send_error_reply(connection, message, &error, -EINVAL);
425
426                 machine = hashmap_get(m->machines, name);
427                 if (!machine)
428                         return bus_send_error_reply(connection, message, &error, -ENOENT);
429
430                 r = machine_stop(machine);
431                 if (r < 0)
432                         return bus_send_error_reply(connection, message, NULL, r);
433
434                 reply = dbus_message_new_method_return(message);
435                 if (!reply)
436                         goto oom;
437
438         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
439                 char *introspection = NULL;
440                 FILE *f;
441                 Iterator i;
442                 Machine *machine;
443                 size_t size;
444                 char *p;
445
446                 reply = dbus_message_new_method_return(message);
447                 if (!reply)
448                         goto oom;
449
450                 /* We roll our own introspection code here, instead of
451                  * relying on bus_default_message_handler() because we
452                  * need to generate our introspection string
453                  * dynamically. */
454
455                 f = open_memstream(&introspection, &size);
456                 if (!f)
457                         goto oom;
458
459                 fputs(INTROSPECTION_BEGIN, f);
460
461                 HASHMAP_FOREACH(machine, m->machines, i) {
462                         p = bus_path_escape(machine->name);
463
464                         if (p) {
465                                 fprintf(f, "<node name=\"machine/%s\"/>", p);
466                                 free(p);
467                         }
468                 }
469
470                 fputs(INTROSPECTION_END, f);
471
472                 if (ferror(f)) {
473                         fclose(f);
474                         free(introspection);
475                         goto oom;
476                 }
477
478                 fclose(f);
479
480                 if (!introspection)
481                         goto oom;
482
483                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
484                         free(introspection);
485                         goto oom;
486                 }
487
488                 free(introspection);
489         } else
490                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, NULL);
491
492         if (reply) {
493                 if (!bus_maybe_send_reply(connection, message, reply))
494                         goto oom;
495         }
496
497         return DBUS_HANDLER_RESULT_HANDLED;
498
499 oom:
500         dbus_error_free(&error);
501
502         return DBUS_HANDLER_RESULT_NEED_MEMORY;
503 }
504
505 const DBusObjectPathVTable bus_manager_vtable = {
506         .message_function = manager_message_handler
507 };
508
509 DBusHandlerResult bus_message_filter(
510                 DBusConnection *connection,
511                 DBusMessage *message,
512                 void *userdata) {
513
514         Manager *m = userdata;
515         DBusError error;
516
517         assert(m);
518         assert(connection);
519         assert(message);
520
521         dbus_error_init(&error);
522
523         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)));
524
525         if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
526                 const char *path, *result, *unit;
527                 Machine *mm;
528                 uint32_t id;
529
530                 if (!dbus_message_get_args(message, &error,
531                                            DBUS_TYPE_UINT32, &id,
532                                            DBUS_TYPE_OBJECT_PATH, &path,
533                                            DBUS_TYPE_STRING, &unit,
534                                            DBUS_TYPE_STRING, &result,
535                                            DBUS_TYPE_INVALID)) {
536                         log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error));
537                         goto finish;
538                 }
539
540
541                 mm = hashmap_get(m->machine_units, unit);
542                 if (mm) {
543                         if (streq_ptr(path, mm->scope_job)) {
544                                 free(mm->scope_job);
545                                 mm->scope_job = NULL;
546
547                                 if (mm->started) {
548                                         if (streq(result, "done"))
549                                                 machine_send_create_reply(mm, NULL);
550                                         else {
551                                                 dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
552                                                 machine_send_create_reply(mm, &error);
553                                         }
554                                 }
555                         }
556
557                         machine_add_to_gc_queue(mm);
558                 }
559
560         } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
561
562                 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
563                 _cleanup_free_ char *unit = NULL;
564                 const char *path;
565
566                 path = dbus_message_get_path(message);
567                 if (!path)
568                         goto finish;
569
570                 unit_name_from_dbus_path(path, &unit);
571                 if (unit) {
572                         Machine *mm;
573
574                         mm = hashmap_get(m->machine_units, unit);
575                         if (mm)
576                                 machine_add_to_gc_queue(mm);
577                 }
578         }
579
580 finish:
581         dbus_error_free(&error);
582
583         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
584 }
585
586 int manager_start_scope(
587                 Manager *manager,
588                 const char *scope,
589                 pid_t pid,
590                 const char *slice,
591                 const char *description,
592                 DBusError *error,
593                 char **job) {
594
595         _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
596         DBusMessageIter iter, sub, sub2, sub3, sub4;
597         const char *timeout_stop_property = "TimeoutStopUSec";
598         const char *pids_property = "PIDs";
599         uint64_t timeout = 500 * USEC_PER_MSEC;
600         const char *fail = "fail";
601         uint32_t u;
602
603         assert(manager);
604         assert(scope);
605         assert(pid > 1);
606
607         if (!slice)
608                 slice = "";
609
610         m = dbus_message_new_method_call(
611                         "org.freedesktop.systemd1",
612                         "/org/freedesktop/systemd1",
613                         "org.freedesktop.systemd1.Manager",
614                         "StartTransientUnit");
615         if (!m)
616                 return log_oom();
617
618         dbus_message_iter_init_append(m, &iter);
619
620         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
621             !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
622             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
623                 return log_oom();
624
625         if (!isempty(slice)) {
626                 const char *slice_property = "Slice";
627
628                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
629                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
630                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
631                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
632                     !dbus_message_iter_close_container(&sub2, &sub3) ||
633                     !dbus_message_iter_close_container(&sub, &sub2))
634                         return log_oom();
635         }
636
637         if (!isempty(description)) {
638                 const char *description_property = "Description";
639
640                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
641                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
642                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
643                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
644                     !dbus_message_iter_close_container(&sub2, &sub3) ||
645                     !dbus_message_iter_close_container(&sub, &sub2))
646                         return log_oom();
647         }
648
649         /* cgroup empty notification is not available in containers
650          * currently. To make this less problematic, let's shorten the
651          * stop timeout for sessions, so that we don't wait
652          * forever. */
653
654         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
655             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
656             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
657             !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
658             !dbus_message_iter_close_container(&sub2, &sub3) ||
659             !dbus_message_iter_close_container(&sub, &sub2))
660                 return log_oom();
661
662         u = pid;
663         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
664             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
665             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
666             !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
667             !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
668             !dbus_message_iter_close_container(&sub3, &sub4) ||
669             !dbus_message_iter_close_container(&sub2, &sub3) ||
670             !dbus_message_iter_close_container(&sub, &sub2) ||
671             !dbus_message_iter_close_container(&iter, &sub))
672                 return log_oom();
673
674         reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
675         if (!reply)
676                 return -EIO;
677
678         if (job) {
679                 const char *j;
680                 char *copy;
681
682                 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
683                         return -EIO;
684
685                 copy = strdup(j);
686                 if (!copy)
687                         return -ENOMEM;
688
689                 *job = copy;
690         }
691
692         return 0;
693 }
694
695 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
696         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
697         const char *fail = "fail";
698         int r;
699
700         assert(manager);
701         assert(unit);
702
703         r = bus_method_call_with_reply(
704                         manager->bus,
705                         "org.freedesktop.systemd1",
706                         "/org/freedesktop/systemd1",
707                         "org.freedesktop.systemd1.Manager",
708                         "StopUnit",
709                         &reply,
710                         error,
711                         DBUS_TYPE_STRING, &unit,
712                         DBUS_TYPE_STRING, &fail,
713                         DBUS_TYPE_INVALID);
714         if (r < 0) {
715                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
716                 return r;
717         }
718
719         if (job) {
720                 const char *j;
721                 char *copy;
722
723                 if (!dbus_message_get_args(reply, error,
724                                            DBUS_TYPE_OBJECT_PATH, &j,
725                                            DBUS_TYPE_INVALID)) {
726                         log_error("Failed to parse reply.");
727                         return -EIO;
728                 }
729
730                 copy = strdup(j);
731                 if (!copy)
732                         return -ENOMEM;
733
734                 *job = copy;
735         }
736
737         return 0;
738 }
739
740 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
741         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
742         const char *w;
743         int r;
744
745         assert(manager);
746         assert(unit);
747
748         w = who == KILL_LEADER ? "process" : "cgroup";
749         assert_cc(sizeof(signo) == sizeof(int32_t));
750
751         r = bus_method_call_with_reply(
752                         manager->bus,
753                         "org.freedesktop.systemd1",
754                         "/org/freedesktop/systemd1",
755                         "org.freedesktop.systemd1.Manager",
756                         "KillUnit",
757                         &reply,
758                         error,
759                         DBUS_TYPE_STRING, &unit,
760                         DBUS_TYPE_STRING, &w,
761                         DBUS_TYPE_INT32, &signo,
762                         DBUS_TYPE_INVALID);
763         if (r < 0) {
764                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
765                 return r;
766         }
767
768         return 0;
769 }
770
771 int manager_unit_is_active(Manager *manager, const char *unit) {
772
773         const char *interface = "org.freedesktop.systemd1.Unit";
774         const char *property = "ActiveState";
775         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
776         _cleanup_free_ char *path = NULL;
777         DBusMessageIter iter, sub;
778         const char *state;
779         DBusError error;
780         int r;
781
782         assert(manager);
783         assert(unit);
784
785         dbus_error_init(&error);
786
787         path = unit_dbus_path_from_name(unit);
788         if (!path)
789                 return -ENOMEM;
790
791         r = bus_method_call_with_reply(
792                         manager->bus,
793                         "org.freedesktop.systemd1",
794                         path,
795                         "org.freedesktop.DBus.Properties",
796                         "Get",
797                         &reply,
798                         &error,
799                         DBUS_TYPE_STRING, &interface,
800                         DBUS_TYPE_STRING, &property,
801                         DBUS_TYPE_INVALID);
802
803         if (r < 0) {
804                 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
805                 dbus_error_free(&error);
806                 return r;
807         }
808
809         if (!dbus_message_iter_init(reply, &iter) ||
810             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
811                 log_error("Failed to parse reply.");
812                 return -EINVAL;
813         }
814
815         dbus_message_iter_recurse(&iter, &sub);
816         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
817                 log_error("Failed to parse reply.");
818                 return -EINVAL;
819         }
820
821         dbus_message_iter_get_basic(&sub, &state);
822
823         return !streq(state, "inactive") && !streq(state, "failed");
824 }