chiark / gitweb /
sd-bus: support connecting to remote hosts, directly into containers
[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 #include <sys/capability.h>
27
28 #include "sd-id128.h"
29 #include "sd-messages.h"
30 #include "strv.h"
31 #include "mkdir.h"
32 #include "path-util.h"
33 #include "special.h"
34 #include "fileio-label.h"
35 #include "label.h"
36 #include "utf8.h"
37 #include "unit-name.h"
38 #include "bus-util.h"
39 #include "bus-errors.h"
40 #include "time-util.h"
41 #include "cgroup-util.h"
42 #include "machined.h"
43
44 static int method_get_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
45         _cleanup_free_ char *p = NULL;
46         Manager *m = userdata;
47         Machine *machine;
48         const char *name;
49         int r;
50
51         assert(bus);
52         assert(message);
53         assert(m);
54
55         r = sd_bus_message_read(message, "s", &name);
56         if (r < 0)
57                 return r;
58
59         machine = hashmap_get(m->machines, name);
60         if (!machine)
61                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
62
63         p = machine_bus_path(machine);
64         if (!p)
65                 return -ENOMEM;
66
67         return sd_bus_reply_method_return(message, "o", p);
68 }
69
70 static int method_get_machine_by_pid(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
71         _cleanup_free_ char *p = NULL;
72         Manager *m = userdata;
73         Machine *machine = NULL;
74         pid_t pid;
75         int r;
76
77         assert(bus);
78         assert(message);
79         assert(m);
80
81         assert_cc(sizeof(pid_t) == sizeof(uint32_t));
82
83         r = sd_bus_message_read(message, "u", &pid);
84         if (r < 0)
85                 return r;
86
87         if (pid == 0) {
88                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
89
90                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
91                 if (r < 0)
92                         return r;
93
94                 r = sd_bus_creds_get_pid(creds, &pid);
95                 if (r < 0)
96                         return r;
97         }
98
99         r = manager_get_machine_by_pid(m, pid, &machine);
100         if (r < 0)
101                 return r;
102         if (!machine)
103                 return sd_bus_error_setf(error, BUS_ERROR_NO_MACHINE_FOR_PID, "PID "PID_FMT" does not belong to any known machine", pid);
104
105         p = machine_bus_path(machine);
106         if (!p)
107                 return -ENOMEM;
108
109         return sd_bus_reply_method_return(message, "o", p);
110 }
111
112 static int method_list_machines(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
113         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
114         Manager *m = userdata;
115         Machine *machine;
116         Iterator i;
117         int r;
118
119         assert(bus);
120         assert(message);
121         assert(m);
122
123         r = sd_bus_message_new_method_return(message, &reply);
124         if (r < 0)
125                 return sd_bus_error_set_errno(error, r);
126
127         r = sd_bus_message_open_container(reply, 'a', "(ssso)");
128         if (r < 0)
129                 return sd_bus_error_set_errno(error, r);
130
131         HASHMAP_FOREACH(machine, m->machines, i) {
132                 _cleanup_free_ char *p = NULL;
133
134                 p = machine_bus_path(machine);
135                 if (!p)
136                         return -ENOMEM;
137
138                 r = sd_bus_message_append(reply, "(ssso)",
139                                           machine->name,
140                                           strempty(machine_class_to_string(machine->class)),
141                                           machine->service,
142                                           p);
143                 if (r < 0)
144                         return sd_bus_error_set_errno(error, r);
145         }
146
147         r = sd_bus_message_close_container(reply);
148         if (r < 0)
149                 return sd_bus_error_set_errno(error, r);
150
151         return sd_bus_send(bus, reply, NULL);
152 }
153
154 static int method_create_or_register_machine(Manager *manager, sd_bus_message *message, Machine **_m, sd_bus_error *error) {
155         const char *name, *service, *class, *root_directory;
156         MachineClass c;
157         uint32_t leader;
158         sd_id128_t id;
159         const void *v;
160         Machine *m;
161         size_t n;
162         int r;
163
164         assert(manager);
165         assert(message);
166         assert(_m);
167
168         r = sd_bus_message_read(message, "s", &name);
169         if (r < 0)
170                 return r;
171         if (!machine_name_is_valid(name))
172                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name");
173
174         r = sd_bus_message_read_array(message, 'y', &v, &n);
175         if (r < 0)
176                 return r;
177         if (n == 0)
178                 id = SD_ID128_NULL;
179         else if (n == 16)
180                 memcpy(&id, v, n);
181         else
182                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine ID parameter");
183
184         r = sd_bus_message_read(message, "ssus", &service, &class, &leader, &root_directory);
185         if (r < 0)
186                 return r;
187
188         if (isempty(class))
189                 c = _MACHINE_CLASS_INVALID;
190         else {
191                 c = machine_class_from_string(class);
192                 if (c < 0)
193                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine class parameter");
194         }
195
196         if (leader == 1)
197                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
198
199         if (!isempty(root_directory) && !path_is_absolute(root_directory))
200                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path");
201
202         if (leader == 0) {
203                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
204
205                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
206                 if (r < 0)
207                         return r;
208
209                 assert_cc(sizeof(uint32_t) == sizeof(pid_t));
210
211                 r = sd_bus_creds_get_pid(creds, (pid_t*) &leader);
212                 if (r < 0)
213                         return r;
214         }
215
216         if (hashmap_get(manager->machines, name))
217                 return sd_bus_error_setf(error, BUS_ERROR_MACHINE_EXISTS, "Machine '%s' already exists", name);
218
219         r = manager_add_machine(manager, name, &m);
220         if (r < 0)
221                 return r;
222
223         m->leader = leader;
224         m->class = c;
225         m->id = id;
226
227         if (!isempty(service)) {
228                 m->service = strdup(service);
229                 if (!m->service) {
230                         r = -ENOMEM;
231                         goto fail;
232                 }
233         }
234
235         if (!isempty(root_directory)) {
236                 m->root_directory = strdup(root_directory);
237                 if (!m->root_directory) {
238                         r = -ENOMEM;
239                         goto fail;
240                 }
241         }
242
243         *_m = m;
244
245         return 1;
246
247 fail:
248         machine_add_to_gc_queue(m);
249         return r;
250 }
251
252 static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
253         Manager *manager = userdata;
254         Machine *m = NULL;
255         int r;
256
257         r = method_create_or_register_machine(manager, message, &m, error);
258         if (r < 0)
259                 return r;
260
261         r = sd_bus_message_enter_container(message, 'a', "(sv)");
262         if (r < 0)
263                 goto fail;
264
265         r = machine_start(m, message, error);
266         if (r < 0)
267                 goto fail;
268
269         m->create_message = sd_bus_message_ref(message);
270         return 1;
271
272 fail:
273         machine_add_to_gc_queue(m);
274         return r;
275 }
276
277 static int method_register_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
278         Manager *manager = userdata;
279         _cleanup_free_ char *p = NULL;
280         Machine *m = NULL;
281         int r;
282
283         r = method_create_or_register_machine(manager, message, &m, error);
284         if (r < 0)
285                 return r;
286
287         r = cg_pid_get_unit(m->leader, &m->unit);
288         if (r < 0) {
289                 r = sd_bus_error_set_errnof(error, r, "Failed to determine unit of process "PID_FMT" : %s", m->leader, strerror(-r));
290                 goto fail;
291         }
292
293         r = machine_start(m, NULL, error);
294         if (r < 0)
295                 goto fail;
296
297         p = machine_bus_path(m);
298         if (!p) {
299                 r = -ENOMEM;
300                 goto fail;
301         }
302
303         return sd_bus_reply_method_return(message, "o", p);
304
305 fail:
306         machine_add_to_gc_queue(m);
307         return r;
308 }
309
310 static int method_terminate_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
311         Manager *m = userdata;
312         Machine *machine;
313         const char *name;
314         int r;
315
316         assert(bus);
317         assert(message);
318         assert(m);
319
320         r = sd_bus_message_read(message, "s", &name);
321         if (r < 0)
322                 return sd_bus_error_set_errno(error, r);
323
324         machine = hashmap_get(m->machines, name);
325         if (!machine)
326                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
327
328         return bus_machine_method_terminate(bus, message, machine, error);
329 }
330
331 static int method_kill_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
332         Manager *m = userdata;
333         Machine *machine;
334         const char *name;
335         int r;
336
337         assert(bus);
338         assert(message);
339         assert(m);
340
341         r = sd_bus_message_read(message, "s", &name);
342         if (r < 0)
343                 return sd_bus_error_set_errno(error, r);
344
345         machine = hashmap_get(m->machines, name);
346         if (!machine)
347                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
348
349         return bus_machine_method_kill(bus, message, machine, error);
350 }
351
352 static int method_get_machine_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
353         Manager *m = userdata;
354         Machine *machine;
355         const char *name;
356         int r;
357
358         assert(bus);
359         assert(message);
360         assert(m);
361
362         r = sd_bus_message_read(message, "s", &name);
363         if (r < 0)
364                 return sd_bus_error_set_errno(error, r);
365
366         machine = hashmap_get(m->machines, name);
367         if (!machine)
368                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
369
370         return bus_machine_method_get_addresses(bus, message, machine, error);
371 }
372
373 const sd_bus_vtable manager_vtable[] = {
374         SD_BUS_VTABLE_START(0),
375         SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED),
376         SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
377         SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED),
378         SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0),
379         SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0),
380         SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
381         SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
382         SD_BUS_METHOD("GetMachineAddresses", "s", "a(yay)", method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
383         SD_BUS_SIGNAL("MachineNew", "so", 0),
384         SD_BUS_SIGNAL("MachineRemoved", "so", 0),
385         SD_BUS_VTABLE_END
386 };
387
388 int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
389         const char *path, *result, *unit;
390         Manager *m = userdata;
391         Machine *machine;
392         uint32_t id;
393         int r;
394
395         assert(bus);
396         assert(message);
397         assert(m);
398
399         r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result);
400         if (r < 0) {
401                 bus_log_parse_error(r);
402                 return r;
403         }
404
405         machine = hashmap_get(m->machine_units, unit);
406         if (!machine)
407                 return 0;
408
409         if (streq_ptr(path, machine->scope_job)) {
410                 free(machine->scope_job);
411                 machine->scope_job = NULL;
412
413                 if (machine->started) {
414                         if (streq(result, "done"))
415                                 machine_send_create_reply(machine, NULL);
416                         else {
417                                 _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
418
419                                 sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
420
421                                 machine_send_create_reply(machine, &e);
422                         }
423                 } else
424                         machine_save(machine);
425         }
426
427         machine_add_to_gc_queue(machine);
428         return 0;
429 }
430
431 int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
432         _cleanup_free_ char *unit = NULL;
433         Manager *m = userdata;
434         Machine *machine;
435         const char *path;
436         int r;
437
438         assert(bus);
439         assert(message);
440         assert(m);
441
442         path = sd_bus_message_get_path(message);
443         if (!path)
444                 return 0;
445
446         r = unit_name_from_dbus_path(path, &unit);
447         if (r < 0)
448                 return r;
449
450         machine = hashmap_get(m->machine_units, unit);
451         if (machine)
452                 machine_add_to_gc_queue(machine);
453
454         return 0;
455 }
456
457 int match_unit_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
458         const char *path, *unit;
459         Manager *m = userdata;
460         Machine *machine;
461         int r;
462
463         assert(bus);
464         assert(message);
465         assert(m);
466
467         r = sd_bus_message_read(message, "so", &unit, &path);
468         if (r < 0) {
469                 bus_log_parse_error(r);
470                 return r;
471         }
472
473         machine = hashmap_get(m->machine_units, unit);
474         if (machine)
475                 machine_add_to_gc_queue(machine);
476
477         return 0;
478 }
479
480 int match_reloading(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
481         Manager *m = userdata;
482         Machine *machine;
483         Iterator i;
484         int b, r;
485
486         assert(bus);
487
488         r = sd_bus_message_read(message, "b", &b);
489         if (r < 0) {
490                 bus_log_parse_error(r);
491                 return r;
492         }
493         if (b)
494                 return 0;
495
496         /* systemd finished reloading, let's recheck all our machines */
497         log_debug("System manager has been reloaded, rechecking machines...");
498
499         HASHMAP_FOREACH(machine, m->machines, i)
500                 machine_add_to_gc_queue(machine);
501
502         return 0;
503 }
504
505 int manager_start_scope(
506                 Manager *manager,
507                 const char *scope,
508                 pid_t pid,
509                 const char *slice,
510                 const char *description,
511                 sd_bus_message *more_properties,
512                 sd_bus_error *error,
513                 char **job) {
514
515         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
516         int r;
517
518         assert(manager);
519         assert(scope);
520         assert(pid > 1);
521
522         r = sd_bus_message_new_method_call(
523                         manager->bus,
524                         &m,
525                         "org.freedesktop.systemd1",
526                         "/org/freedesktop/systemd1",
527                         "org.freedesktop.systemd1.Manager",
528                         "StartTransientUnit");
529         if (r < 0)
530                 return r;
531
532         r = sd_bus_message_append(m, "ss", strempty(scope), "fail");
533         if (r < 0)
534                 return r;
535
536         r = sd_bus_message_open_container(m, 'a', "(sv)");
537         if (r < 0)
538                 return r;
539
540         if (!isempty(slice)) {
541                 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
542                 if (r < 0)
543                         return r;
544         }
545
546         if (!isempty(description)) {
547                 r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
548                 if (r < 0)
549                         return r;
550         }
551
552         r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
553         if (r < 0)
554                 return r;
555
556         if (more_properties) {
557                 r = sd_bus_message_copy(m, more_properties, true);
558                 if (r < 0)
559                         return r;
560         }
561
562         r = sd_bus_message_close_container(m);
563         if (r < 0)
564                 return r;
565
566         r = sd_bus_message_append(m, "a(sa(sv))", 0);
567         if (r < 0)
568                 return r;
569
570         r = sd_bus_call(manager->bus, m, 0, error, &reply);
571         if (r < 0)
572                 return r;
573
574         if (job) {
575                 const char *j;
576                 char *copy;
577
578                 r = sd_bus_message_read(reply, "o", &j);
579                 if (r < 0)
580                         return r;
581
582                 copy = strdup(j);
583                 if (!copy)
584                         return -ENOMEM;
585
586                 *job = copy;
587         }
588
589         return 1;
590 }
591
592 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
593         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
594         int r;
595
596         assert(manager);
597         assert(unit);
598
599         r = sd_bus_call_method(
600                         manager->bus,
601                         "org.freedesktop.systemd1",
602                         "/org/freedesktop/systemd1",
603                         "org.freedesktop.systemd1.Manager",
604                         "StopUnit",
605                         error,
606                         &reply,
607                         "ss", unit, "fail");
608         if (r < 0) {
609                 if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
610                     sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
611
612                         if (job)
613                                 *job = NULL;
614
615                         sd_bus_error_free(error);
616                         return 0;
617                 }
618
619                 return r;
620         }
621
622         if (job) {
623                 const char *j;
624                 char *copy;
625
626                 r = sd_bus_message_read(reply, "o", &j);
627                 if (r < 0)
628                         return r;
629
630                 copy = strdup(j);
631                 if (!copy)
632                         return -ENOMEM;
633
634                 *job = copy;
635         }
636
637         return 1;
638 }
639
640 int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_error *error) {
641         assert(manager);
642         assert(unit);
643
644         return sd_bus_call_method(
645                         manager->bus,
646                         "org.freedesktop.systemd1",
647                         "/org/freedesktop/systemd1",
648                         "org.freedesktop.systemd1.Manager",
649                         "KillUnit",
650                         error,
651                         NULL,
652                         "ssi", unit, "all", signo);
653 }
654
655 int manager_unit_is_active(Manager *manager, const char *unit) {
656         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
657         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
658         _cleanup_free_ char *path = NULL;
659         const char *state;
660         int r;
661
662         assert(manager);
663         assert(unit);
664
665         path = unit_dbus_path_from_name(unit);
666         if (!path)
667                 return -ENOMEM;
668
669         r = sd_bus_get_property(
670                         manager->bus,
671                         "org.freedesktop.systemd1",
672                         path,
673                         "org.freedesktop.systemd1.Unit",
674                         "ActiveState",
675                         &error,
676                         &reply,
677                         "s");
678         if (r < 0) {
679                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
680                     sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
681                         return true;
682
683                 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
684                     sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED))
685                         return false;
686
687                 return r;
688         }
689
690         r = sd_bus_message_read(reply, "s", &state);
691         if (r < 0)
692                 return -EINVAL;
693
694         return !streq(state, "inactive") && !streq(state, "failed");
695 }
696
697 int manager_job_is_active(Manager *manager, const char *path) {
698         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
699         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
700         int r;
701
702         assert(manager);
703         assert(path);
704
705         r = sd_bus_get_property(
706                         manager->bus,
707                         "org.freedesktop.systemd1",
708                         path,
709                         "org.freedesktop.systemd1.Job",
710                         "State",
711                         &error,
712                         &reply,
713                         "s");
714         if (r < 0) {
715                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
716                     sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
717                         return true;
718
719                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
720                         return false;
721
722                 return r;
723         }
724
725         /* We don't actually care about the state really. The fact
726          * that we could read the job state is enough for us */
727
728         return true;
729 }
730
731 int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
732         _cleanup_free_ char *unit = NULL;
733         Machine *mm;
734         int r;
735
736         assert(m);
737         assert(pid >= 1);
738         assert(machine);
739
740         r = cg_pid_get_unit(pid, &unit);
741         if (r < 0)
742                 mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
743         else
744                 mm = hashmap_get(m->machine_units, unit);
745
746         if (!mm)
747                 return 0;
748
749         *machine = mm;
750         return 1;
751 }
752
753 int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
754         Machine *machine;
755
756         assert(m);
757         assert(name);
758
759         machine = hashmap_get(m->machines, name);
760         if (!machine) {
761                 machine = machine_new(m, name);
762                 if (!machine)
763                         return -ENOMEM;
764         }
765
766         if (_machine)
767                 *_machine = machine;
768
769         return 0;
770 }