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