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