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