]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machinectl.c
sd-bus: rename sd_bus_open_system_container() to sd_bus_open_system_machine()
[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 login_machine(int argc, char *argv[], void *userdata) {
1014 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1015 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1016 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1017 _cleanup_event_unref_ sd_event *event = NULL;
1018 int master = -1, r, ret = 0;
1019 sd_bus *bus = userdata;
1020 const char *pty;
1021 sigset_t mask;
1022 char last_char = 0;
1023
1024 assert(bus);
1025
1026 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1027 arg_transport != BUS_TRANSPORT_MACHINE) {
1028 log_error("Login only supported on local machines.");
1029 return -ENOTSUP;
1030 }
1031
1032 r = sd_event_default(&event);
1033 if (r < 0)
1034 return log_error_errno(r, "Failed to get event loop: %m");
1035
1036 r = sd_bus_attach_event(bus, event, 0);
1037 if (r < 0)
1038 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1039
1040 r = sd_bus_message_new_method_call(bus,
1041 &m,
1042 "org.freedesktop.machine1",
1043 "/org/freedesktop/machine1",
1044 "org.freedesktop.machine1.Manager",
1045 "OpenMachineLogin");
1046 if (r < 0)
1047 return bus_log_create_error(r);
1048
1049 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1050 if (r < 0)
1051 return bus_log_create_error(r);
1052
1053 r = sd_bus_message_append(m, "s", argv[1]);
1054 if (r < 0)
1055 return bus_log_create_error(r);
1056
1057 r = sd_bus_call(bus, m, 0, &error, &reply);
1058 if (r < 0) {
1059 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1060 return r;
1061 }
1062
1063 r = sd_bus_message_read(reply, "hs", &master, &pty);
1064 if (r < 0)
1065 return bus_log_parse_error(r);
1066
1067 assert_se(sigemptyset(&mask) == 0);
1068 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1069 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1070
1071 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", argv[1]);
1072
1073 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1074 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1075
1076 r = pty_forward_new(event, master, true, &forward);
1077 if (r < 0)
1078 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1079
1080 r = sd_event_loop(event);
1081 if (r < 0)
1082 return log_error_errno(r, "Failed to run event loop: %m");
1083
1084 pty_forward_last_char(forward, &last_char);
1085
1086 forward = pty_forward_free(forward);
1087
1088 if (last_char != '\n')
1089 fputc('\n', stdout);
1090
1091 log_info("Connection to container %s terminated.", argv[1]);
1092
1093 sd_event_get_exit_code(event, &ret);
1094 return ret;
1095 }
1096
1097 static int help(int argc, char *argv[], void *userdata) {
1098
1099 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1100 "Send control commands to or query the virtual machine and container\n"
1101 "registration manager.\n\n"
1102 " -h --help Show this help\n"
1103 " --version Show package version\n"
1104 " --no-pager Do not pipe output into a pager\n"
1105 " --no-legend Do not show the headers and footers\n"
1106 " -H --host=[USER@]HOST Operate on remote host\n"
1107 " -M --machine=CONTAINER Operate on local container\n"
1108 " -p --property=NAME Show only properties by this name\n"
1109 " -a --all Show all properties, including empty ones\n"
1110 " -l --full Do not ellipsize output\n"
1111 " --kill-who=WHO Who to send signal to\n"
1112 " -s --signal=SIGNAL Which signal to send\n"
1113 " --read-only Create read-only bind mount\n"
1114 " --mkdir Create directory before bind mounting, if missing\n\n"
1115 "Machine Commands:\n"
1116 " list List running VMs and containers\n"
1117 " status NAME... Show VM/container status\n"
1118 " show NAME... Show properties of one or more VMs/containers\n"
1119 " login NAME Get a login prompt on a container\n"
1120 " poweroff NAME... Power off one or more containers\n"
1121 " reboot NAME... Reboot one or more containers\n"
1122 " kill NAME... Send signal to processes of a VM/container\n"
1123 " terminate NAME... Terminate one or more VMs/containers\n"
1124 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n"
1125 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1126 " copy-from NAME PATH [PATH] Copy files from a container to the host\n\n"
1127 "Image commands:\n"
1128 " list-images Show available images\n",
1129 program_invocation_short_name);
1130
1131 return 0;
1132 }
1133
1134 static int parse_argv(int argc, char *argv[]) {
1135
1136 enum {
1137 ARG_VERSION = 0x100,
1138 ARG_NO_PAGER,
1139 ARG_NO_LEGEND,
1140 ARG_KILL_WHO,
1141 ARG_READ_ONLY,
1142 ARG_MKDIR,
1143 };
1144
1145 static const struct option options[] = {
1146 { "help", no_argument, NULL, 'h' },
1147 { "version", no_argument, NULL, ARG_VERSION },
1148 { "property", required_argument, NULL, 'p' },
1149 { "all", no_argument, NULL, 'a' },
1150 { "full", no_argument, NULL, 'l' },
1151 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1152 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1153 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1154 { "signal", required_argument, NULL, 's' },
1155 { "host", required_argument, NULL, 'H' },
1156 { "machine", required_argument, NULL, 'M' },
1157 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1158 { "mkdir", no_argument, NULL, ARG_MKDIR },
1159 {}
1160 };
1161
1162 int c, r;
1163
1164 assert(argc >= 0);
1165 assert(argv);
1166
1167 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1168
1169 switch (c) {
1170
1171 case 'h':
1172 return help(0, NULL, NULL);
1173
1174 case ARG_VERSION:
1175 puts(PACKAGE_STRING);
1176 puts(SYSTEMD_FEATURES);
1177 return 0;
1178
1179 case 'p':
1180 r = strv_extend(&arg_property, optarg);
1181 if (r < 0)
1182 return log_oom();
1183
1184 /* If the user asked for a particular
1185 * property, show it to him, even if it is
1186 * empty. */
1187 arg_all = true;
1188 break;
1189
1190 case 'a':
1191 arg_all = true;
1192 break;
1193
1194 case 'l':
1195 arg_full = true;
1196 break;
1197
1198 case ARG_NO_PAGER:
1199 arg_no_pager = true;
1200 break;
1201
1202 case ARG_NO_LEGEND:
1203 arg_legend = false;
1204 break;
1205
1206 case ARG_KILL_WHO:
1207 arg_kill_who = optarg;
1208 break;
1209
1210 case 's':
1211 arg_signal = signal_from_string_try_harder(optarg);
1212 if (arg_signal < 0) {
1213 log_error("Failed to parse signal string %s.", optarg);
1214 return -EINVAL;
1215 }
1216 break;
1217
1218 case 'H':
1219 arg_transport = BUS_TRANSPORT_REMOTE;
1220 arg_host = optarg;
1221 break;
1222
1223 case 'M':
1224 arg_transport = BUS_TRANSPORT_MACHINE;
1225 arg_host = optarg;
1226 break;
1227
1228 case ARG_READ_ONLY:
1229 arg_read_only = true;
1230 break;
1231
1232 case ARG_MKDIR:
1233 arg_mkdir = true;
1234 break;
1235
1236 case '?':
1237 return -EINVAL;
1238
1239 default:
1240 assert_not_reached("Unhandled option");
1241 }
1242
1243 return 1;
1244 }
1245
1246 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1247
1248 static const Verb verbs[] = {
1249 { "help", VERB_ANY, VERB_ANY, 0, help },
1250 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
1251 { "list-images", VERB_ANY, 1, 0, list_images },
1252 { "status", 2, VERB_ANY, 0, show },
1253 { "show", VERB_ANY, VERB_ANY, 0, show },
1254 { "terminate", 2, VERB_ANY, 0, terminate_machine },
1255 { "reboot", 2, VERB_ANY, 0, reboot_machine },
1256 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
1257 { "kill", 2, VERB_ANY, 0, kill_machine },
1258 { "login", 2, 2, 0, login_machine },
1259 { "bind", 3, 4, 0, bind_mount },
1260 { "copy-to", 3, 4, 0, copy_files },
1261 { "copy-from", 3, 4, 0, copy_files },
1262 {}
1263 };
1264
1265 return dispatch_verb(argc, argv, verbs, bus);
1266 }
1267
1268 int main(int argc, char*argv[]) {
1269 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1270 int r;
1271
1272 setlocale(LC_ALL, "");
1273 log_parse_environment();
1274 log_open();
1275
1276 r = parse_argv(argc, argv);
1277 if (r <= 0)
1278 goto finish;
1279
1280 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1281 if (r < 0) {
1282 log_error_errno(r, "Failed to create bus connection: %m");
1283 goto finish;
1284 }
1285
1286 r = machinectl_main(argc, argv, bus);
1287
1288 finish:
1289 pager_close();
1290
1291 strv_free(arg_property);
1292
1293 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1294 }