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