chiark / gitweb /
1b1eb3a333728a9522f7856ed95e3eca5c9b9cff
[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, *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                 mm = hashmap_get(m->machine_units, unit);
535                 if (mm) {
536                         if (streq_ptr(path, mm->scope_job)) {
537                                 free(mm->scope_job);
538                                 mm->scope_job = NULL;
539                                 machine_save(mm);
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         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
574                 const char *path, *unit;
575                 Machine *mm;
576
577                 if (!dbus_message_get_args(message, &error,
578                                            DBUS_TYPE_STRING, &unit,
579                                            DBUS_TYPE_OBJECT_PATH, &path,
580                                            DBUS_TYPE_INVALID)) {
581                         log_error("Failed to parse UnitRemoved message: %s", bus_error_message(&error));
582                         goto finish;
583                 }
584
585                 mm = hashmap_get(m->machine_units, unit);
586                 if (mm) {
587                         hashmap_remove(m->machine_units, mm->scope);
588                         free(mm->scope);
589                         mm->scope = NULL;
590
591                         machine_add_to_gc_queue(mm);
592                 }
593         }
594
595 finish:
596         dbus_error_free(&error);
597
598         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
599 }
600
601 int manager_start_scope(
602                 Manager *manager,
603                 const char *scope,
604                 pid_t pid,
605                 const char *slice,
606                 const char *description,
607                 DBusError *error,
608                 char **job) {
609
610         _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
611         DBusMessageIter iter, sub, sub2, sub3, sub4;
612         const char *timeout_stop_property = "TimeoutStopUSec";
613         const char *pids_property = "PIDs";
614         uint64_t timeout = 500 * USEC_PER_MSEC;
615         const char *fail = "fail";
616         uint32_t u;
617
618         assert(manager);
619         assert(scope);
620         assert(pid > 1);
621
622         if (!slice)
623                 slice = "";
624
625         m = dbus_message_new_method_call(
626                         "org.freedesktop.systemd1",
627                         "/org/freedesktop/systemd1",
628                         "org.freedesktop.systemd1.Manager",
629                         "StartTransientUnit");
630         if (!m)
631                 return log_oom();
632
633         dbus_message_iter_init_append(m, &iter);
634
635         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
636             !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
637             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
638                 return log_oom();
639
640         if (!isempty(slice)) {
641                 const char *slice_property = "Slice";
642
643                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
644                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
645                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
646                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
647                     !dbus_message_iter_close_container(&sub2, &sub3) ||
648                     !dbus_message_iter_close_container(&sub, &sub2))
649                         return log_oom();
650         }
651
652         if (!isempty(description)) {
653                 const char *description_property = "Description";
654
655                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
656                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
657                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
658                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
659                     !dbus_message_iter_close_container(&sub2, &sub3) ||
660                     !dbus_message_iter_close_container(&sub, &sub2))
661                         return log_oom();
662         }
663
664         /* cgroup empty notification is not available in containers
665          * currently. To make this less problematic, let's shorten the
666          * stop timeout for sessions, so that we don't wait
667          * forever. */
668
669         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
670             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
671             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
672             !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
673             !dbus_message_iter_close_container(&sub2, &sub3) ||
674             !dbus_message_iter_close_container(&sub, &sub2))
675                 return log_oom();
676
677         u = pid;
678         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
679             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
680             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
681             !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
682             !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
683             !dbus_message_iter_close_container(&sub3, &sub4) ||
684             !dbus_message_iter_close_container(&sub2, &sub3) ||
685             !dbus_message_iter_close_container(&sub, &sub2) ||
686             !dbus_message_iter_close_container(&iter, &sub))
687                 return log_oom();
688
689         reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
690         if (!reply)
691                 return -EIO;
692
693         if (job) {
694                 const char *j;
695                 char *copy;
696
697                 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
698                         return -EIO;
699
700                 copy = strdup(j);
701                 if (!copy)
702                         return -ENOMEM;
703
704                 *job = copy;
705         }
706
707         return 0;
708 }
709
710 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
711         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
712         const char *fail = "fail";
713         int r;
714
715         assert(manager);
716         assert(unit);
717
718         r = bus_method_call_with_reply(
719                         manager->bus,
720                         "org.freedesktop.systemd1",
721                         "/org/freedesktop/systemd1",
722                         "org.freedesktop.systemd1.Manager",
723                         "StopUnit",
724                         &reply,
725                         error,
726                         DBUS_TYPE_STRING, &unit,
727                         DBUS_TYPE_STRING, &fail,
728                         DBUS_TYPE_INVALID);
729         if (r < 0) {
730                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
731                 return r;
732         }
733
734         if (job) {
735                 const char *j;
736                 char *copy;
737
738                 if (!dbus_message_get_args(reply, error,
739                                            DBUS_TYPE_OBJECT_PATH, &j,
740                                            DBUS_TYPE_INVALID)) {
741                         log_error("Failed to parse reply.");
742                         return -EIO;
743                 }
744
745                 copy = strdup(j);
746                 if (!copy)
747                         return -ENOMEM;
748
749                 *job = copy;
750         }
751
752         return 0;
753 }
754
755 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
756         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
757         const char *w;
758         int r;
759
760         assert(manager);
761         assert(unit);
762
763         w = who == KILL_LEADER ? "process" : "cgroup";
764         assert_cc(sizeof(signo) == sizeof(int32_t));
765
766         r = bus_method_call_with_reply(
767                         manager->bus,
768                         "org.freedesktop.systemd1",
769                         "/org/freedesktop/systemd1",
770                         "org.freedesktop.systemd1.Manager",
771                         "KillUnit",
772                         &reply,
773                         error,
774                         DBUS_TYPE_STRING, &unit,
775                         DBUS_TYPE_STRING, &w,
776                         DBUS_TYPE_INT32, &signo,
777                         DBUS_TYPE_INVALID);
778         if (r < 0) {
779                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
780                 return r;
781         }
782
783         return 0;
784 }
785
786 int manager_unit_is_active(Manager *manager, const char *unit) {
787
788         const char *interface = "org.freedesktop.systemd1.Unit";
789         const char *property = "ActiveState";
790         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
791         _cleanup_free_ char *path = NULL;
792         DBusMessageIter iter, sub;
793         const char *state;
794         DBusError error;
795         int r;
796
797         assert(manager);
798         assert(unit);
799
800         dbus_error_init(&error);
801
802         path = unit_dbus_path_from_name(unit);
803         if (!path)
804                 return -ENOMEM;
805
806         r = bus_method_call_with_reply(
807                         manager->bus,
808                         "org.freedesktop.systemd1",
809                         path,
810                         "org.freedesktop.DBus.Properties",
811                         "Get",
812                         &reply,
813                         &error,
814                         DBUS_TYPE_STRING, &interface,
815                         DBUS_TYPE_STRING, &property,
816                         DBUS_TYPE_INVALID);
817
818         if (r < 0) {
819                 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
820                 dbus_error_free(&error);
821                 return r;
822         }
823
824         if (!dbus_message_iter_init(reply, &iter) ||
825             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
826                 log_error("Failed to parse reply.");
827                 return -EINVAL;
828         }
829
830         dbus_message_iter_recurse(&iter, &sub);
831         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
832                 log_error("Failed to parse reply.");
833                 return -EINVAL;
834         }
835
836         dbus_message_iter_get_basic(&sub, &state);
837
838         return !streq(state, "inactive") && !streq(state, "failed");
839 }