1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
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.
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.
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/>.
22 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
33 #include <sys/mount.h>
42 #include "bus-error.h"
45 #include "unit-name.h"
46 #include "cgroup-show.h"
47 #include "cgroup-util.h"
49 #include "event-util.h"
50 #include "path-util.h"
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;
67 static void pager_open_if_enabled(void) {
69 /* Cache result before we open the pager */
76 static int list_machines(int argc
, char *argv
[], void *userdata
) {
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
;
87 pager_open_if_enabled();
89 r
= sd_bus_call_method(
91 "org.freedesktop.machine1",
92 "/org/freedesktop/machine1",
93 "org.freedesktop.machine1.Manager",
99 log_error("Could not get machines: %s", bus_error_message(&error
, -r
));
104 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
106 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssso)");
108 return bus_log_parse_error(r
);
110 while ((r
= sd_bus_message_read(reply
, "(ssso)", &name
, &class, &service
, &object
)) > 0) {
111 printf("%-32s %-9s %-16s\n", name
, class, service
);
116 return bus_log_parse_error(r
);
118 r
= sd_bus_message_exit_container(reply
);
120 return bus_log_parse_error(r
);
123 printf("\n%u machines listed.\n", k
);
128 typedef struct ImageInfo
{
134 static int compare_image_info(const void *a
, const void *b
) {
135 const ImageInfo
*x
= a
, *y
= b
;
137 return strcmp(x
->name
, y
->name
);
140 static int list_images(int argc
, char *argv
[], void *userdata
) {
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
;
154 pager_open_if_enabled();
156 r
= sd_bus_call_method(
158 "org.freedesktop.machine1",
159 "/org/freedesktop/machine1",
160 "org.freedesktop.machine1.Manager",
166 log_error("Could not get images: %s", bus_error_message(&error
, -r
));
170 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssbo)");
172 return bus_log_parse_error(r
);
174 while ((r
= sd_bus_message_read(reply
, "(ssbo)", &name
, &type
, &read_only
, &object
)) > 0) {
176 if (name
[0] == '.' && !arg_all
)
179 if (!GREEDY_REALLOC(images
, n_allocated
, n_images
+ 1))
182 images
[n_images
].name
= name
;
183 images
[n_images
].type
= type
;
184 images
[n_images
].read_only
= read_only
;
186 if (strlen(name
) > max_name
)
187 max_name
= strlen(name
);
189 if (strlen(type
) > max_type
)
190 max_type
= strlen(type
);
195 return bus_log_parse_error(r
);
197 r
= sd_bus_message_exit_container(reply
);
199 return bus_log_parse_error(r
);
201 qsort_safe(images
, n_images
, sizeof(ImageInfo
), compare_image_info
);
204 printf("%-*s %-*s %-3s\n", (int) max_name
, "NAME", (int) max_type
, "TYPE", "RO");
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
));
214 return bus_log_parse_error(r
);
218 printf("\n%zu images listed.\n", n_images
);
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
;
234 if (arg_transport
== BUS_TRANSPORT_REMOTE
)
237 path
= unit_dbus_path_from_name(unit
);
241 r
= sd_bus_get_property(
243 "org.freedesktop.systemd1",
245 endswith(unit
, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
251 log_error("Failed to query ControlGroup: %s", bus_error_message(&error
, -r
));
255 r
= sd_bus_message_read(reply
, "s", &cgroup
);
257 return bus_log_parse_error(r
);
262 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, false) != 0 && leader
<= 0)
266 arg_all
* OUTPUT_SHOW_ALL
|
267 arg_full
* OUTPUT_FULL_WIDTH
;
275 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, "\t\t ", c
, false, &leader
, leader
> 0, output_flags
);
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
;
288 r
= sd_bus_call_method(bus
,
289 "org.freedesktop.machine1",
290 "/org/freedesktop/machine1",
291 "org.freedesktop.machine1.Manager",
292 "GetMachineAddresses",
299 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
301 return bus_log_parse_error(r
);
303 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
307 char buffer
[MAX(INET6_ADDRSTRLEN
, INET_ADDRSTRLEN
)];
309 r
= sd_bus_message_read(reply
, "i", &family
);
311 return bus_log_parse_error(r
);
313 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
315 return bus_log_parse_error(r
);
317 fputs(prefix
, stdout
);
318 fputs(inet_ntop(family
, a
, buffer
, sizeof(buffer
)), stdout
);
319 if (family
== AF_INET6
&& ifi
> 0)
323 r
= sd_bus_message_exit_container(reply
);
325 return bus_log_parse_error(r
);
327 if (prefix
!= prefix2
)
331 return bus_log_parse_error(r
);
333 r
= sd_bus_message_exit_container(reply
);
335 return bus_log_parse_error(r
);
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
;
349 r
= sd_bus_call_method(bus
,
350 "org.freedesktop.machine1",
351 "/org/freedesktop/machine1",
352 "org.freedesktop.machine1.Manager",
353 "GetMachineOSRelease",
360 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
362 return bus_log_parse_error(r
);
364 while ((r
= sd_bus_message_read(reply
, "{ss}", &k
, &v
)) > 0) {
365 if (streq(k
, "PRETTY_NAME"))
370 return bus_log_parse_error(r
);
372 r
= sd_bus_message_exit_container(reply
);
374 return bus_log_parse_error(r
);
377 printf("%s%s\n", prefix
, pretty
);
382 typedef struct MachineStatusInfo
{
388 char *root_directory
;
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
;
403 fputs(strna(i
->name
), stdout
);
405 if (!sd_id128_equal(i
->id
, SD_ID128_NULL
))
406 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
410 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
);
411 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
);
414 printf("\t Since: %s; %s\n", s2
, s1
);
416 printf("\t Since: %s\n", s2
);
419 _cleanup_free_
char *t
= NULL
;
421 printf("\t Leader: %u", (unsigned) i
->leader
);
423 get_process_comm(i
->leader
, &t
);
431 printf("\t Service: %s", i
->service
);
434 printf("; class %s", i
->class);
438 printf("\t Class: %s\n", i
->class);
440 if (i
->root_directory
)
441 printf("\t Root: %s\n", i
->root_directory
);
443 if (i
->n_netif
> 0) {
446 fputs("\t Iface:", stdout
);
448 for (c
= 0; c
< i
->n_netif
; c
++) {
449 char name
[IF_NAMESIZE
+1] = "";
451 if (if_indextoname(i
->netif
[c
], name
)) {
460 printf(" %i", i
->netif
[c
]);
466 print_addresses(bus
, i
->name
, ifi
,
470 print_os_release(bus
, i
->name
, "\t OS: ");
473 printf("\t Unit: %s\n", i
->unit
);
474 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
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
;
484 assert_cc(sizeof(int32_t) == sizeof(int));
485 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
491 i
->n_netif
= l
/ sizeof(int32_t);
492 i
->netif
= memdup(v
, l
);
499 static int show_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
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 },
514 MachineStatusInfo info
= {};
522 r
= bus_map_all_properties(bus
,
523 "org.freedesktop.machine1",
528 return log_error_errno(r
, "Could not get properties: %m");
534 print_machine_status_info(bus
, &info
);
540 free(info
.root_directory
);
546 static int show_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
558 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
560 log_error_errno(r
, "Could not get properties: %m");
565 static int show(int argc
, char *argv
[], void *userdata
) {
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
;
575 properties
= !strstr(argv
[0], "status");
577 pager_open_if_enabled();
579 if (properties
&& argc
<= 1) {
581 /* If no argument is specified, inspect the manager
583 r
= show_properties(bus
, "/org/freedesktop/machine1", &new_line
);
588 for (i
= 1; i
< argc
; i
++) {
589 const char *path
= NULL
;
591 r
= sd_bus_call_method(
593 "org.freedesktop.machine1",
594 "/org/freedesktop/machine1",
595 "org.freedesktop.machine1.Manager",
601 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
605 r
= sd_bus_message_read(reply
, "o", &path
);
607 return bus_log_parse_error(r
);
610 r
= show_properties(bus
, path
, &new_line
);
612 r
= show_info(argv
[0], bus
, path
, &new_line
);
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
;
626 arg_kill_who
= "all";
628 for (i
= 1; i
< argc
; i
++) {
631 r
= sd_bus_call_method(
633 "org.freedesktop.machine1",
634 "/org/freedesktop/machine1",
635 "org.freedesktop.machine1.Manager",
639 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
641 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
649 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
650 arg_kill_who
= "leader";
651 arg_signal
= SIGINT
; /* sysvinit + systemd */
653 return kill_machine(argc
, argv
, userdata
);
656 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
657 arg_kill_who
= "leader";
658 arg_signal
= SIGRTMIN
+4; /* only systemd */
660 return kill_machine(argc
, argv
, userdata
);
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
;
670 for (i
= 1; i
< argc
; i
++) {
673 r
= sd_bus_call_method(
675 "org.freedesktop.machine1",
676 "/org/freedesktop/machine1",
677 "org.freedesktop.machine1.Manager",
683 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
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
;
702 r
= sd_bus_call_method(
704 "org.freedesktop.machine1",
705 "/org/freedesktop/machine1",
706 "org.freedesktop.machine1.Manager",
712 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
716 r
= sd_bus_message_read(reply
, "o", &object
);
718 return bus_log_parse_error(r
);
720 r
= sd_bus_get_property(
722 "org.freedesktop.machine1",
724 "org.freedesktop.machine1.Machine",
730 return log_error_errno(r
, "Failed to retrieve PID of leader: %m");
732 r
= sd_bus_message_read(reply2
, "u", &leader
);
734 return bus_log_parse_error(r
);
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
;
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
);
756 if (!path_is_absolute(container_path
)) {
757 log_error("Container path not absolute.");
761 t
= strdup(host_path
);
762 host_basename
= basename(t
);
763 host_dirname
= dirname(host_path
);
765 t
= strdup(container_path
);
766 container_basename
= basename(t
);
767 container_dirname
= dirname(container_path
);
769 r
= machine_get_leader(bus
, argv
[1], &leader
);
773 hostfd
= open(host_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
775 return log_error_errno(errno
, "Failed to open source directory: %m");
779 return log_error_errno(errno
, "Failed to fork(): %m");
786 q
= procfs_file_alloca(leader
, "ns/mnt");
787 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
789 log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
793 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
794 log_error_errno(errno
, "Failed to join namespace of leader: %m");
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");
805 r
= copy_tree_at(containerfd
, container_basename
, hostfd
, host_basename
, true);
807 r
= copy_tree_at(hostfd
, host_basename
, containerfd
, container_basename
, true);
809 log_error_errno(errno
, "Failed to copy tree: %m");
816 r
= wait_for_terminate(child
, &si
);
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.");
823 if (si
.si_status
!= EXIT_SUCCESS
)
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
;
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;
842 /* One day, when bind mounting /proc/self/fd/n works across
843 * namespace boundaries we should rework this logic to make
846 dest
= argv
[3] ?: argv
[2];
847 if (!path_is_absolute(dest
)) {
848 log_error("Destination path not absolute.");
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.");
858 r
= machine_get_leader(bus
, argv
[1], &leader
);
862 /* Our goal is to install a new bind mount into the container,
863 possibly read-only. This is irritatingly complex
864 unfortunately, currently.
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
871 if (!mkdtemp(mount_slave
))
872 return log_error_errno(errno
, "Failed to create playground: %m");
874 mount_slave_created
= true;
876 if (mount(mount_slave
, mount_slave
, NULL
, MS_BIND
, NULL
) < 0) {
877 r
= log_error_errno(errno
, "Failed to make bind mount: %m");
881 mount_slave_mounted
= true;
883 if (mount(NULL
, mount_slave
, NULL
, MS_SLAVE
, NULL
) < 0) {
884 r
= log_error_errno(errno
, "Failed to remount slave: %m");
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");
896 mount_tmp_created
= true;
898 if (mount(argv
[2], mount_tmp
, NULL
, MS_BIND
, NULL
) < 0) {
899 r
= log_error_errno(errno
, "Failed to overmount: %m");
903 mount_tmp_mounted
= true;
905 /* Third, we remount the new bind mount read-only if requested. */
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");
912 /* Fourth, we move the new bind mount into the propagation
913 * directory. This way it will appear there read-only
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");
922 mount_outside_created
= true;
924 if (mount(mount_tmp
, mount_outside
, NULL
, MS_MOVE
, NULL
) < 0) {
925 r
= log_error_errno(errno
, "Failed to move: %m");
929 mount_outside_mounted
= true;
930 mount_tmp_mounted
= false;
932 (void) rmdir(mount_tmp
);
933 mount_tmp_created
= false;
935 (void) umount(mount_slave
);
936 mount_slave_mounted
= false;
938 (void) rmdir(mount_slave
);
939 mount_slave_created
= false;
943 r
= log_error_errno(errno
, "Failed to fork(): %m");
948 const char *mount_inside
;
952 q
= procfs_file_alloca(leader
, "ns/mnt");
953 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
955 log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
959 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
960 log_error_errno(errno
, "Failed to join namespace of leader: %m");
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");
977 r
= wait_for_terminate(child
, &si
);
979 log_error_errno(r
, "Failed to wait for client: %m");
982 if (si
.si_code
!= CLD_EXITED
) {
983 log_error("Client died abnormally.");
987 if (si
.si_status
!= EXIT_SUCCESS
) {
995 if (mount_outside_mounted
)
996 umount(mount_outside
);
997 if (mount_outside_created
)
998 rmdir(mount_outside
);
1000 if (mount_tmp_mounted
)
1002 if (mount_tmp_created
)
1005 if (mount_slave_mounted
)
1006 umount(mount_slave
);
1007 if (mount_slave_created
)
1008 umount(mount_slave
);
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
;
1026 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1027 arg_transport
!= BUS_TRANSPORT_CONTAINER
) {
1028 log_error("Login only supported on local machines.");
1032 r
= sd_event_default(&event
);
1034 return log_error_errno(r
, "Failed to get event loop: %m");
1036 r
= sd_bus_attach_event(bus
, event
, 0);
1038 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1040 r
= sd_bus_call_method(bus
,
1041 "org.freedesktop.machine1",
1042 "/org/freedesktop/machine1",
1043 "org.freedesktop.machine1.Manager",
1049 log_error("Failed to get machine PTY: %s", bus_error_message(&error
, -r
));
1053 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1055 return bus_log_parse_error(r
);
1057 assert_se(sigemptyset(&mask
) == 0);
1058 sigset_add_many(&mask
, SIGWINCH
, SIGTERM
, SIGINT
, -1);
1059 assert_se(sigprocmask(SIG_BLOCK
, &mask
, NULL
) == 0);
1061 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", argv
[1]);
1063 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1064 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1066 r
= pty_forward_new(event
, master
, true, &forward
);
1068 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1070 r
= sd_event_loop(event
);
1072 return log_error_errno(r
, "Failed to run event loop: %m");
1074 pty_forward_last_char(forward
, &last_char
);
1076 forward
= pty_forward_free(forward
);
1078 if (last_char
!= '\n')
1079 fputc('\n', stdout
);
1081 log_info("Connection to container %s terminated.", argv
[1]);
1083 sd_event_get_exit_code(event
, &ret
);
1087 static int help(int argc
, char *argv
[], void *userdata
) {
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"
1118 " list-images Show available images\n",
1119 program_invocation_short_name
);
1124 static int parse_argv(int argc
, char *argv
[]) {
1127 ARG_VERSION
= 0x100,
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
},
1157 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:", options
, NULL
)) >= 0)
1162 return help(0, NULL
, NULL
);
1165 puts(PACKAGE_STRING
);
1166 puts(SYSTEMD_FEATURES
);
1170 r
= strv_extend(&arg_property
, optarg
);
1174 /* If the user asked for a particular
1175 * property, show it to him, even if it is
1189 arg_no_pager
= true;
1197 arg_kill_who
= optarg
;
1201 arg_signal
= signal_from_string_try_harder(optarg
);
1202 if (arg_signal
< 0) {
1203 log_error("Failed to parse signal string %s.", optarg
);
1209 arg_transport
= BUS_TRANSPORT_REMOTE
;
1214 arg_transport
= BUS_TRANSPORT_CONTAINER
;
1219 arg_read_only
= true;
1230 assert_not_reached("Unhandled option");
1236 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
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
},
1255 return dispatch_verb(argc
, argv
, verbs
, bus
);
1258 int main(int argc
, char*argv
[]) {
1259 _cleanup_bus_close_unref_ sd_bus
*bus
= NULL
;
1262 setlocale(LC_ALL
, "");
1263 log_parse_environment();
1266 r
= parse_argv(argc
, argv
);
1270 r
= bus_open_transport(arg_transport
, arg_host
, false, &bus
);
1272 log_error_errno(r
, "Failed to create bus connection: %m");
1276 r
= machinectl_main(argc
, argv
, bus
);
1281 strv_free(arg_property
);
1283 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;