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