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