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