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