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