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