]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machinectl.c
cde0c6af137a848b24d1b4ec184b5802bc67e8b6
[thirdparty/systemd.git] / src / machine / machinectl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 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 <sys/socket.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <getopt.h>
27 #include <pwd.h>
28 #include <locale.h>
29 #include <fcntl.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <net/if.h>
33 #include <sys/mount.h>
34 #include <libgen.h>
35
36 #include "sd-bus.h"
37 #include "log.h"
38 #include "util.h"
39 #include "macro.h"
40 #include "pager.h"
41 #include "bus-util.h"
42 #include "bus-error.h"
43 #include "build.h"
44 #include "strv.h"
45 #include "unit-name.h"
46 #include "cgroup-show.h"
47 #include "cgroup-util.h"
48 #include "ptyfwd.h"
49 #include "event-util.h"
50 #include "path-util.h"
51 #include "mkdir.h"
52 #include "copy.h"
53 #include "verbs.h"
54
55 static char **arg_property = NULL;
56 static bool arg_all = false;
57 static bool arg_full = false;
58 static bool arg_no_pager = false;
59 static bool arg_legend = true;
60 static const char *arg_kill_who = NULL;
61 static int arg_signal = SIGTERM;
62 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
63 static char *arg_host = NULL;
64 static bool arg_read_only = false;
65 static bool arg_mkdir = false;
66
67 static void pager_open_if_enabled(void) {
68
69 /* Cache result before we open the pager */
70 if (arg_no_pager)
71 return;
72
73 pager_open(false);
74 }
75
76 static int list_machines(int argc, char *argv[], void *userdata) {
77
78 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
79 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
80 const char *name, *class, *service, *object;
81 sd_bus *bus = userdata;
82 unsigned k = 0;
83 int r;
84
85 assert(bus);
86
87 pager_open_if_enabled();
88
89 r = sd_bus_call_method(
90 bus,
91 "org.freedesktop.machine1",
92 "/org/freedesktop/machine1",
93 "org.freedesktop.machine1.Manager",
94 "ListMachines",
95 &error,
96 &reply,
97 "");
98 if (r < 0) {
99 log_error("Could not get machines: %s", bus_error_message(&error, -r));
100 return r;
101 }
102
103 if (arg_legend)
104 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
105
106 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
107 if (r < 0)
108 return bus_log_parse_error(r);
109
110 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
111 printf("%-32s %-9s %-16s\n", name, class, service);
112
113 k++;
114 }
115 if (r < 0)
116 return bus_log_parse_error(r);
117
118 r = sd_bus_message_exit_container(reply);
119 if (r < 0)
120 return bus_log_parse_error(r);
121
122 if (arg_legend)
123 printf("\n%u machines listed.\n", k);
124
125 return 0;
126 }
127
128 typedef struct ImageInfo {
129 const char *name;
130 const char *type;
131 bool read_only;
132 } ImageInfo;
133
134 static int compare_image_info(const void *a, const void *b) {
135 const ImageInfo *x = a, *y = b;
136
137 return strcmp(x->name, y->name);
138 }
139
140 static int list_images(int argc, char *argv[], void *userdata) {
141
142 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
143 size_t max_name = strlen("NAME"), max_type = strlen("TYPE");
144 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
145 _cleanup_free_ ImageInfo *images = NULL;
146 size_t n_images = 0, n_allocated = 0, j;
147 const char *name, *type, *object;
148 sd_bus *bus = userdata;
149 int read_only;
150 int r;
151
152 assert(bus);
153
154 pager_open_if_enabled();
155
156 r = sd_bus_call_method(
157 bus,
158 "org.freedesktop.machine1",
159 "/org/freedesktop/machine1",
160 "org.freedesktop.machine1.Manager",
161 "ListImages",
162 &error,
163 &reply,
164 "");
165 if (r < 0) {
166 log_error("Could not get images: %s", bus_error_message(&error, -r));
167 return r;
168 }
169
170 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbo)");
171 if (r < 0)
172 return bus_log_parse_error(r);
173
174 while ((r = sd_bus_message_read(reply, "(ssbo)", &name, &type, &read_only, &object)) > 0) {
175
176 if (name[0] == '.' && !arg_all)
177 continue;
178
179 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
180 return log_oom();
181
182 images[n_images].name = name;
183 images[n_images].type = type;
184 images[n_images].read_only = read_only;
185
186 if (strlen(name) > max_name)
187 max_name = strlen(name);
188
189 if (strlen(type) > max_type)
190 max_type = strlen(type);
191
192 n_images++;
193 }
194 if (r < 0)
195 return bus_log_parse_error(r);
196
197 r = sd_bus_message_exit_container(reply);
198 if (r < 0)
199 return bus_log_parse_error(r);
200
201 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
202
203 if (arg_legend)
204 printf("%-*s %-*s %-3s\n", (int) max_name, "NAME", (int) max_type, "TYPE", "RO");
205
206 for (j = 0; j < n_images; j++) {
207 printf("%-*s %-*s %-3s\n",
208 (int) max_name, images[j].name,
209 (int) max_type, images[j].type,
210 yes_no(images[j].read_only));
211 }
212
213 if (r < 0)
214 return bus_log_parse_error(r);
215
216
217 if (arg_legend)
218 printf("\n%zu images listed.\n", n_images);
219
220 return 0;
221 }
222
223 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
224 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
225 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
226 _cleanup_free_ char *path = NULL;
227 const char *cgroup;
228 int r, output_flags;
229 unsigned c;
230
231 assert(bus);
232 assert(unit);
233
234 if (arg_transport == BUS_TRANSPORT_REMOTE)
235 return 0;
236
237 path = unit_dbus_path_from_name(unit);
238 if (!path)
239 return log_oom();
240
241 r = sd_bus_get_property(
242 bus,
243 "org.freedesktop.systemd1",
244 path,
245 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
246 "ControlGroup",
247 &error,
248 &reply,
249 "s");
250 if (r < 0) {
251 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
252 return r;
253 }
254
255 r = sd_bus_message_read(reply, "s", &cgroup);
256 if (r < 0)
257 return bus_log_parse_error(r);
258
259 if (isempty(cgroup))
260 return 0;
261
262 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
263 return 0;
264
265 output_flags =
266 arg_all * OUTPUT_SHOW_ALL |
267 arg_full * OUTPUT_FULL_WIDTH;
268
269 c = columns();
270 if (c > 18)
271 c -= 18;
272 else
273 c = 0;
274
275 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
276 return 0;
277 }
278
279 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
280 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
281 int r;
282
283 assert(bus);
284 assert(name);
285 assert(prefix);
286 assert(prefix2);
287
288 r = sd_bus_call_method(bus,
289 "org.freedesktop.machine1",
290 "/org/freedesktop/machine1",
291 "org.freedesktop.machine1.Manager",
292 "GetMachineAddresses",
293 NULL,
294 &reply,
295 "s", name);
296 if (r < 0)
297 return r;
298
299 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
300 if (r < 0)
301 return bus_log_parse_error(r);
302
303 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
304 int family;
305 const void *a;
306 size_t sz;
307 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
308
309 r = sd_bus_message_read(reply, "i", &family);
310 if (r < 0)
311 return bus_log_parse_error(r);
312
313 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
314 if (r < 0)
315 return bus_log_parse_error(r);
316
317 fputs(prefix, stdout);
318 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
319 if (family == AF_INET6 && ifi > 0)
320 printf("%%%i", ifi);
321 fputc('\n', stdout);
322
323 r = sd_bus_message_exit_container(reply);
324 if (r < 0)
325 return bus_log_parse_error(r);
326
327 if (prefix != prefix2)
328 prefix = prefix2;
329 }
330 if (r < 0)
331 return bus_log_parse_error(r);
332
333 r = sd_bus_message_exit_container(reply);
334 if (r < 0)
335 return bus_log_parse_error(r);
336
337 return 0;
338 }
339
340 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
341 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
342 const char *k, *v, *pretty = NULL;
343 int r;
344
345 assert(bus);
346 assert(name);
347 assert(prefix);
348
349 r = sd_bus_call_method(bus,
350 "org.freedesktop.machine1",
351 "/org/freedesktop/machine1",
352 "org.freedesktop.machine1.Manager",
353 "GetMachineOSRelease",
354 NULL,
355 &reply,
356 "s", name);
357 if (r < 0)
358 return r;
359
360 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
361 if (r < 0)
362 return bus_log_parse_error(r);
363
364 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
365 if (streq(k, "PRETTY_NAME"))
366 pretty = v;
367
368 }
369 if (r < 0)
370 return bus_log_parse_error(r);
371
372 r = sd_bus_message_exit_container(reply);
373 if (r < 0)
374 return bus_log_parse_error(r);
375
376 if (pretty)
377 printf("%s%s\n", prefix, pretty);
378
379 return 0;
380 }
381
382 typedef struct MachineStatusInfo {
383 char *name;
384 sd_id128_t id;
385 char *class;
386 char *service;
387 char *unit;
388 char *root_directory;
389 pid_t leader;
390 usec_t timestamp;
391 int *netif;
392 unsigned n_netif;
393 } MachineStatusInfo;
394
395 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
396 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
397 char since2[FORMAT_TIMESTAMP_MAX], *s2;
398 int ifi = -1;
399
400 assert(bus);
401 assert(i);
402
403 fputs(strna(i->name), stdout);
404
405 if (!sd_id128_equal(i->id, SD_ID128_NULL))
406 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
407 else
408 putchar('\n');
409
410 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
411 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
412
413 if (s1)
414 printf("\t Since: %s; %s\n", s2, s1);
415 else if (s2)
416 printf("\t Since: %s\n", s2);
417
418 if (i->leader > 0) {
419 _cleanup_free_ char *t = NULL;
420
421 printf("\t Leader: %u", (unsigned) i->leader);
422
423 get_process_comm(i->leader, &t);
424 if (t)
425 printf(" (%s)", t);
426
427 putchar('\n');
428 }
429
430 if (i->service) {
431 printf("\t Service: %s", i->service);
432
433 if (i->class)
434 printf("; class %s", i->class);
435
436 putchar('\n');
437 } else if (i->class)
438 printf("\t Class: %s\n", i->class);
439
440 if (i->root_directory)
441 printf("\t Root: %s\n", i->root_directory);
442
443 if (i->n_netif > 0) {
444 unsigned c;
445
446 fputs("\t Iface:", stdout);
447
448 for (c = 0; c < i->n_netif; c++) {
449 char name[IF_NAMESIZE+1] = "";
450
451 if (if_indextoname(i->netif[c], name)) {
452 fputc(' ', stdout);
453 fputs(name, stdout);
454
455 if (ifi < 0)
456 ifi = i->netif[c];
457 else
458 ifi = 0;
459 } else
460 printf(" %i", i->netif[c]);
461 }
462
463 fputc('\n', stdout);
464 }
465
466 print_addresses(bus, i->name, ifi,
467 "\t Address: ",
468 "\t ");
469
470 print_os_release(bus, i->name, "\t OS: ");
471
472 if (i->unit) {
473 printf("\t Unit: %s\n", i->unit);
474 show_unit_cgroup(bus, i->unit, i->leader);
475 }
476 }
477
478 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
479 MachineStatusInfo *i = userdata;
480 size_t l;
481 const void *v;
482 int r;
483
484 assert_cc(sizeof(int32_t) == sizeof(int));
485 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
486 if (r < 0)
487 return r;
488 if (r == 0)
489 return -EBADMSG;
490
491 i->n_netif = l / sizeof(int32_t);
492 i->netif = memdup(v, l);
493 if (!i->netif)
494 return -ENOMEM;
495
496 return 0;
497 }
498
499 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
500
501 static const struct bus_properties_map map[] = {
502 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
503 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
504 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
505 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
506 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
507 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
508 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
509 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
510 { "NetworkInterfaces", "ai", map_netif, 0 },
511 {}
512 };
513
514 MachineStatusInfo info = {};
515 int r;
516
517 assert(verb);
518 assert(bus);
519 assert(path);
520 assert(new_line);
521
522 r = bus_map_all_properties(bus,
523 "org.freedesktop.machine1",
524 path,
525 map,
526 &info);
527 if (r < 0)
528 return log_error_errno(r, "Could not get properties: %m");
529
530 if (*new_line)
531 printf("\n");
532 *new_line = true;
533
534 print_machine_status_info(bus, &info);
535
536 free(info.name);
537 free(info.class);
538 free(info.service);
539 free(info.unit);
540 free(info.root_directory);
541 free(info.netif);
542
543 return r;
544 }
545
546 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
547 int r;
548
549 assert(bus);
550 assert(path);
551 assert(new_line);
552
553 if (*new_line)
554 printf("\n");
555
556 *new_line = true;
557
558 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
559 if (r < 0)
560 log_error_errno(r, "Could not get properties: %m");
561
562 return r;
563 }
564
565 static int show(int argc, char *argv[], void *userdata) {
566
567 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
568 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
569 bool properties, new_line = false;
570 sd_bus *bus = userdata;
571 int r = 0, i;
572
573 assert(bus);
574
575 properties = !strstr(argv[0], "status");
576
577 pager_open_if_enabled();
578
579 if (properties && argc <= 1) {
580
581 /* If no argument is specified, inspect the manager
582 * itself */
583 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
584 if (r < 0)
585 return r;
586 }
587
588 for (i = 1; i < argc; i++) {
589 const char *path = NULL;
590
591 r = sd_bus_call_method(
592 bus,
593 "org.freedesktop.machine1",
594 "/org/freedesktop/machine1",
595 "org.freedesktop.machine1.Manager",
596 "GetMachine",
597 &error,
598 &reply,
599 "s", argv[i]);
600 if (r < 0) {
601 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
602 return r;
603 }
604
605 r = sd_bus_message_read(reply, "o", &path);
606 if (r < 0)
607 return bus_log_parse_error(r);
608
609 if (properties)
610 r = show_properties(bus, path, &new_line);
611 else
612 r = show_info(argv[0], bus, path, &new_line);
613 }
614
615 return r;
616 }
617
618 static int kill_machine(int argc, char *argv[], void *userdata) {
619 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
620 sd_bus *bus = userdata;
621 int i;
622
623 assert(bus);
624
625 if (!arg_kill_who)
626 arg_kill_who = "all";
627
628 for (i = 1; i < argc; i++) {
629 int r;
630
631 r = sd_bus_call_method(
632 bus,
633 "org.freedesktop.machine1",
634 "/org/freedesktop/machine1",
635 "org.freedesktop.machine1.Manager",
636 "KillMachine",
637 &error,
638 NULL,
639 "ssi", argv[i], arg_kill_who, arg_signal);
640 if (r < 0) {
641 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
642 return r;
643 }
644 }
645
646 return 0;
647 }
648
649 static int reboot_machine(int argc, char *argv[], void *userdata) {
650 arg_kill_who = "leader";
651 arg_signal = SIGINT; /* sysvinit + systemd */
652
653 return kill_machine(argc, argv, userdata);
654 }
655
656 static int poweroff_machine(int argc, char *argv[], void *userdata) {
657 arg_kill_who = "leader";
658 arg_signal = SIGRTMIN+4; /* only systemd */
659
660 return kill_machine(argc, argv, userdata);
661 }
662
663 static int terminate_machine(int argc, char *argv[], void *userdata) {
664 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
665 sd_bus *bus = userdata;
666 int i;
667
668 assert(bus);
669
670 for (i = 1; i < argc; i++) {
671 int r;
672
673 r = sd_bus_call_method(
674 bus,
675 "org.freedesktop.machine1",
676 "/org/freedesktop/machine1",
677 "org.freedesktop.machine1.Manager",
678 "TerminateMachine",
679 &error,
680 NULL,
681 "s", argv[i]);
682 if (r < 0) {
683 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
684 return r;
685 }
686 }
687
688 return 0;
689 }
690
691 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
692 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
693 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
694 const char *object;
695 uint32_t leader;
696 int r;
697
698 assert(bus);
699 assert(name);
700 assert(ret);
701
702 r = sd_bus_call_method(
703 bus,
704 "org.freedesktop.machine1",
705 "/org/freedesktop/machine1",
706 "org.freedesktop.machine1.Manager",
707 "GetMachine",
708 &error,
709 &reply,
710 "s", name);
711 if (r < 0) {
712 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
713 return r;
714 }
715
716 r = sd_bus_message_read(reply, "o", &object);
717 if (r < 0)
718 return bus_log_parse_error(r);
719
720 r = sd_bus_get_property(
721 bus,
722 "org.freedesktop.machine1",
723 object,
724 "org.freedesktop.machine1.Machine",
725 "Leader",
726 &error,
727 &reply2,
728 "u");
729 if (r < 0)
730 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
731
732 r = sd_bus_message_read(reply2, "u", &leader);
733 if (r < 0)
734 return bus_log_parse_error(r);
735
736 *ret = leader;
737 return 0;
738 }
739
740 static int copy_files(int argc, char *argv[], void *userdata) {
741 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
742 _cleanup_close_ int hostfd = -1;
743 sd_bus *bus = userdata;
744 pid_t child, leader;
745 bool copy_from;
746 siginfo_t si;
747 int r;
748
749 assert(bus);
750
751 copy_from = streq(argv[0], "copy-from");
752 dest = argv[3] ?: argv[2];
753 host_path = strdupa(copy_from ? dest : argv[2]);
754 container_path = strdupa(copy_from ? argv[2] : dest);
755
756 if (!path_is_absolute(container_path)) {
757 log_error("Container path not absolute.");
758 return -EINVAL;
759 }
760
761 t = strdup(host_path);
762 host_basename = basename(t);
763 host_dirname = dirname(host_path);
764
765 t = strdup(container_path);
766 container_basename = basename(t);
767 container_dirname = dirname(container_path);
768
769 r = machine_get_leader(bus, argv[1], &leader);
770 if (r < 0)
771 return r;
772
773 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
774 if (r < 0)
775 return log_error_errno(errno, "Failed to open source directory: %m");
776
777 child = fork();
778 if (child < 0)
779 return log_error_errno(errno, "Failed to fork(): %m");
780
781 if (child == 0) {
782 int containerfd;
783 const char *q;
784 int mntfd;
785
786 q = procfs_file_alloca(leader, "ns/mnt");
787 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
788 if (mntfd < 0) {
789 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
790 _exit(EXIT_FAILURE);
791 }
792
793 if (setns(mntfd, CLONE_NEWNS) < 0) {
794 log_error_errno(errno, "Failed to join namespace of leader: %m");
795 _exit(EXIT_FAILURE);
796 }
797
798 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
799 if (containerfd < 0) {
800 log_error_errno(errno, "Failed top open destination directory: %m");
801 _exit(EXIT_FAILURE);
802 }
803
804 if (copy_from)
805 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
806 else
807 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
808 if (r < 0) {
809 log_error_errno(errno, "Failed to copy tree: %m");
810 _exit(EXIT_FAILURE);
811 }
812
813 _exit(EXIT_SUCCESS);
814 }
815
816 r = wait_for_terminate(child, &si);
817 if (r < 0)
818 return log_error_errno(r, "Failed to wait for client: %m");
819 if (si.si_code != CLD_EXITED) {
820 log_error("Client died abnormally.");
821 return -EIO;
822 }
823 if (si.si_status != EXIT_SUCCESS)
824 return -EIO;
825
826 return 0;
827 }
828
829 static int bind_mount(int argc, char *argv[], void *userdata) {
830 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
831 sd_bus *bus = userdata;
832 pid_t child, leader;
833 const char *dest;
834 siginfo_t si;
835 bool mount_slave_created = false, mount_slave_mounted = false,
836 mount_tmp_created = false, mount_tmp_mounted = false,
837 mount_outside_created = false, mount_outside_mounted = false;
838 int r;
839
840 assert(bus);
841
842 /* One day, when bind mounting /proc/self/fd/n works across
843 * namespace boundaries we should rework this logic to make
844 * use of it... */
845
846 dest = argv[3] ?: argv[2];
847 if (!path_is_absolute(dest)) {
848 log_error("Destination path not absolute.");
849 return -EINVAL;
850 }
851
852 p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
853 if (access(p, F_OK) < 0) {
854 log_error("Container does not allow propagation of mount points.");
855 return -ENOTSUP;
856 }
857
858 r = machine_get_leader(bus, argv[1], &leader);
859 if (r < 0)
860 return r;
861
862 /* Our goal is to install a new bind mount into the container,
863 possibly read-only. This is irritatingly complex
864 unfortunately, currently.
865
866 First, we start by creating a private playground in /tmp,
867 that we can mount MS_SLAVE. (Which is necessary, since
868 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
869 mounts.) */
870
871 if (!mkdtemp(mount_slave))
872 return log_error_errno(errno, "Failed to create playground: %m");
873
874 mount_slave_created = true;
875
876 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
877 r = log_error_errno(errno, "Failed to make bind mount: %m");
878 goto finish;
879 }
880
881 mount_slave_mounted = true;
882
883 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
884 r = log_error_errno(errno, "Failed to remount slave: %m");
885 goto finish;
886 }
887
888 /* Second, we mount the source directory to a directory inside
889 of our MS_SLAVE playground. */
890 mount_tmp = strappenda(mount_slave, "/mount");
891 if (mkdir(mount_tmp, 0700) < 0) {
892 r = log_error_errno(errno, "Failed to create temporary mount: %m");
893 goto finish;
894 }
895
896 mount_tmp_created = true;
897
898 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
899 r = log_error_errno(errno, "Failed to overmount: %m");
900 goto finish;
901 }
902
903 mount_tmp_mounted = true;
904
905 /* Third, we remount the new bind mount read-only if requested. */
906 if (arg_read_only)
907 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
908 r = log_error_errno(errno, "Failed to mark read-only: %m");
909 goto finish;
910 }
911
912 /* Fourth, we move the new bind mount into the propagation
913 * directory. This way it will appear there read-only
914 * right-away. */
915
916 mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
917 if (!mkdtemp(mount_outside)) {
918 r = log_error_errno(errno, "Cannot create propagation directory: %m");
919 goto finish;
920 }
921
922 mount_outside_created = true;
923
924 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
925 r = log_error_errno(errno, "Failed to move: %m");
926 goto finish;
927 }
928
929 mount_outside_mounted = true;
930 mount_tmp_mounted = false;
931
932 (void) rmdir(mount_tmp);
933 mount_tmp_created = false;
934
935 (void) umount(mount_slave);
936 mount_slave_mounted = false;
937
938 (void) rmdir(mount_slave);
939 mount_slave_created = false;
940
941 child = fork();
942 if (child < 0) {
943 r = log_error_errno(errno, "Failed to fork(): %m");
944 goto finish;
945 }
946
947 if (child == 0) {
948 const char *mount_inside;
949 int mntfd;
950 const char *q;
951
952 q = procfs_file_alloca(leader, "ns/mnt");
953 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
954 if (mntfd < 0) {
955 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
956 _exit(EXIT_FAILURE);
957 }
958
959 if (setns(mntfd, CLONE_NEWNS) < 0) {
960 log_error_errno(errno, "Failed to join namespace of leader: %m");
961 _exit(EXIT_FAILURE);
962 }
963
964 if (arg_mkdir)
965 mkdir_p(dest, 0755);
966
967 /* Fifth, move the mount to the right place inside */
968 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
969 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
970 log_error_errno(errno, "Failed to mount: %m");
971 _exit(EXIT_FAILURE);
972 }
973
974 _exit(EXIT_SUCCESS);
975 }
976
977 r = wait_for_terminate(child, &si);
978 if (r < 0) {
979 log_error_errno(r, "Failed to wait for client: %m");
980 goto finish;
981 }
982 if (si.si_code != CLD_EXITED) {
983 log_error("Client died abnormally.");
984 r = -EIO;
985 goto finish;
986 }
987 if (si.si_status != EXIT_SUCCESS) {
988 r = -EIO;
989 goto finish;
990 }
991
992 r = 0;
993
994 finish:
995 if (mount_outside_mounted)
996 umount(mount_outside);
997 if (mount_outside_created)
998 rmdir(mount_outside);
999
1000 if (mount_tmp_mounted)
1001 umount(mount_tmp);
1002 if (mount_tmp_created)
1003 umount(mount_tmp);
1004
1005 if (mount_slave_mounted)
1006 umount(mount_slave);
1007 if (mount_slave_created)
1008 umount(mount_slave);
1009
1010 return r;
1011 }
1012
1013 static int openpt_in_namespace(pid_t pid, int flags) {
1014 _cleanup_close_pair_ int pair[2] = { -1, -1 };
1015 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
1016 union {
1017 struct cmsghdr cmsghdr;
1018 uint8_t buf[CMSG_SPACE(sizeof(int))];
1019 } control = {};
1020 struct msghdr mh = {
1021 .msg_control = &control,
1022 .msg_controllen = sizeof(control),
1023 };
1024 struct cmsghdr *cmsg;
1025 int master = -1, r;
1026 pid_t child;
1027 siginfo_t si;
1028
1029 assert(pid > 0);
1030
1031 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
1032 if (r < 0)
1033 return r;
1034
1035 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1036 return -errno;
1037
1038 child = fork();
1039 if (child < 0)
1040 return -errno;
1041
1042 if (child == 0) {
1043 pair[0] = safe_close(pair[0]);
1044
1045 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
1046 if (r < 0)
1047 _exit(EXIT_FAILURE);
1048
1049 master = posix_openpt(flags);
1050 if (master < 0)
1051 _exit(EXIT_FAILURE);
1052
1053 cmsg = CMSG_FIRSTHDR(&mh);
1054 cmsg->cmsg_level = SOL_SOCKET;
1055 cmsg->cmsg_type = SCM_RIGHTS;
1056 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
1057 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
1058
1059 mh.msg_controllen = cmsg->cmsg_len;
1060
1061 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
1062 _exit(EXIT_FAILURE);
1063
1064 _exit(EXIT_SUCCESS);
1065 }
1066
1067 pair[1] = safe_close(pair[1]);
1068
1069 r = wait_for_terminate(child, &si);
1070 if (r < 0)
1071 return r;
1072 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
1073 return -EIO;
1074
1075 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
1076 return -errno;
1077
1078 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
1079 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
1080 int *fds;
1081 unsigned n_fds;
1082
1083 fds = (int*) CMSG_DATA(cmsg);
1084 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
1085
1086 if (n_fds != 1) {
1087 close_many(fds, n_fds);
1088 return -EIO;
1089 }
1090
1091 master = fds[0];
1092 }
1093
1094 if (master < 0)
1095 return -EIO;
1096
1097 return master;
1098 }
1099
1100 static int login_machine(int argc, char *argv[], void *userdata) {
1101 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1102 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1103 _cleanup_bus_close_unref_ sd_bus *container_bus = NULL;
1104 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1105 _cleanup_event_unref_ sd_event *event = NULL;
1106 _cleanup_close_ int master = -1;
1107 _cleanup_free_ char *getty = NULL;
1108 sd_bus *bus = userdata;
1109 const char *pty, *p;
1110 pid_t leader;
1111 sigset_t mask;
1112 int r, ret = 0;
1113
1114 assert(bus);
1115
1116 if (arg_transport != BUS_TRANSPORT_LOCAL) {
1117 log_error("Login only supported on local machines.");
1118 return -ENOTSUP;
1119 }
1120
1121 r = sd_event_default(&event);
1122 if (r < 0)
1123 return log_error_errno(r, "Failed to get event loop: %m");
1124
1125 r = sd_bus_attach_event(bus, event, 0);
1126 if (r < 0)
1127 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1128
1129 r = machine_get_leader(bus, argv[1], &leader);
1130 if (r < 0)
1131 return r;
1132
1133 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
1134 if (master < 0)
1135 return log_error_errno(master, "Failed to acquire pseudo tty: %m");
1136
1137 pty = ptsname(master);
1138 if (!pty)
1139 return log_error_errno(errno, "Failed to get pty name: %m");
1140
1141 p = startswith(pty, "/dev/pts/");
1142 if (!p) {
1143 log_error("Invalid pty name %s.", pty);
1144 return -EIO;
1145 }
1146
1147 r = sd_bus_open_system_container(&container_bus, argv[1]);
1148 if (r < 0)
1149 return log_error_errno(r, "Failed to get container bus: %m");
1150
1151 getty = strjoin("container-getty@", p, ".service", NULL);
1152 if (!getty)
1153 return log_oom();
1154
1155 if (unlockpt(master) < 0)
1156 return log_error_errno(errno, "Failed to unlock tty: %m");
1157
1158 r = sd_bus_call_method(container_bus,
1159 "org.freedesktop.systemd1",
1160 "/org/freedesktop/systemd1",
1161 "org.freedesktop.systemd1.Manager",
1162 "StartUnit",
1163 &error, &reply,
1164 "ss", getty, "replace");
1165 if (r < 0) {
1166 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
1167 return r;
1168 }
1169
1170 container_bus = sd_bus_unref(container_bus);
1171
1172 assert_se(sigemptyset(&mask) == 0);
1173 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1174 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1175
1176 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", argv[1]);
1177
1178 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1179 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1180
1181 r = pty_forward_new(event, master, &forward);
1182 if (r < 0)
1183 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1184
1185 r = sd_event_loop(event);
1186 if (r < 0)
1187 return log_error_errno(r, "Failed to run event loop: %m");
1188
1189 forward = pty_forward_free(forward);
1190
1191 fputc('\n', stdout);
1192
1193 log_info("Connection to container %s terminated.", argv[1]);
1194
1195 sd_event_get_exit_code(event, &ret);
1196 return ret;
1197 }
1198
1199 static int help(int argc, char *argv[], void *userdata) {
1200
1201 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1202 "Send control commands to or query the virtual machine and container\n"
1203 "registration manager.\n\n"
1204 " -h --help Show this help\n"
1205 " --version Show package version\n"
1206 " --no-pager Do not pipe output into a pager\n"
1207 " --no-legend Do not show the headers and footers\n"
1208 " -H --host=[USER@]HOST Operate on remote host\n"
1209 " -M --machine=CONTAINER Operate on local container\n"
1210 " -p --property=NAME Show only properties by this name\n"
1211 " -a --all Show all properties, including empty ones\n"
1212 " -l --full Do not ellipsize output\n"
1213 " --kill-who=WHO Who to send signal to\n"
1214 " -s --signal=SIGNAL Which signal to send\n"
1215 " --read-only Create read-only bind mount\n"
1216 " --mkdir Create directory before bind mounting, if missing\n\n"
1217 "Machine Commands:\n"
1218 " list List running VMs and containers\n"
1219 " status NAME... Show VM/container status\n"
1220 " show NAME... Show properties of one or more VMs/containers\n"
1221 " login NAME Get a login prompt on a container\n"
1222 " poweroff NAME... Power off one or more containers\n"
1223 " reboot NAME... Reboot one or more containers\n"
1224 " kill NAME... Send signal to processes of a VM/container\n"
1225 " terminate NAME... Terminate one or more VMs/containers\n"
1226 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n"
1227 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1228 " copy-from NAME PATH [PATH] Copy files from a container to the host\n\n"
1229 "Image commands:\n"
1230 " list-images Show available images\n",
1231 program_invocation_short_name);
1232
1233 return 0;
1234 }
1235
1236 static int parse_argv(int argc, char *argv[]) {
1237
1238 enum {
1239 ARG_VERSION = 0x100,
1240 ARG_NO_PAGER,
1241 ARG_NO_LEGEND,
1242 ARG_KILL_WHO,
1243 ARG_READ_ONLY,
1244 ARG_MKDIR,
1245 };
1246
1247 static const struct option options[] = {
1248 { "help", no_argument, NULL, 'h' },
1249 { "version", no_argument, NULL, ARG_VERSION },
1250 { "property", required_argument, NULL, 'p' },
1251 { "all", no_argument, NULL, 'a' },
1252 { "full", no_argument, NULL, 'l' },
1253 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1254 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1255 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1256 { "signal", required_argument, NULL, 's' },
1257 { "host", required_argument, NULL, 'H' },
1258 { "machine", required_argument, NULL, 'M' },
1259 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1260 { "mkdir", no_argument, NULL, ARG_MKDIR },
1261 {}
1262 };
1263
1264 int c, r;
1265
1266 assert(argc >= 0);
1267 assert(argv);
1268
1269 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1270
1271 switch (c) {
1272
1273 case 'h':
1274 return help(0, NULL, NULL);
1275
1276 case ARG_VERSION:
1277 puts(PACKAGE_STRING);
1278 puts(SYSTEMD_FEATURES);
1279 return 0;
1280
1281 case 'p':
1282 r = strv_extend(&arg_property, optarg);
1283 if (r < 0)
1284 return log_oom();
1285
1286 /* If the user asked for a particular
1287 * property, show it to him, even if it is
1288 * empty. */
1289 arg_all = true;
1290 break;
1291
1292 case 'a':
1293 arg_all = true;
1294 break;
1295
1296 case 'l':
1297 arg_full = true;
1298 break;
1299
1300 case ARG_NO_PAGER:
1301 arg_no_pager = true;
1302 break;
1303
1304 case ARG_NO_LEGEND:
1305 arg_legend = false;
1306 break;
1307
1308 case ARG_KILL_WHO:
1309 arg_kill_who = optarg;
1310 break;
1311
1312 case 's':
1313 arg_signal = signal_from_string_try_harder(optarg);
1314 if (arg_signal < 0) {
1315 log_error("Failed to parse signal string %s.", optarg);
1316 return -EINVAL;
1317 }
1318 break;
1319
1320 case 'H':
1321 arg_transport = BUS_TRANSPORT_REMOTE;
1322 arg_host = optarg;
1323 break;
1324
1325 case 'M':
1326 arg_transport = BUS_TRANSPORT_CONTAINER;
1327 arg_host = optarg;
1328 break;
1329
1330 case ARG_READ_ONLY:
1331 arg_read_only = true;
1332 break;
1333
1334 case ARG_MKDIR:
1335 arg_mkdir = true;
1336 break;
1337
1338 case '?':
1339 return -EINVAL;
1340
1341 default:
1342 assert_not_reached("Unhandled option");
1343 }
1344
1345 return 1;
1346 }
1347
1348 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1349
1350 static const Verb verbs[] = {
1351 { "help", VERB_ANY, VERB_ANY, 0, help },
1352 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
1353 { "list-images", VERB_ANY, 1, 0, list_images },
1354 { "status", 2, VERB_ANY, 0, show },
1355 { "show", VERB_ANY, VERB_ANY, 0, show },
1356 { "terminate", 2, VERB_ANY, 0, terminate_machine },
1357 { "reboot", 2, VERB_ANY, 0, reboot_machine },
1358 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
1359 { "kill", 2, VERB_ANY, 0, kill_machine },
1360 { "login", 2, 2, 0, login_machine },
1361 { "bind", 3, 4, 0, bind_mount },
1362 { "copy-to", 3, 4, 0, copy_files },
1363 { "copy-from", 3, 4, 0, copy_files },
1364 {}
1365 };
1366
1367 return dispatch_verb(argc, argv, verbs, bus);
1368 }
1369
1370 int main(int argc, char*argv[]) {
1371 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1372 int r;
1373
1374 setlocale(LC_ALL, "");
1375 log_parse_environment();
1376 log_open();
1377
1378 r = parse_argv(argc, argv);
1379 if (r <= 0)
1380 goto finish;
1381
1382 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1383 if (r < 0) {
1384 log_error_errno(r, "Failed to create bus connection: %m");
1385 goto finish;
1386 }
1387
1388 r = machinectl_main(argc, argv, bus);
1389
1390 finish:
1391 pager_close();
1392
1393 strv_free(arg_property);
1394
1395 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1396 }