chiark / gitweb /
logind: update the session state file before we send out the CreateSession() reply
[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                                 } else
555                                         machine_save(mm);
556                         }
557
558                         machine_add_to_gc_queue(mm);
559                 }
560
561         } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
562
563                 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
564                 _cleanup_free_ char *unit = NULL;
565                 const char *path;
566
567                 path = dbus_message_get_path(message);
568                 if (!path)
569                         goto finish;
570
571                 unit_name_from_dbus_path(path, &unit);
572                 if (unit) {
573                         Machine *mm;
574
575                         mm = hashmap_get(m->machine_units, unit);
576                         if (mm)
577                                 machine_add_to_gc_queue(mm);
578                 }
579
580         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
581                 const char *path, *unit;
582                 Machine *mm;
583
584                 if (!dbus_message_get_args(message, &error,
585                                            DBUS_TYPE_STRING, &unit,
586                                            DBUS_TYPE_OBJECT_PATH, &path,
587                                            DBUS_TYPE_INVALID)) {
588                         log_error("Failed to parse UnitRemoved message: %s", bus_error_message(&error));
589                         goto finish;
590                 }
591
592                 mm = hashmap_get(m->machine_units, unit);
593                 if (mm)
594                         machine_add_to_gc_queue(mm);
595
596         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "Reloading")) {
597                 dbus_bool_t b;
598
599                 if (!dbus_message_get_args(message, &error,
600                                            DBUS_TYPE_BOOLEAN, &b,
601                                            DBUS_TYPE_INVALID)) {
602                         log_error("Failed to parse Reloading message: %s", bus_error_message(&error));
603                         goto finish;
604                 }
605
606                 /* systemd finished reloading, let's recheck all our machines */
607                 if (!b) {
608                         Machine *mm;
609                         Iterator i;
610
611                         log_debug("System manager has been reloaded, rechecking machines...");
612
613                         HASHMAP_FOREACH(mm, m->machines, i)
614                                 machine_add_to_gc_queue(mm);
615                 }
616         }
617
618 finish:
619         dbus_error_free(&error);
620
621         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
622 }
623
624 static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src);
625
626 static int copy_one_field(DBusMessageIter *dest, DBusMessageIter *src) {
627         int type, r;
628
629         type = dbus_message_iter_get_arg_type(src);
630
631         switch (type) {
632
633         case DBUS_TYPE_STRUCT: {
634                 DBusMessageIter dest_sub, src_sub;
635
636                 dbus_message_iter_recurse(src, &src_sub);
637
638                 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_STRUCT, NULL, &dest_sub))
639                         return log_oom();
640
641                 r = copy_many_fields(&dest_sub, &src_sub);
642                 if (r < 0)
643                         return r;
644
645                 if (!dbus_message_iter_close_container(dest, &dest_sub))
646                         return log_oom();
647
648                 return 0;
649         }
650
651         case DBUS_TYPE_ARRAY: {
652                 DBusMessageIter dest_sub, src_sub;
653
654                 dbus_message_iter_recurse(src, &src_sub);
655
656                 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_ARRAY, dbus_message_iter_get_signature(&src_sub), &dest_sub))
657                         return log_oom();
658
659                 r = copy_many_fields(&dest_sub, &src_sub);
660                 if (r < 0)
661                         return r;
662
663                 if (!dbus_message_iter_close_container(dest, &dest_sub))
664                         return log_oom();
665
666                 return 0;
667         }
668
669         case DBUS_TYPE_VARIANT: {
670                 DBusMessageIter dest_sub, src_sub;
671
672                 dbus_message_iter_recurse(src, &src_sub);
673
674                 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_VARIANT, dbus_message_iter_get_signature(&src_sub), &dest_sub))
675                         return log_oom();
676
677                 r = copy_one_field(&dest_sub, &src_sub);
678                 if (r < 0)
679                         return r;
680
681                 if (!dbus_message_iter_close_container(dest, &dest_sub))
682                         return log_oom();
683
684                 return 0;
685         }
686
687         case DBUS_TYPE_STRING:
688         case DBUS_TYPE_OBJECT_PATH:
689         case DBUS_TYPE_BYTE:
690         case DBUS_TYPE_BOOLEAN:
691         case DBUS_TYPE_UINT16:
692         case DBUS_TYPE_INT16:
693         case DBUS_TYPE_UINT32:
694         case DBUS_TYPE_INT32:
695         case DBUS_TYPE_UINT64:
696         case DBUS_TYPE_INT64:
697         case DBUS_TYPE_DOUBLE:
698         case DBUS_TYPE_SIGNATURE: {
699                 const void *p;
700
701                 dbus_message_iter_get_basic(src, &p);
702                 dbus_message_iter_append_basic(dest, type, &p);
703                 return 0;
704         }
705
706         default:
707                 return -EINVAL;
708         }
709 }
710
711 static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src) {
712         int r;
713
714         assert(dest);
715         assert(src);
716
717         while (dbus_message_iter_get_arg_type(src) != DBUS_TYPE_INVALID) {
718
719                 r = copy_one_field(dest, src);
720                 if (r < 0)
721                         return r;
722
723                 dbus_message_iter_next(src);
724         }
725
726         return 0;
727 }
728
729 int manager_start_scope(
730                 Manager *manager,
731                 const char *scope,
732                 pid_t pid,
733                 const char *slice,
734                 const char *description,
735                 DBusMessageIter *more_properties,
736                 DBusError *error,
737                 char **job) {
738
739         _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
740         DBusMessageIter iter, sub, sub2, sub3, sub4;
741         const char *timeout_stop_property = "TimeoutStopUSec";
742         const char *pids_property = "PIDs";
743         uint64_t timeout = 500 * USEC_PER_MSEC;
744         const char *fail = "fail";
745         uint32_t u;
746         int r;
747
748         assert(manager);
749         assert(scope);
750         assert(pid > 1);
751
752         if (!slice)
753                 slice = "";
754
755         m = dbus_message_new_method_call(
756                         "org.freedesktop.systemd1",
757                         "/org/freedesktop/systemd1",
758                         "org.freedesktop.systemd1.Manager",
759                         "StartTransientUnit");
760         if (!m)
761                 return log_oom();
762
763         dbus_message_iter_init_append(m, &iter);
764
765         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
766             !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
767             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
768                 return log_oom();
769
770         if (!isempty(slice)) {
771                 const char *slice_property = "Slice";
772
773                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
774                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
775                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
776                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
777                     !dbus_message_iter_close_container(&sub2, &sub3) ||
778                     !dbus_message_iter_close_container(&sub, &sub2))
779                         return log_oom();
780         }
781
782         if (!isempty(description)) {
783                 const char *description_property = "Description";
784
785                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
786                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
787                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
788                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
789                     !dbus_message_iter_close_container(&sub2, &sub3) ||
790                     !dbus_message_iter_close_container(&sub, &sub2))
791                         return log_oom();
792         }
793
794         /* cgroup empty notification is not available in containers
795          * currently. To make this less problematic, let's shorten the
796          * stop timeout for sessions, so that we don't wait
797          * forever. */
798
799         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
800             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
801             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
802             !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
803             !dbus_message_iter_close_container(&sub2, &sub3) ||
804             !dbus_message_iter_close_container(&sub, &sub2))
805                 return log_oom();
806
807         u = pid;
808         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
809             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
810             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
811             !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
812             !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
813             !dbus_message_iter_close_container(&sub3, &sub4) ||
814             !dbus_message_iter_close_container(&sub2, &sub3) ||
815             !dbus_message_iter_close_container(&sub, &sub2))
816                 return log_oom();
817
818         if (more_properties) {
819                 r = copy_many_fields(&sub, more_properties);
820                 if (r < 0)
821                         return r;
822         }
823
824         if (!dbus_message_iter_close_container(&iter, &sub))
825                 return log_oom();
826
827         reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
828         if (!reply)
829                 return -EIO;
830
831         if (job) {
832                 const char *j;
833                 char *copy;
834
835                 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
836                         return -EIO;
837
838                 copy = strdup(j);
839                 if (!copy)
840                         return -ENOMEM;
841
842                 *job = copy;
843         }
844
845         return 0;
846 }
847
848 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
849         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
850         const char *fail = "fail";
851         int r;
852
853         assert(manager);
854         assert(unit);
855
856         r = bus_method_call_with_reply(
857                         manager->bus,
858                         "org.freedesktop.systemd1",
859                         "/org/freedesktop/systemd1",
860                         "org.freedesktop.systemd1.Manager",
861                         "StopUnit",
862                         &reply,
863                         error,
864                         DBUS_TYPE_STRING, &unit,
865                         DBUS_TYPE_STRING, &fail,
866                         DBUS_TYPE_INVALID);
867         if (r < 0) {
868                 if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
869                     dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
870
871                         if (job)
872                                 *job = NULL;
873
874                         dbus_error_free(error);
875                         return 0;
876                 }
877
878                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
879                 return r;
880         }
881
882         if (job) {
883                 const char *j;
884                 char *copy;
885
886                 if (!dbus_message_get_args(reply, error,
887                                            DBUS_TYPE_OBJECT_PATH, &j,
888                                            DBUS_TYPE_INVALID)) {
889                         log_error("Failed to parse reply.");
890                         return -EIO;
891                 }
892
893                 copy = strdup(j);
894                 if (!copy)
895                         return -ENOMEM;
896
897                 *job = copy;
898         }
899
900         return 1;
901 }
902
903 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
904         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
905         const char *w;
906         int r;
907
908         assert(manager);
909         assert(unit);
910
911         w = who == KILL_LEADER ? "process" : "cgroup";
912         assert_cc(sizeof(signo) == sizeof(int32_t));
913
914         r = bus_method_call_with_reply(
915                         manager->bus,
916                         "org.freedesktop.systemd1",
917                         "/org/freedesktop/systemd1",
918                         "org.freedesktop.systemd1.Manager",
919                         "KillUnit",
920                         &reply,
921                         error,
922                         DBUS_TYPE_STRING, &unit,
923                         DBUS_TYPE_STRING, &w,
924                         DBUS_TYPE_INT32, &signo,
925                         DBUS_TYPE_INVALID);
926         if (r < 0) {
927                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
928                 return r;
929         }
930
931         return 0;
932 }
933
934 int manager_unit_is_active(Manager *manager, const char *unit) {
935
936         const char *interface = "org.freedesktop.systemd1.Unit";
937         const char *property = "ActiveState";
938         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
939         _cleanup_free_ char *path = NULL;
940         DBusMessageIter iter, sub;
941         const char *state;
942         DBusError error;
943         int r;
944
945         assert(manager);
946         assert(unit);
947
948         dbus_error_init(&error);
949
950         path = unit_dbus_path_from_name(unit);
951         if (!path)
952                 return -ENOMEM;
953
954         r = bus_method_call_with_reply(
955                         manager->bus,
956                         "org.freedesktop.systemd1",
957                         path,
958                         "org.freedesktop.DBus.Properties",
959                         "Get",
960                         &reply,
961                         &error,
962                         DBUS_TYPE_STRING, &interface,
963                         DBUS_TYPE_STRING, &property,
964                         DBUS_TYPE_INVALID);
965         if (r < 0) {
966                 if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY) ||
967                     dbus_error_has_name(&error, DBUS_ERROR_DISCONNECTED)) {
968                         dbus_error_free(&error);
969                         return true;
970                 }
971
972                 if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
973                     dbus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) {
974                         dbus_error_free(&error);
975                         return false;
976                 }
977
978                 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
979                 dbus_error_free(&error);
980                 return r;
981         }
982
983         if (!dbus_message_iter_init(reply, &iter) ||
984             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
985                 log_error("Failed to parse reply.");
986                 return -EINVAL;
987         }
988
989         dbus_message_iter_recurse(&iter, &sub);
990         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
991                 log_error("Failed to parse reply.");
992                 return -EINVAL;
993         }
994
995         dbus_message_iter_get_basic(&sub, &state);
996
997         return !streq(state, "inactive") && !streq(state, "failed");
998 }