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