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