chiark / gitweb /
logind: update state file after generating the session fifo, not before
[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 (!dbus_message_iter_next(&iter) ||
189             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
190             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)
191                 return -EINVAL;
192
193         dbus_message_iter_recurse(&iter, &sub);
194
195         if (hashmap_get(manager->machines, name))
196                 return -EEXIST;
197
198         if (leader <= 0) {
199                 leader = bus_get_unix_process_id(manager->bus, dbus_message_get_sender(message), NULL);
200                 if (leader == 0)
201                         return -EINVAL;
202         }
203
204         r = manager_add_machine(manager, name, &m);
205         if (r < 0)
206                 goto fail;
207
208         m->leader = leader;
209         m->class = c;
210         m->id = id;
211
212         if (!isempty(service)) {
213                 m->service = strdup(service);
214                 if (!m->service) {
215                         r = -ENOMEM;
216                         goto fail;
217                 }
218         }
219
220         if (!isempty(root_directory)) {
221                 m->root_directory = strdup(root_directory);
222                 if (!m->root_directory) {
223                         r = -ENOMEM;
224                         goto fail;
225                 }
226         }
227
228         r = machine_start(m, &sub);
229         if (r < 0)
230                 goto fail;
231
232         m->create_message = dbus_message_ref(message);
233
234         return 0;
235
236 fail:
237         if (m)
238                 machine_add_to_gc_queue(m);
239
240         return r;
241 }
242
243 static DBusHandlerResult manager_message_handler(
244                 DBusConnection *connection,
245                 DBusMessage *message,
246                 void *userdata) {
247
248         Manager *m = userdata;
249
250         DBusError error;
251         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
252         int r;
253
254         assert(connection);
255         assert(message);
256         assert(m);
257
258         dbus_error_init(&error);
259
260         if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachine")) {
261                 Machine *machine;
262                 const char *name;
263                 char *p;
264                 bool b;
265
266                 if (!dbus_message_get_args(
267                                     message,
268                                     &error,
269                                     DBUS_TYPE_STRING, &name,
270                                     DBUS_TYPE_INVALID))
271                         return bus_send_error_reply(connection, message, &error, -EINVAL);
272
273                 machine = hashmap_get(m->machines, name);
274                 if (!machine)
275                         return bus_send_error_reply(connection, message, &error, -ENOENT);
276
277                 reply = dbus_message_new_method_return(message);
278                 if (!reply)
279                         goto oom;
280
281                 p = machine_bus_path(machine);
282                 if (!p)
283                         goto oom;
284
285                 b = dbus_message_append_args(
286                                 reply,
287                                 DBUS_TYPE_OBJECT_PATH, &p,
288                                 DBUS_TYPE_INVALID);
289                 free(p);
290
291                 if (!b)
292                         goto oom;
293
294         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachineByPID")) {
295                 uint32_t pid;
296                 char *p;
297                 Machine *machine;
298                 bool b;
299
300                 if (!dbus_message_get_args(
301                                     message,
302                                     &error,
303                                     DBUS_TYPE_UINT32, &pid,
304                                     DBUS_TYPE_INVALID))
305                         return bus_send_error_reply(connection, message, &error, -EINVAL);
306
307                 r = manager_get_machine_by_pid(m, pid, &machine);
308                 if (r <= 0)
309                         return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
310
311                 reply = dbus_message_new_method_return(message);
312                 if (!reply)
313                         goto oom;
314
315                 p = machine_bus_path(machine);
316                 if (!p)
317                         goto oom;
318
319                 b = dbus_message_append_args(
320                                 reply,
321                                 DBUS_TYPE_OBJECT_PATH, &p,
322                                 DBUS_TYPE_INVALID);
323                 free(p);
324
325                 if (!b)
326                         goto oom;
327
328         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "ListMachines")) {
329                 Machine *machine;
330                 Iterator i;
331                 DBusMessageIter iter, sub;
332
333                 reply = dbus_message_new_method_return(message);
334                 if (!reply)
335                         goto oom;
336
337                 dbus_message_iter_init_append(reply, &iter);
338
339                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssso)", &sub))
340                         goto oom;
341
342                 HASHMAP_FOREACH(machine, m->machines, i) {
343                         _cleanup_free_ char *p = NULL;
344                         DBusMessageIter sub2;
345                         const char *class;
346
347                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
348                                 goto oom;
349
350                         p = machine_bus_path(machine);
351                         if (!p)
352                                 goto oom;
353
354                         class = strempty(machine_class_to_string(machine->class));
355
356                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->name) ||
357                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &class) ||
358                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->service) ||
359                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
360                                 free(p);
361                                 goto oom;
362                         }
363
364                         if (!dbus_message_iter_close_container(&sub, &sub2))
365                                 goto oom;
366                 }
367
368                 if (!dbus_message_iter_close_container(&iter, &sub))
369                         goto oom;
370
371         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "CreateMachine")) {
372
373                 r = bus_manager_create_machine(m, message);
374                 if (r < 0)
375                         return bus_send_error_reply(connection, message, NULL, r);
376
377         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "KillMachine")) {
378                 const char *swho;
379                 int32_t signo;
380                 KillWho who;
381                 const char *name;
382                 Machine *machine;
383
384                 if (!dbus_message_get_args(
385                                     message,
386                                     &error,
387                                     DBUS_TYPE_STRING, &name,
388                                     DBUS_TYPE_STRING, &swho,
389                                     DBUS_TYPE_INT32, &signo,
390                                     DBUS_TYPE_INVALID))
391                         return bus_send_error_reply(connection, message, &error, -EINVAL);
392
393                 if (isempty(swho))
394                         who = KILL_ALL;
395                 else {
396                         who = kill_who_from_string(swho);
397                         if (who < 0)
398                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
399                 }
400
401                 if (signo <= 0 || signo >= _NSIG)
402                         return bus_send_error_reply(connection, message, &error, -EINVAL);
403
404                 machine = hashmap_get(m->machines, name);
405                 if (!machine)
406                         return bus_send_error_reply(connection, message, &error, -ENOENT);
407
408                 r = machine_kill(machine, who, signo);
409                 if (r < 0)
410                         return bus_send_error_reply(connection, message, NULL, r);
411
412                 reply = dbus_message_new_method_return(message);
413                 if (!reply)
414                         goto oom;
415
416         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "TerminateMachine")) {
417                 const char *name;
418                 Machine *machine;
419
420                 if (!dbus_message_get_args(
421                                     message,
422                                     &error,
423                                     DBUS_TYPE_STRING, &name,
424                                     DBUS_TYPE_INVALID))
425                         return bus_send_error_reply(connection, message, &error, -EINVAL);
426
427                 machine = hashmap_get(m->machines, name);
428                 if (!machine)
429                         return bus_send_error_reply(connection, message, &error, -ENOENT);
430
431                 r = machine_stop(machine);
432                 if (r < 0)
433                         return bus_send_error_reply(connection, message, NULL, r);
434
435                 reply = dbus_message_new_method_return(message);
436                 if (!reply)
437                         goto oom;
438
439         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
440                 char *introspection = NULL;
441                 FILE *f;
442                 Iterator i;
443                 Machine *machine;
444                 size_t size;
445                 char *p;
446
447                 reply = dbus_message_new_method_return(message);
448                 if (!reply)
449                         goto oom;
450
451                 /* We roll our own introspection code here, instead of
452                  * relying on bus_default_message_handler() because we
453                  * need to generate our introspection string
454                  * dynamically. */
455
456                 f = open_memstream(&introspection, &size);
457                 if (!f)
458                         goto oom;
459
460                 fputs(INTROSPECTION_BEGIN, f);
461
462                 HASHMAP_FOREACH(machine, m->machines, i) {
463                         p = bus_path_escape(machine->name);
464
465                         if (p) {
466                                 fprintf(f, "<node name=\"machine/%s\"/>", p);
467                                 free(p);
468                         }
469                 }
470
471                 fputs(INTROSPECTION_END, f);
472
473                 if (ferror(f)) {
474                         fclose(f);
475                         free(introspection);
476                         goto oom;
477                 }
478
479                 fclose(f);
480
481                 if (!introspection)
482                         goto oom;
483
484                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
485                         free(introspection);
486                         goto oom;
487                 }
488
489                 free(introspection);
490         } else
491                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, NULL);
492
493         if (reply) {
494                 if (!bus_maybe_send_reply(connection, message, reply))
495                         goto oom;
496         }
497
498         return DBUS_HANDLER_RESULT_HANDLED;
499
500 oom:
501         dbus_error_free(&error);
502
503         return DBUS_HANDLER_RESULT_NEED_MEMORY;
504 }
505
506 const DBusObjectPathVTable bus_manager_vtable = {
507         .message_function = manager_message_handler
508 };
509
510 DBusHandlerResult bus_message_filter(
511                 DBusConnection *connection,
512                 DBusMessage *message,
513                 void *userdata) {
514
515         Manager *m = userdata;
516         DBusError error;
517
518         assert(m);
519         assert(connection);
520         assert(message);
521
522         dbus_error_init(&error);
523
524         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)));
525
526         if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
527                 const char *path, *result, *unit;
528                 Machine *mm;
529                 uint32_t id;
530
531                 if (!dbus_message_get_args(message, &error,
532                                            DBUS_TYPE_UINT32, &id,
533                                            DBUS_TYPE_OBJECT_PATH, &path,
534                                            DBUS_TYPE_STRING, &unit,
535                                            DBUS_TYPE_STRING, &result,
536                                            DBUS_TYPE_INVALID)) {
537                         log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error));
538                         goto finish;
539                 }
540
541                 mm = hashmap_get(m->machine_units, unit);
542                 if (mm) {
543                         if (streq_ptr(path, mm->scope_job)) {
544                                 free(mm->scope_job);
545                                 mm->scope_job = NULL;
546
547                                 if (mm->started) {
548                                         if (streq(result, "done"))
549                                                 machine_send_create_reply(mm, NULL);
550                                         else {
551                                                 dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
552                                                 machine_send_create_reply(mm, &error);
553                                         }
554                                 }
555
556                                 machine_save(mm);
557                         }
558
559                         machine_add_to_gc_queue(mm);
560                 }
561
562         } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
563
564                 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
565                 _cleanup_free_ char *unit = NULL;
566                 const char *path;
567
568                 path = dbus_message_get_path(message);
569                 if (!path)
570                         goto finish;
571
572                 unit_name_from_dbus_path(path, &unit);
573                 if (unit) {
574                         Machine *mm;
575
576                         mm = hashmap_get(m->machine_units, unit);
577                         if (mm)
578                                 machine_add_to_gc_queue(mm);
579                 }
580
581         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
582                 const char *path, *unit;
583                 Machine *mm;
584
585                 if (!dbus_message_get_args(message, &error,
586                                            DBUS_TYPE_STRING, &unit,
587                                            DBUS_TYPE_OBJECT_PATH, &path,
588                                            DBUS_TYPE_INVALID)) {
589                         log_error("Failed to parse UnitRemoved message: %s", bus_error_message(&error));
590                         goto finish;
591                 }
592
593                 mm = hashmap_get(m->machine_units, unit);
594                 if (mm)
595                         machine_add_to_gc_queue(mm);
596
597         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "Reloading")) {
598                 dbus_bool_t b;
599
600                 if (!dbus_message_get_args(message, &error,
601                                            DBUS_TYPE_BOOLEAN, &b,
602                                            DBUS_TYPE_INVALID)) {
603                         log_error("Failed to parse Reloading message: %s", bus_error_message(&error));
604                         goto finish;
605                 }
606
607                 /* systemd finished reloading, let's recheck all our machines */
608                 if (!b) {
609                         Machine *mm;
610                         Iterator i;
611
612                         log_debug("System manager has been reloaded, rechecking machines...");
613
614                         HASHMAP_FOREACH(mm, m->machines, i)
615                                 machine_add_to_gc_queue(mm);
616                 }
617         }
618
619 finish:
620         dbus_error_free(&error);
621
622         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
623 }
624
625 static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src);
626
627 static int copy_one_field(DBusMessageIter *dest, DBusMessageIter *src) {
628         int type, r;
629
630         type = dbus_message_iter_get_arg_type(src);
631
632         switch (type) {
633
634         case DBUS_TYPE_STRUCT: {
635                 DBusMessageIter dest_sub, src_sub;
636
637                 dbus_message_iter_recurse(src, &src_sub);
638
639                 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_STRUCT, NULL, &dest_sub))
640                         return log_oom();
641
642                 r = copy_many_fields(&dest_sub, &src_sub);
643                 if (r < 0)
644                         return r;
645
646                 if (!dbus_message_iter_close_container(dest, &dest_sub))
647                         return log_oom();
648
649                 return 0;
650         }
651
652         case DBUS_TYPE_ARRAY: {
653                 DBusMessageIter dest_sub, src_sub;
654
655                 dbus_message_iter_recurse(src, &src_sub);
656
657                 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_ARRAY, dbus_message_iter_get_signature(&src_sub), &dest_sub))
658                         return log_oom();
659
660                 r = copy_many_fields(&dest_sub, &src_sub);
661                 if (r < 0)
662                         return r;
663
664                 if (!dbus_message_iter_close_container(dest, &dest_sub))
665                         return log_oom();
666
667                 return 0;
668         }
669
670         case DBUS_TYPE_VARIANT: {
671                 DBusMessageIter dest_sub, src_sub;
672
673                 dbus_message_iter_recurse(src, &src_sub);
674
675                 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_VARIANT, dbus_message_iter_get_signature(&src_sub), &dest_sub))
676                         return log_oom();
677
678                 r = copy_one_field(&dest_sub, &src_sub);
679                 if (r < 0)
680                         return r;
681
682                 if (!dbus_message_iter_close_container(dest, &dest_sub))
683                         return log_oom();
684
685                 return 0;
686         }
687
688         case DBUS_TYPE_STRING:
689         case DBUS_TYPE_OBJECT_PATH:
690         case DBUS_TYPE_BYTE:
691         case DBUS_TYPE_BOOLEAN:
692         case DBUS_TYPE_UINT16:
693         case DBUS_TYPE_INT16:
694         case DBUS_TYPE_UINT32:
695         case DBUS_TYPE_INT32:
696         case DBUS_TYPE_UINT64:
697         case DBUS_TYPE_INT64:
698         case DBUS_TYPE_DOUBLE:
699         case DBUS_TYPE_SIGNATURE: {
700                 const void *p;
701
702                 dbus_message_iter_get_basic(src, &p);
703                 dbus_message_iter_append_basic(dest, type, &p);
704                 return 0;
705         }
706
707         default:
708                 return -EINVAL;
709         }
710 }
711
712 static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src) {
713         int r;
714
715         assert(dest);
716         assert(src);
717
718         while (dbus_message_iter_get_arg_type(src) != DBUS_TYPE_INVALID) {
719
720                 r = copy_one_field(dest, src);
721                 if (r < 0)
722                         return r;
723
724                 dbus_message_iter_next(src);
725         }
726
727         return 0;
728 }
729
730 int manager_start_scope(
731                 Manager *manager,
732                 const char *scope,
733                 pid_t pid,
734                 const char *slice,
735                 const char *description,
736                 DBusMessageIter *more_properties,
737                 DBusError *error,
738                 char **job) {
739
740         _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
741         DBusMessageIter iter, sub, sub2, sub3, sub4;
742         const char *timeout_stop_property = "TimeoutStopUSec";
743         const char *pids_property = "PIDs";
744         uint64_t timeout = 500 * USEC_PER_MSEC;
745         const char *fail = "fail";
746         uint32_t u;
747         int r;
748
749         assert(manager);
750         assert(scope);
751         assert(pid > 1);
752
753         if (!slice)
754                 slice = "";
755
756         m = dbus_message_new_method_call(
757                         "org.freedesktop.systemd1",
758                         "/org/freedesktop/systemd1",
759                         "org.freedesktop.systemd1.Manager",
760                         "StartTransientUnit");
761         if (!m)
762                 return log_oom();
763
764         dbus_message_iter_init_append(m, &iter);
765
766         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
767             !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
768             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
769                 return log_oom();
770
771         if (!isempty(slice)) {
772                 const char *slice_property = "Slice";
773
774                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
775                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
776                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
777                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
778                     !dbus_message_iter_close_container(&sub2, &sub3) ||
779                     !dbus_message_iter_close_container(&sub, &sub2))
780                         return log_oom();
781         }
782
783         if (!isempty(description)) {
784                 const char *description_property = "Description";
785
786                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
787                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
788                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
789                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
790                     !dbus_message_iter_close_container(&sub2, &sub3) ||
791                     !dbus_message_iter_close_container(&sub, &sub2))
792                         return log_oom();
793         }
794
795         /* cgroup empty notification is not available in containers
796          * currently. To make this less problematic, let's shorten the
797          * stop timeout for sessions, so that we don't wait
798          * forever. */
799
800         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
801             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
802             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
803             !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
804             !dbus_message_iter_close_container(&sub2, &sub3) ||
805             !dbus_message_iter_close_container(&sub, &sub2))
806                 return log_oom();
807
808         u = pid;
809         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
810             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
811             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
812             !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
813             !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
814             !dbus_message_iter_close_container(&sub3, &sub4) ||
815             !dbus_message_iter_close_container(&sub2, &sub3) ||
816             !dbus_message_iter_close_container(&sub, &sub2))
817                 return log_oom();
818
819         if (more_properties) {
820                 r = copy_many_fields(&sub, more_properties);
821                 if (r < 0)
822                         return r;
823         }
824
825         if (!dbus_message_iter_close_container(&iter, &sub))
826                 return log_oom();
827
828         reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
829         if (!reply)
830                 return -EIO;
831
832         if (job) {
833                 const char *j;
834                 char *copy;
835
836                 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
837                         return -EIO;
838
839                 copy = strdup(j);
840                 if (!copy)
841                         return -ENOMEM;
842
843                 *job = copy;
844         }
845
846         return 0;
847 }
848
849 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
850         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
851         const char *fail = "fail";
852         int r;
853
854         assert(manager);
855         assert(unit);
856
857         r = bus_method_call_with_reply(
858                         manager->bus,
859                         "org.freedesktop.systemd1",
860                         "/org/freedesktop/systemd1",
861                         "org.freedesktop.systemd1.Manager",
862                         "StopUnit",
863                         &reply,
864                         error,
865                         DBUS_TYPE_STRING, &unit,
866                         DBUS_TYPE_STRING, &fail,
867                         DBUS_TYPE_INVALID);
868         if (r < 0) {
869                 if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
870                     dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
871
872                         if (job)
873                                 *job = NULL;
874
875                         dbus_error_free(error);
876                         return 0;
877                 }
878
879                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
880                 return r;
881         }
882
883         if (job) {
884                 const char *j;
885                 char *copy;
886
887                 if (!dbus_message_get_args(reply, error,
888                                            DBUS_TYPE_OBJECT_PATH, &j,
889                                            DBUS_TYPE_INVALID)) {
890                         log_error("Failed to parse reply.");
891                         return -EIO;
892                 }
893
894                 copy = strdup(j);
895                 if (!copy)
896                         return -ENOMEM;
897
898                 *job = copy;
899         }
900
901         return 1;
902 }
903
904 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
905         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
906         const char *w;
907         int r;
908
909         assert(manager);
910         assert(unit);
911
912         w = who == KILL_LEADER ? "process" : "cgroup";
913         assert_cc(sizeof(signo) == sizeof(int32_t));
914
915         r = bus_method_call_with_reply(
916                         manager->bus,
917                         "org.freedesktop.systemd1",
918                         "/org/freedesktop/systemd1",
919                         "org.freedesktop.systemd1.Manager",
920                         "KillUnit",
921                         &reply,
922                         error,
923                         DBUS_TYPE_STRING, &unit,
924                         DBUS_TYPE_STRING, &w,
925                         DBUS_TYPE_INT32, &signo,
926                         DBUS_TYPE_INVALID);
927         if (r < 0) {
928                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
929                 return r;
930         }
931
932         return 0;
933 }
934
935 int manager_unit_is_active(Manager *manager, const char *unit) {
936
937         const char *interface = "org.freedesktop.systemd1.Unit";
938         const char *property = "ActiveState";
939         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
940         _cleanup_free_ char *path = NULL;
941         DBusMessageIter iter, sub;
942         const char *state;
943         DBusError error;
944         int r;
945
946         assert(manager);
947         assert(unit);
948
949         dbus_error_init(&error);
950
951         path = unit_dbus_path_from_name(unit);
952         if (!path)
953                 return -ENOMEM;
954
955         r = bus_method_call_with_reply(
956                         manager->bus,
957                         "org.freedesktop.systemd1",
958                         path,
959                         "org.freedesktop.DBus.Properties",
960                         "Get",
961                         &reply,
962                         &error,
963                         DBUS_TYPE_STRING, &interface,
964                         DBUS_TYPE_STRING, &property,
965                         DBUS_TYPE_INVALID);
966         if (r < 0) {
967                 if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY) ||
968                     dbus_error_has_name(&error, DBUS_ERROR_DISCONNECTED)) {
969                         dbus_error_free(&error);
970                         return true;
971                 }
972
973                 if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
974                     dbus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) {
975                         dbus_error_free(&error);
976                         return false;
977                 }
978
979                 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
980                 dbus_error_free(&error);
981                 return r;
982         }
983
984         if (!dbus_message_iter_init(reply, &iter) ||
985             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
986                 log_error("Failed to parse reply.");
987                 return -EINVAL;
988         }
989
990         dbus_message_iter_recurse(&iter, &sub);
991         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
992                 log_error("Failed to parse reply.");
993                 return -EINVAL;
994         }
995
996         dbus_message_iter_get_basic(&sub, &state);
997
998         return !streq(state, "inactive") && !streq(state, "failed");
999 }