chiark / gitweb /
7c1802ce2a74e749c14cafa10f71a9664f4a73fe
[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         m->registered = true;
227
228         if (!isempty(service)) {
229                 m->service = strdup(service);
230                 if (!m->service) {
231                         r = -ENOMEM;
232                         goto fail;
233                 }
234         }
235
236         if (!isempty(root_directory)) {
237                 m->root_directory = strdup(root_directory);
238                 if (!m->root_directory) {
239                         r = -ENOMEM;
240                         goto fail;
241                 }
242         }
243
244         *_m = m;
245
246         return 1;
247
248 fail:
249         machine_add_to_gc_queue(m);
250         return r;
251 }
252
253 static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
254         Manager *manager = userdata;
255         Machine *m = NULL;
256         int r;
257
258         r = method_create_or_register_machine(manager, message, &m, error);
259         if (r < 0)
260                 return r;
261
262         r = sd_bus_message_enter_container(message, 'a', "(sv)");
263         if (r < 0)
264                 goto fail;
265
266         r = machine_start(m, message, error);
267         if (r < 0)
268                 goto fail;
269
270         m->create_message = sd_bus_message_ref(message);
271         return 1;
272
273 fail:
274         machine_add_to_gc_queue(m);
275         return r;
276 }
277
278 static int method_register_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
279         Manager *manager = userdata;
280         _cleanup_free_ char *p = NULL;
281         Machine *m = NULL;
282         int r;
283
284         r = method_create_or_register_machine(manager, message, &m, error);
285         if (r < 0)
286                 return r;
287
288         r = cg_pid_get_unit(m->leader, &m->unit);
289         if (r < 0) {
290                 r = sd_bus_error_set_errnof(error, r, "Failed to determine unit of process "PID_FMT" : %s", m->leader, strerror(-r));
291                 goto fail;
292         }
293
294         r = machine_start(m, NULL, error);
295         if (r < 0)
296                 goto fail;
297
298         p = machine_bus_path(m);
299         if (!p) {
300                 r = -ENOMEM;
301                 goto fail;
302         }
303
304         return sd_bus_reply_method_return(message, "o", p);
305
306 fail:
307         machine_add_to_gc_queue(m);
308         return r;
309 }
310
311 static int method_terminate_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
312         Manager *m = userdata;
313         Machine *machine;
314         const char *name;
315         int r;
316
317         assert(bus);
318         assert(message);
319         assert(m);
320
321         r = sd_bus_message_read(message, "s", &name);
322         if (r < 0)
323                 return sd_bus_error_set_errno(error, r);
324
325         machine = hashmap_get(m->machines, name);
326         if (!machine)
327                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
328
329         return bus_machine_method_terminate(bus, message, machine, error);
330 }
331
332 static int method_kill_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
333         Manager *m = userdata;
334         Machine *machine;
335         const char *name;
336         int r;
337
338         assert(bus);
339         assert(message);
340         assert(m);
341
342         r = sd_bus_message_read(message, "s", &name);
343         if (r < 0)
344                 return sd_bus_error_set_errno(error, r);
345
346         machine = hashmap_get(m->machines, name);
347         if (!machine)
348                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
349
350         return bus_machine_method_kill(bus, message, machine, error);
351 }
352
353 static int method_get_machine_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
354         Manager *m = userdata;
355         Machine *machine;
356         const char *name;
357         int r;
358
359         assert(bus);
360         assert(message);
361         assert(m);
362
363         r = sd_bus_message_read(message, "s", &name);
364         if (r < 0)
365                 return sd_bus_error_set_errno(error, r);
366
367         machine = hashmap_get(m->machines, name);
368         if (!machine)
369                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
370
371         return bus_machine_method_get_addresses(bus, message, machine, error);
372 }
373
374 const sd_bus_vtable manager_vtable[] = {
375         SD_BUS_VTABLE_START(0),
376         SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED),
377         SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
378         SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED),
379         SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0),
380         SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0),
381         SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
382         SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
383         SD_BUS_METHOD("GetMachineAddresses", "s", "a(yay)", method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
384         SD_BUS_SIGNAL("MachineNew", "so", 0),
385         SD_BUS_SIGNAL("MachineRemoved", "so", 0),
386         SD_BUS_VTABLE_END
387 };
388
389 int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
390         const char *path, *result, *unit;
391         Manager *m = userdata;
392         Machine *machine;
393         uint32_t id;
394         int r;
395
396         assert(bus);
397         assert(message);
398         assert(m);
399
400         r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result);
401         if (r < 0) {
402                 bus_log_parse_error(r);
403                 return r;
404         }
405
406         machine = hashmap_get(m->machine_units, unit);
407         if (!machine)
408                 return 0;
409
410         if (streq_ptr(path, machine->scope_job)) {
411                 free(machine->scope_job);
412                 machine->scope_job = NULL;
413
414                 if (machine->started) {
415                         if (streq(result, "done"))
416                                 machine_send_create_reply(machine, NULL);
417                         else {
418                                 _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
419
420                                 sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
421
422                                 machine_send_create_reply(machine, &e);
423                         }
424                 } else
425                         machine_save(machine);
426         }
427
428         machine_add_to_gc_queue(machine);
429         return 0;
430 }
431
432 int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
433         _cleanup_free_ char *unit = NULL;
434         Manager *m = userdata;
435         Machine *machine;
436         const char *path;
437         int r;
438
439         assert(bus);
440         assert(message);
441         assert(m);
442
443         path = sd_bus_message_get_path(message);
444         if (!path)
445                 return 0;
446
447         r = unit_name_from_dbus_path(path, &unit);
448         if (r < 0)
449                 return r;
450
451         machine = hashmap_get(m->machine_units, unit);
452         if (machine)
453                 machine_add_to_gc_queue(machine);
454
455         return 0;
456 }
457
458 int match_unit_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
459         const char *path, *unit;
460         Manager *m = userdata;
461         Machine *machine;
462         int r;
463
464         assert(bus);
465         assert(message);
466         assert(m);
467
468         r = sd_bus_message_read(message, "so", &unit, &path);
469         if (r < 0) {
470                 bus_log_parse_error(r);
471                 return r;
472         }
473
474         machine = hashmap_get(m->machine_units, unit);
475         if (machine)
476                 machine_add_to_gc_queue(machine);
477
478         return 0;
479 }
480
481 int match_reloading(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
482         Manager *m = userdata;
483         Machine *machine;
484         Iterator i;
485         int b, r;
486
487         assert(bus);
488
489         r = sd_bus_message_read(message, "b", &b);
490         if (r < 0) {
491                 bus_log_parse_error(r);
492                 return r;
493         }
494         if (b)
495                 return 0;
496
497         /* systemd finished reloading, let's recheck all our machines */
498         log_debug("System manager has been reloaded, rechecking machines...");
499
500         HASHMAP_FOREACH(machine, m->machines, i)
501                 machine_add_to_gc_queue(machine);
502
503         return 0;
504 }
505
506 int manager_start_scope(
507                 Manager *manager,
508                 const char *scope,
509                 pid_t pid,
510                 const char *slice,
511                 const char *description,
512                 sd_bus_message *more_properties,
513                 sd_bus_error *error,
514                 char **job) {
515
516         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
517         int r;
518
519         assert(manager);
520         assert(scope);
521         assert(pid > 1);
522
523         r = sd_bus_message_new_method_call(
524                         manager->bus,
525                         &m,
526                         "org.freedesktop.systemd1",
527                         "/org/freedesktop/systemd1",
528                         "org.freedesktop.systemd1.Manager",
529                         "StartTransientUnit");
530         if (r < 0)
531                 return r;
532
533         r = sd_bus_message_append(m, "ss", strempty(scope), "fail");
534         if (r < 0)
535                 return r;
536
537         r = sd_bus_message_open_container(m, 'a', "(sv)");
538         if (r < 0)
539                 return r;
540
541         if (!isempty(slice)) {
542                 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
543                 if (r < 0)
544                         return r;
545         }
546
547         if (!isempty(description)) {
548                 r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
549                 if (r < 0)
550                         return r;
551         }
552
553         r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
554         if (r < 0)
555                 return r;
556
557         if (more_properties) {
558                 r = sd_bus_message_copy(m, more_properties, true);
559                 if (r < 0)
560                         return r;
561         }
562
563         r = sd_bus_message_close_container(m);
564         if (r < 0)
565                 return r;
566
567         r = sd_bus_message_append(m, "a(sa(sv))", 0);
568         if (r < 0)
569                 return r;
570
571         r = sd_bus_call(manager->bus, m, 0, error, &reply);
572         if (r < 0)
573                 return r;
574
575         if (job) {
576                 const char *j;
577                 char *copy;
578
579                 r = sd_bus_message_read(reply, "o", &j);
580                 if (r < 0)
581                         return r;
582
583                 copy = strdup(j);
584                 if (!copy)
585                         return -ENOMEM;
586
587                 *job = copy;
588         }
589
590         return 1;
591 }
592
593 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
594         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
595         int r;
596
597         assert(manager);
598         assert(unit);
599
600         r = sd_bus_call_method(
601                         manager->bus,
602                         "org.freedesktop.systemd1",
603                         "/org/freedesktop/systemd1",
604                         "org.freedesktop.systemd1.Manager",
605                         "StopUnit",
606                         error,
607                         &reply,
608                         "ss", unit, "fail");
609         if (r < 0) {
610                 if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
611                     sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
612
613                         if (job)
614                                 *job = NULL;
615
616                         sd_bus_error_free(error);
617                         return 0;
618                 }
619
620                 return r;
621         }
622
623         if (job) {
624                 const char *j;
625                 char *copy;
626
627                 r = sd_bus_message_read(reply, "o", &j);
628                 if (r < 0)
629                         return r;
630
631                 copy = strdup(j);
632                 if (!copy)
633                         return -ENOMEM;
634
635                 *job = copy;
636         }
637
638         return 1;
639 }
640
641 int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_error *error) {
642         assert(manager);
643         assert(unit);
644
645         return sd_bus_call_method(
646                         manager->bus,
647                         "org.freedesktop.systemd1",
648                         "/org/freedesktop/systemd1",
649                         "org.freedesktop.systemd1.Manager",
650                         "KillUnit",
651                         error,
652                         NULL,
653                         "ssi", unit, "all", signo);
654 }
655
656 int manager_unit_is_active(Manager *manager, const char *unit) {
657         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
658         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
659         _cleanup_free_ char *path = NULL;
660         const char *state;
661         int r;
662
663         assert(manager);
664         assert(unit);
665
666         path = unit_dbus_path_from_name(unit);
667         if (!path)
668                 return -ENOMEM;
669
670         r = sd_bus_get_property(
671                         manager->bus,
672                         "org.freedesktop.systemd1",
673                         path,
674                         "org.freedesktop.systemd1.Unit",
675                         "ActiveState",
676                         &error,
677                         &reply,
678                         "s");
679         if (r < 0) {
680                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
681                     sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
682                         return true;
683
684                 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
685                     sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED))
686                         return false;
687
688                 return r;
689         }
690
691         r = sd_bus_message_read(reply, "s", &state);
692         if (r < 0)
693                 return -EINVAL;
694
695         return !streq(state, "inactive") && !streq(state, "failed");
696 }
697
698 int manager_job_is_active(Manager *manager, const char *path) {
699         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
700         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
701         int r;
702
703         assert(manager);
704         assert(path);
705
706         r = sd_bus_get_property(
707                         manager->bus,
708                         "org.freedesktop.systemd1",
709                         path,
710                         "org.freedesktop.systemd1.Job",
711                         "State",
712                         &error,
713                         &reply,
714                         "s");
715         if (r < 0) {
716                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
717                     sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
718                         return true;
719
720                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
721                         return false;
722
723                 return r;
724         }
725
726         /* We don't actually care about the state really. The fact
727          * that we could read the job state is enough for us */
728
729         return true;
730 }
731
732 int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
733         _cleanup_free_ char *unit = NULL;
734         Machine *mm;
735         int r;
736
737         assert(m);
738         assert(pid >= 1);
739         assert(machine);
740
741         r = cg_pid_get_unit(pid, &unit);
742         if (r < 0)
743                 mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
744         else
745                 mm = hashmap_get(m->machine_units, unit);
746
747         if (!mm)
748                 return 0;
749
750         *machine = mm;
751         return 1;
752 }
753
754 int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
755         Machine *machine;
756
757         assert(m);
758         assert(name);
759
760         machine = hashmap_get(m->machines, name);
761         if (!machine) {
762                 machine = machine_new(m, name);
763                 if (!machine)
764                         return -ENOMEM;
765         }
766
767         if (_machine)
768                 *_machine = machine;
769
770         return 0;
771 }