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>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
32 #include <sys/mount.h>
39 #include "spawn-polkit-agent.h"
41 #include "bus-error.h"
44 #include "unit-name.h"
45 #include "cgroup-show.h"
46 #include "logs-show.h"
47 #include "cgroup-util.h"
49 #include "event-util.h"
50 #include "path-util.h"
54 #include "import-util.h"
55 #include "process-util.h"
56 #include "terminal-util.h"
57 #include "signal-util.h"
59 #include "hostname-util.h"
61 static char **arg_property
= NULL
;
62 static bool arg_all
= false;
63 static bool arg_full
= false;
64 static bool arg_no_pager
= false;
65 static bool arg_legend
= true;
66 static const char *arg_kill_who
= NULL
;
67 static int arg_signal
= SIGTERM
;
68 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
69 static char *arg_host
= NULL
;
70 static bool arg_read_only
= false;
71 static bool arg_mkdir
= false;
72 static bool arg_quiet
= false;
73 static bool arg_ask_password
= true;
74 static unsigned arg_lines
= 10;
75 static OutputMode arg_output
= OUTPUT_SHORT
;
76 static bool arg_force
= false;
77 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
78 static const char* arg_dkr_index_url
= NULL
;
79 static const char* arg_format
= NULL
;
80 static const char *arg_uid
= NULL
;
81 static char **arg_setenv
= NULL
;
83 static void pager_open_if_enabled(void) {
91 static void polkit_agent_open_if_enabled(void) {
93 /* Open the polkit agent as a child process if necessary */
95 if (!arg_ask_password
)
98 if (arg_transport
!= BUS_TRANSPORT_LOCAL
)
104 static OutputFlags
get_output_flags(void) {
106 arg_all
* OUTPUT_SHOW_ALL
|
107 arg_full
* OUTPUT_FULL_WIDTH
|
108 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH
|
109 on_tty() * OUTPUT_COLOR
|
110 !arg_quiet
* OUTPUT_WARN_CUTOFF
;
113 typedef struct MachineInfo
{
119 static int compare_machine_info(const void *a
, const void *b
) {
120 const MachineInfo
*x
= a
, *y
= b
;
122 return strcmp(x
->name
, y
->name
);
125 static int list_machines(int argc
, char *argv
[], void *userdata
) {
127 size_t max_name
= strlen("MACHINE"), max_class
= strlen("CLASS"), max_service
= strlen("SERVICE");
128 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
129 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
130 _cleanup_free_ MachineInfo
*machines
= NULL
;
131 const char *name
, *class, *service
, *object
;
132 size_t n_machines
= 0, n_allocated
= 0, j
;
133 sd_bus
*bus
= userdata
;
138 pager_open_if_enabled();
140 r
= sd_bus_call_method(
142 "org.freedesktop.machine1",
143 "/org/freedesktop/machine1",
144 "org.freedesktop.machine1.Manager",
150 log_error("Could not get machines: %s", bus_error_message(&error
, -r
));
154 r
= sd_bus_message_enter_container(reply
, 'a', "(ssso)");
156 return bus_log_parse_error(r
);
158 while ((r
= sd_bus_message_read(reply
, "(ssso)", &name
, &class, &service
, &object
)) > 0) {
161 if (!GREEDY_REALLOC(machines
, n_allocated
, n_machines
+ 1))
164 machines
[n_machines
].name
= name
;
165 machines
[n_machines
].class = class;
166 machines
[n_machines
].service
= service
;
183 return bus_log_parse_error(r
);
185 r
= sd_bus_message_exit_container(reply
);
187 return bus_log_parse_error(r
);
189 qsort_safe(machines
, n_machines
, sizeof(MachineInfo
), compare_machine_info
);
192 printf("%-*s %-*s %-*s\n",
193 (int) max_name
, "MACHINE",
194 (int) max_class
, "CLASS",
195 (int) max_service
, "SERVICE");
197 for (j
= 0; j
< n_machines
; j
++)
198 printf("%-*s %-*s %-*s\n",
199 (int) max_name
, machines
[j
].name
,
200 (int) max_class
, machines
[j
].class,
201 (int) max_service
, machines
[j
].service
);
204 printf("\n%zu machines listed.\n", n_machines
);
209 typedef struct ImageInfo
{
218 static int compare_image_info(const void *a
, const void *b
) {
219 const ImageInfo
*x
= a
, *y
= b
;
221 return strcmp(x
->name
, y
->name
);
224 static int list_images(int argc
, char *argv
[], void *userdata
) {
226 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
227 size_t max_name
= strlen("NAME"), max_type
= strlen("TYPE"), max_size
= strlen("USAGE"), max_crtime
= strlen("CREATED"), max_mtime
= strlen("MODIFIED");
228 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
229 _cleanup_free_ ImageInfo
*images
= NULL
;
230 size_t n_images
= 0, n_allocated
= 0, j
;
231 const char *name
, *type
, *object
;
232 sd_bus
*bus
= userdata
;
233 uint64_t crtime
, mtime
, size
;
238 pager_open_if_enabled();
240 r
= sd_bus_call_method(
242 "org.freedesktop.machine1",
243 "/org/freedesktop/machine1",
244 "org.freedesktop.machine1.Manager",
250 log_error("Could not get images: %s", bus_error_message(&error
, -r
));
254 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssbttto)");
256 return bus_log_parse_error(r
);
258 while ((r
= sd_bus_message_read(reply
, "(ssbttto)", &name
, &type
, &read_only
, &crtime
, &mtime
, &size
, &object
)) > 0) {
259 char buf
[MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_BYTES_MAX
)];
262 if (name
[0] == '.' && !arg_all
)
265 if (!GREEDY_REALLOC(images
, n_allocated
, n_images
+ 1))
268 images
[n_images
].name
= name
;
269 images
[n_images
].type
= type
;
270 images
[n_images
].read_only
= read_only
;
271 images
[n_images
].crtime
= crtime
;
272 images
[n_images
].mtime
= mtime
;
273 images
[n_images
].size
= size
;
284 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), crtime
)));
290 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), mtime
)));
295 if (size
!= (uint64_t) -1) {
296 l
= strlen(strna(format_bytes(buf
, sizeof(buf
), size
)));
304 return bus_log_parse_error(r
);
306 r
= sd_bus_message_exit_container(reply
);
308 return bus_log_parse_error(r
);
310 qsort_safe(images
, n_images
, sizeof(ImageInfo
), compare_image_info
);
313 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
314 (int) max_name
, "NAME",
315 (int) max_type
, "TYPE",
317 (int) max_size
, "USAGE",
318 (int) max_crtime
, "CREATED",
319 (int) max_mtime
, "MODIFIED");
321 for (j
= 0; j
< n_images
; j
++) {
322 char crtime_buf
[FORMAT_TIMESTAMP_MAX
], mtime_buf
[FORMAT_TIMESTAMP_MAX
], size_buf
[FORMAT_BYTES_MAX
];
324 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
325 (int) max_name
, images
[j
].name
,
326 (int) max_type
, images
[j
].type
,
327 images
[j
].read_only
? ansi_highlight_red() : "", yes_no(images
[j
].read_only
), images
[j
].read_only
? ansi_highlight_off() : "",
328 (int) max_size
, strna(format_bytes(size_buf
, sizeof(size_buf
), images
[j
].size
)),
329 (int) max_crtime
, strna(format_timestamp(crtime_buf
, sizeof(crtime_buf
), images
[j
].crtime
)),
330 (int) max_mtime
, strna(format_timestamp(mtime_buf
, sizeof(mtime_buf
), images
[j
].mtime
)));
334 printf("\n%zu images listed.\n", n_images
);
339 static int show_unit_cgroup(sd_bus
*bus
, const char *unit
, pid_t leader
) {
340 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
341 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
342 _cleanup_free_
char *path
= NULL
;
350 if (arg_transport
== BUS_TRANSPORT_REMOTE
)
353 path
= unit_dbus_path_from_name(unit
);
357 r
= sd_bus_get_property(
359 "org.freedesktop.systemd1",
361 endswith(unit
, ".scope") ? "org.freedesktop.systemd1.Scope" :
362 endswith(unit
, ".slice") ? "org.freedesktop.systemd1.Slice" : "org.freedesktop.systemd1.Service",
368 log_error("Failed to query ControlGroup: %s", bus_error_message(&error
, -r
));
372 r
= sd_bus_message_read(reply
, "s", &cgroup
);
374 return bus_log_parse_error(r
);
376 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, false) != 0 && leader
<= 0)
385 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, "\t\t ", c
, false, &leader
, leader
> 0, get_output_flags());
389 static int print_addresses(sd_bus
*bus
, const char *name
, int ifi
, const char *prefix
, const char *prefix2
) {
390 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
398 r
= sd_bus_call_method(bus
,
399 "org.freedesktop.machine1",
400 "/org/freedesktop/machine1",
401 "org.freedesktop.machine1.Manager",
402 "GetMachineAddresses",
409 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
411 return bus_log_parse_error(r
);
413 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
417 char buffer
[MAX(INET6_ADDRSTRLEN
, INET_ADDRSTRLEN
)];
419 r
= sd_bus_message_read(reply
, "i", &family
);
421 return bus_log_parse_error(r
);
423 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
425 return bus_log_parse_error(r
);
427 fputs(prefix
, stdout
);
428 fputs(inet_ntop(family
, a
, buffer
, sizeof(buffer
)), stdout
);
429 if (family
== AF_INET6
&& ifi
> 0)
433 r
= sd_bus_message_exit_container(reply
);
435 return bus_log_parse_error(r
);
437 if (prefix
!= prefix2
)
441 return bus_log_parse_error(r
);
443 r
= sd_bus_message_exit_container(reply
);
445 return bus_log_parse_error(r
);
450 static int print_os_release(sd_bus
*bus
, const char *name
, const char *prefix
) {
451 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
452 const char *k
, *v
, *pretty
= NULL
;
459 r
= sd_bus_call_method(bus
,
460 "org.freedesktop.machine1",
461 "/org/freedesktop/machine1",
462 "org.freedesktop.machine1.Manager",
463 "GetMachineOSRelease",
470 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
472 return bus_log_parse_error(r
);
474 while ((r
= sd_bus_message_read(reply
, "{ss}", &k
, &v
)) > 0) {
475 if (streq(k
, "PRETTY_NAME"))
480 return bus_log_parse_error(r
);
482 r
= sd_bus_message_exit_container(reply
);
484 return bus_log_parse_error(r
);
487 printf("%s%s\n", prefix
, pretty
);
492 typedef struct MachineStatusInfo
{
498 char *root_directory
;
500 struct dual_timestamp timestamp
;
505 static void machine_status_info_clear(MachineStatusInfo
*info
) {
511 free(info
->root_directory
);
517 static void print_machine_status_info(sd_bus
*bus
, MachineStatusInfo
*i
) {
518 char since1
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
519 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
525 fputs(strna(i
->name
), stdout
);
527 if (!sd_id128_equal(i
->id
, SD_ID128_NULL
))
528 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
532 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
.realtime
);
533 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
.realtime
);
536 printf("\t Since: %s; %s\n", s2
, s1
);
538 printf("\t Since: %s\n", s2
);
541 _cleanup_free_
char *t
= NULL
;
543 printf("\t Leader: %u", (unsigned) i
->leader
);
545 get_process_comm(i
->leader
, &t
);
553 printf("\t Service: %s", i
->service
);
556 printf("; class %s", i
->class);
560 printf("\t Class: %s\n", i
->class);
562 if (i
->root_directory
)
563 printf("\t Root: %s\n", i
->root_directory
);
565 if (i
->n_netif
> 0) {
568 fputs("\t Iface:", stdout
);
570 for (c
= 0; c
< i
->n_netif
; c
++) {
571 char name
[IF_NAMESIZE
+1] = "";
573 if (if_indextoname(i
->netif
[c
], name
)) {
582 printf(" %i", i
->netif
[c
]);
588 print_addresses(bus
, i
->name
, ifi
,
592 print_os_release(bus
, i
->name
, "\t OS: ");
595 printf("\t Unit: %s\n", i
->unit
);
596 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
598 if (arg_transport
== BUS_TRANSPORT_LOCAL
) {
600 show_journal_by_unit(
605 i
->timestamp
.monotonic
,
608 get_output_flags() | OUTPUT_BEGIN_NEWLINE
,
609 SD_JOURNAL_LOCAL_ONLY
,
616 static int map_netif(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
617 MachineStatusInfo
*i
= userdata
;
622 assert_cc(sizeof(int32_t) == sizeof(int));
623 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
629 i
->n_netif
= l
/ sizeof(int32_t);
630 i
->netif
= memdup(v
, l
);
637 static int show_machine_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
639 static const struct bus_properties_map map
[] = {
640 { "Name", "s", NULL
, offsetof(MachineStatusInfo
, name
) },
641 { "Class", "s", NULL
, offsetof(MachineStatusInfo
, class) },
642 { "Service", "s", NULL
, offsetof(MachineStatusInfo
, service
) },
643 { "Unit", "s", NULL
, offsetof(MachineStatusInfo
, unit
) },
644 { "RootDirectory", "s", NULL
, offsetof(MachineStatusInfo
, root_directory
) },
645 { "Leader", "u", NULL
, offsetof(MachineStatusInfo
, leader
) },
646 { "Timestamp", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.realtime
) },
647 { "TimestampMonotonic", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.monotonic
) },
648 { "Id", "ay", bus_map_id128
, offsetof(MachineStatusInfo
, id
) },
649 { "NetworkInterfaces", "ai", map_netif
, 0 },
653 _cleanup_(machine_status_info_clear
) MachineStatusInfo info
= {};
661 r
= bus_map_all_properties(bus
,
662 "org.freedesktop.machine1",
667 return log_error_errno(r
, "Could not get properties: %m");
673 print_machine_status_info(bus
, &info
);
678 static int show_machine_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
690 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
692 log_error_errno(r
, "Could not get properties: %m");
697 static int show_machine(int argc
, char *argv
[], void *userdata
) {
699 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
700 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
701 bool properties
, new_line
= false;
702 sd_bus
*bus
= userdata
;
707 properties
= !strstr(argv
[0], "status");
709 pager_open_if_enabled();
711 if (properties
&& argc
<= 1) {
713 /* If no argument is specified, inspect the manager
715 r
= show_machine_properties(bus
, "/org/freedesktop/machine1", &new_line
);
720 for (i
= 1; i
< argc
; i
++) {
721 const char *path
= NULL
;
723 r
= sd_bus_call_method(
725 "org.freedesktop.machine1",
726 "/org/freedesktop/machine1",
727 "org.freedesktop.machine1.Manager",
733 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
737 r
= sd_bus_message_read(reply
, "o", &path
);
739 return bus_log_parse_error(r
);
742 r
= show_machine_properties(bus
, path
, &new_line
);
744 r
= show_machine_info(argv
[0], bus
, path
, &new_line
);
750 typedef struct ImageStatusInfo
{
759 uint64_t usage_exclusive
;
760 uint64_t limit_exclusive
;
763 static void image_status_info_clear(ImageStatusInfo
*info
) {
772 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
773 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
774 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
775 char bs
[FORMAT_BYTES_MAX
], *s3
;
776 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
782 fputs(i
->name
, stdout
);
787 printf("\t Type: %s\n", i
->type
);
790 printf("\t Path: %s\n", i
->path
);
792 printf("\t RO: %s%s%s\n",
793 i
->read_only
? ansi_highlight_red() : "",
794 i
->read_only
? "read-only" : "writable",
795 i
->read_only
? ansi_highlight_off() : "");
797 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
798 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
800 printf("\t Created: %s; %s\n", s2
, s1
);
802 printf("\t Created: %s\n", s2
);
804 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
805 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
807 printf("\tModified: %s; %s\n", s2
, s1
);
809 printf("\tModified: %s\n", s2
);
811 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
812 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
814 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
816 printf("\t Usage: %s\n", s3
);
818 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
819 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
821 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
823 printf("\t Limit: %s\n", s3
);
826 static int show_image_info(sd_bus
*bus
, const char *path
, bool *new_line
) {
828 static const struct bus_properties_map map
[] = {
829 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
830 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
831 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
832 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
833 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
834 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
835 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
836 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
837 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
838 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
842 _cleanup_(image_status_info_clear
) ImageStatusInfo info
= {};
849 r
= bus_map_all_properties(bus
,
850 "org.freedesktop.machine1",
855 return log_error_errno(r
, "Could not get properties: %m");
861 print_image_status_info(bus
, &info
);
866 typedef struct PoolStatusInfo
{
872 static void pool_status_info_clear(PoolStatusInfo
*info
) {
881 static void print_pool_status_info(sd_bus
*bus
, PoolStatusInfo
*i
) {
882 char bs
[FORMAT_BYTES_MAX
], *s
;
885 printf("\t Path: %s\n", i
->path
);
887 s
= format_bytes(bs
, sizeof(bs
), i
->usage
);
889 printf("\t Usage: %s\n", s
);
891 s
= format_bytes(bs
, sizeof(bs
), i
->limit
);
893 printf("\t Limit: %s\n", s
);
896 static int show_pool_info(sd_bus
*bus
) {
898 static const struct bus_properties_map map
[] = {
899 { "PoolPath", "s", NULL
, offsetof(PoolStatusInfo
, path
) },
900 { "PoolUsage", "t", NULL
, offsetof(PoolStatusInfo
, usage
) },
901 { "PoolLimit", "t", NULL
, offsetof(PoolStatusInfo
, limit
) },
905 _cleanup_(pool_status_info_clear
) PoolStatusInfo info
= {
906 .usage
= (uint64_t) -1,
907 .limit
= (uint64_t) -1,
913 r
= bus_map_all_properties(bus
,
914 "org.freedesktop.machine1",
915 "/org/freedesktop/machine1",
919 return log_error_errno(r
, "Could not get properties: %m");
921 print_pool_status_info(bus
, &info
);
927 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
939 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
941 log_error_errno(r
, "Could not get properties: %m");
946 static int show_image(int argc
, char *argv
[], void *userdata
) {
948 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
949 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
950 bool properties
, new_line
= false;
951 sd_bus
*bus
= userdata
;
956 properties
= !strstr(argv
[0], "status");
958 pager_open_if_enabled();
962 /* If no argument is specified, inspect the manager
966 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
968 r
= show_pool_info(bus
);
973 for (i
= 1; i
< argc
; i
++) {
974 const char *path
= NULL
;
976 r
= sd_bus_call_method(
978 "org.freedesktop.machine1",
979 "/org/freedesktop/machine1",
980 "org.freedesktop.machine1.Manager",
986 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
990 r
= sd_bus_message_read(reply
, "o", &path
);
992 return bus_log_parse_error(r
);
995 r
= show_image_properties(bus
, path
, &new_line
);
997 r
= show_image_info(bus
, path
, &new_line
);
1003 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
1004 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1005 sd_bus
*bus
= userdata
;
1010 polkit_agent_open_if_enabled();
1013 arg_kill_who
= "all";
1015 for (i
= 1; i
< argc
; i
++) {
1016 r
= sd_bus_call_method(
1018 "org.freedesktop.machine1",
1019 "/org/freedesktop/machine1",
1020 "org.freedesktop.machine1.Manager",
1024 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
1026 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
1034 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
1035 arg_kill_who
= "leader";
1036 arg_signal
= SIGINT
; /* sysvinit + systemd */
1038 return kill_machine(argc
, argv
, userdata
);
1041 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
1042 arg_kill_who
= "leader";
1043 arg_signal
= SIGRTMIN
+4; /* only systemd */
1045 return kill_machine(argc
, argv
, userdata
);
1048 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
1049 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1050 sd_bus
*bus
= userdata
;
1055 polkit_agent_open_if_enabled();
1057 for (i
= 1; i
< argc
; i
++) {
1058 r
= sd_bus_call_method(
1060 "org.freedesktop.machine1",
1061 "/org/freedesktop/machine1",
1062 "org.freedesktop.machine1.Manager",
1068 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1076 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1077 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1078 _cleanup_free_
char *abs_host_path
= NULL
;
1079 char *dest
, *host_path
, *container_path
;
1080 sd_bus
*bus
= userdata
;
1086 polkit_agent_open_if_enabled();
1088 copy_from
= streq(argv
[0], "copy-from");
1089 dest
= argv
[3] ?: argv
[2];
1090 host_path
= copy_from
? dest
: argv
[2];
1091 container_path
= copy_from
? argv
[2] : dest
;
1093 if (!path_is_absolute(host_path
)) {
1094 abs_host_path
= path_make_absolute_cwd(host_path
);
1097 host_path
= abs_host_path
;
1100 r
= sd_bus_call_method(
1102 "org.freedesktop.machine1",
1103 "/org/freedesktop/machine1",
1104 "org.freedesktop.machine1.Manager",
1105 copy_from
? "CopyFromMachine" : "CopyToMachine",
1110 copy_from
? container_path
: host_path
,
1111 copy_from
? host_path
: container_path
);
1113 log_error("Failed to copy: %s", bus_error_message(&error
, -r
));
1120 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1121 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1122 sd_bus
*bus
= userdata
;
1127 polkit_agent_open_if_enabled();
1129 r
= sd_bus_call_method(
1131 "org.freedesktop.machine1",
1132 "/org/freedesktop/machine1",
1133 "org.freedesktop.machine1.Manager",
1144 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1151 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1152 PTYForward
** forward
= (PTYForward
**) userdata
;
1159 /* If the forwarder is already initialized, tell it to
1160 * exit on the next vhangup(), so that we still flush
1161 * out what might be queued and exit then. */
1163 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1167 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1170 /* On error, or when the forwarder is not initialized yet, quit immediately */
1171 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1175 static int process_forward(sd_event
*event
, PTYForward
**forward
, int master
, bool ignore_vhangup
, const char *name
) {
1181 assert(master
>= 0);
1184 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGWINCH
, SIGTERM
, SIGINT
, -1) >= 0);
1186 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name
);
1188 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1189 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1191 r
= pty_forward_new(event
, master
, ignore_vhangup
, false, forward
);
1193 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1195 r
= sd_event_loop(event
);
1197 return log_error_errno(r
, "Failed to run event loop: %m");
1199 pty_forward_get_last_char(*forward
, &last_char
);
1203 pty_forward_get_ignore_vhangup(*forward
) == 0;
1205 *forward
= pty_forward_free(*forward
);
1207 if (last_char
!= '\n')
1208 fputc('\n', stdout
);
1211 log_info("Machine %s terminated.", name
);
1213 log_info("Connection to machine %s terminated.", name
);
1215 sd_event_get_exit_code(event
, &ret
);
1219 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1220 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
, *m
= NULL
;
1221 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1222 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1223 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1224 _cleanup_event_unref_ sd_event
*event
= NULL
;
1226 sd_bus
*bus
= userdata
;
1227 const char *pty
, *match
;
1231 if (!strv_isempty(arg_setenv
) || arg_uid
) {
1232 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1236 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1237 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1238 log_error("Login only supported on local machines.");
1242 polkit_agent_open_if_enabled();
1244 r
= sd_event_default(&event
);
1246 return log_error_errno(r
, "Failed to get event loop: %m");
1248 r
= sd_bus_attach_event(bus
, event
, 0);
1250 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1252 match
= strjoina("type='signal',"
1253 "sender='org.freedesktop.machine1',"
1254 "path='/org/freedesktop/machine1',",
1255 "interface='org.freedesktop.machine1.Manager',"
1256 "member='MachineRemoved',"
1261 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1263 return log_error_errno(r
, "Failed to add machine removal match: %m");
1265 r
= sd_bus_call_method(
1267 "org.freedesktop.machine1",
1268 "/org/freedesktop/machine1",
1269 "org.freedesktop.machine1.Manager",
1275 log_error("Failed to get login PTY: %s", bus_error_message(&error
, -r
));
1279 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1281 return bus_log_parse_error(r
);
1283 return process_forward(event
, &forward
, master
, true, argv
[1]);
1286 static int shell_machine(int argc
, char *argv
[], void *userdata
) {
1287 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
, *m
= NULL
;
1288 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1289 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1290 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1291 _cleanup_event_unref_ sd_event
*event
= NULL
;
1293 sd_bus
*bus
= userdata
;
1294 const char *pty
, *match
;
1298 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1299 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1300 log_error("Shell only supported on local machines.");
1304 polkit_agent_open_if_enabled();
1306 r
= sd_event_default(&event
);
1308 return log_error_errno(r
, "Failed to get event loop: %m");
1310 r
= sd_bus_attach_event(bus
, event
, 0);
1312 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1314 match
= strjoina("type='signal',"
1315 "sender='org.freedesktop.machine1',"
1316 "path='/org/freedesktop/machine1',",
1317 "interface='org.freedesktop.machine1.Manager',"
1318 "member='MachineRemoved',"
1323 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1325 return log_error_errno(r
, "Failed to add machine removal match: %m");
1327 r
= sd_bus_message_new_method_call(
1330 "org.freedesktop.machine1",
1331 "/org/freedesktop/machine1",
1332 "org.freedesktop.machine1.Manager",
1333 "OpenMachineShell");
1335 return bus_log_create_error(r
);
1337 r
= sd_bus_message_append(m
, "sss", argv
[1], arg_uid
, argv
[2]);
1339 return bus_log_create_error(r
);
1341 r
= sd_bus_message_append_strv(m
, strv_length(argv
+ 2) <= 1 ? NULL
: argv
+ 2);
1343 return bus_log_create_error(r
);
1345 r
= sd_bus_message_append_strv(m
, arg_setenv
);
1347 return bus_log_create_error(r
);
1349 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1351 log_error("Failed to get shell PTY: %s", bus_error_message(&error
, -r
));
1355 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1357 return bus_log_parse_error(r
);
1359 return process_forward(event
, &forward
, master
, false, argv
[1]);
1362 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1363 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1364 sd_bus
*bus
= userdata
;
1369 polkit_agent_open_if_enabled();
1371 for (i
= 1; i
< argc
; i
++) {
1372 r
= sd_bus_call_method(
1374 "org.freedesktop.machine1",
1375 "/org/freedesktop/machine1",
1376 "org.freedesktop.machine1.Manager",
1382 log_error("Could not remove image: %s", bus_error_message(&error
, -r
));
1390 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1391 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1392 sd_bus
*bus
= userdata
;
1395 polkit_agent_open_if_enabled();
1397 r
= sd_bus_call_method(
1399 "org.freedesktop.machine1",
1400 "/org/freedesktop/machine1",
1401 "org.freedesktop.machine1.Manager",
1405 "ss", argv
[1], argv
[2]);
1407 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1414 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1415 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1416 sd_bus
*bus
= userdata
;
1419 polkit_agent_open_if_enabled();
1421 r
= sd_bus_call_method(
1423 "org.freedesktop.machine1",
1424 "/org/freedesktop/machine1",
1425 "org.freedesktop.machine1.Manager",
1429 "ssb", argv
[1], argv
[2], arg_read_only
);
1431 log_error("Could not clone image: %s", bus_error_message(&error
, -r
));
1438 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1439 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1440 sd_bus
*bus
= userdata
;
1444 b
= parse_boolean(argv
[2]);
1446 log_error("Failed to parse boolean argument: %s", argv
[2]);
1451 polkit_agent_open_if_enabled();
1453 r
= sd_bus_call_method(
1455 "org.freedesktop.machine1",
1456 "/org/freedesktop/machine1",
1457 "org.freedesktop.machine1.Manager",
1458 "MarkImageReadOnly",
1463 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1470 static int make_service_name(const char *name
, char **ret
) {
1471 _cleanup_free_
char *e
= NULL
;
1477 if (!machine_name_is_valid(name
)) {
1478 log_error("Invalid machine name %s.", name
);
1482 e
= unit_name_escape(name
);
1486 r
= unit_name_build("systemd-nspawn", e
, ".service", ret
);
1488 return log_error_errno(r
, "Failed to build unit name: %m");
1493 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1494 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1495 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1496 sd_bus
*bus
= userdata
;
1501 polkit_agent_open_if_enabled();
1503 r
= bus_wait_for_jobs_new(bus
, &w
);
1507 for (i
= 1; i
< argc
; i
++) {
1508 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1509 _cleanup_free_
char *unit
= NULL
;
1512 r
= make_service_name(argv
[i
], &unit
);
1516 r
= sd_bus_call_method(
1518 "org.freedesktop.systemd1",
1519 "/org/freedesktop/systemd1",
1520 "org.freedesktop.systemd1.Manager",
1524 "ss", unit
, "fail");
1526 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1530 r
= sd_bus_message_read(reply
, "o", &object
);
1532 return bus_log_parse_error(r
);
1534 r
= bus_wait_for_jobs_add(w
, object
);
1539 r
= bus_wait_for_jobs(w
, arg_quiet
);
1546 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1547 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1548 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1549 int carries_install_info
= 0;
1550 const char *method
= NULL
;
1551 sd_bus
*bus
= userdata
;
1556 polkit_agent_open_if_enabled();
1558 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1560 r
= sd_bus_message_new_method_call(
1563 "org.freedesktop.systemd1",
1564 "/org/freedesktop/systemd1",
1565 "org.freedesktop.systemd1.Manager",
1568 return bus_log_create_error(r
);
1570 r
= sd_bus_message_open_container(m
, 'a', "s");
1572 return bus_log_create_error(r
);
1574 for (i
= 1; i
< argc
; i
++) {
1575 _cleanup_free_
char *unit
= NULL
;
1577 r
= make_service_name(argv
[i
], &unit
);
1581 r
= sd_bus_message_append(m
, "s", unit
);
1583 return bus_log_create_error(r
);
1586 r
= sd_bus_message_close_container(m
);
1588 return bus_log_create_error(r
);
1590 if (streq(argv
[0], "enable"))
1591 r
= sd_bus_message_append(m
, "bb", false, false);
1593 r
= sd_bus_message_append(m
, "b", false);
1595 return bus_log_create_error(r
);
1597 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1599 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1603 if (streq(argv
[0], "enable")) {
1604 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1606 return bus_log_parse_error(r
);
1609 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, NULL
, NULL
);
1613 r
= sd_bus_call_method(
1615 "org.freedesktop.systemd1",
1616 "/org/freedesktop/systemd1",
1617 "org.freedesktop.systemd1.Manager",
1623 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1630 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1631 const char **our_path
= userdata
, *line
;
1638 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1640 bus_log_parse_error(r
);
1644 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
1647 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1650 log_full(priority
, "%s", line
);
1654 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1655 const char **our_path
= userdata
, *path
, *result
;
1662 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1664 bus_log_parse_error(r
);
1668 if (!streq_ptr(*our_path
, path
))
1671 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
1675 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
1680 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
1682 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
1686 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1687 _cleanup_bus_slot_unref_ sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1688 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1689 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1690 _cleanup_event_unref_ sd_event
* event
= NULL
;
1691 const char *path
= NULL
;
1698 polkit_agent_open_if_enabled();
1700 r
= sd_event_default(&event
);
1702 return log_error_errno(r
, "Failed to get event loop: %m");
1704 r
= sd_bus_attach_event(bus
, event
, 0);
1706 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1708 r
= sd_bus_add_match(
1712 "sender='org.freedesktop.import1',"
1713 "interface='org.freedesktop.import1.Manager',"
1714 "member='TransferRemoved',"
1715 "path='/org/freedesktop/import1'",
1716 match_transfer_removed
, &path
);
1718 return log_error_errno(r
, "Failed to install match: %m");
1720 r
= sd_bus_add_match(
1724 "sender='org.freedesktop.import1',"
1725 "interface='org.freedesktop.import1.Transfer',"
1726 "member='LogMessage'",
1727 match_log_message
, &path
);
1729 return log_error_errno(r
, "Failed to install match: %m");
1731 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1733 log_error("Failed transfer image: %s", bus_error_message(&error
, -r
));
1737 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
1739 return bus_log_parse_error(r
);
1741 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
1744 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
1746 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1747 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1749 r
= sd_event_loop(event
);
1751 return log_error_errno(r
, "Failed to run event loop: %m");
1756 static int import_tar(int argc
, char *argv
[], void *userdata
) {
1757 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1758 _cleanup_free_
char *ll
= NULL
;
1759 _cleanup_close_
int fd
= -1;
1760 const char *local
= NULL
, *path
= NULL
;
1761 sd_bus
*bus
= userdata
;
1768 if (isempty(path
) || streq(path
, "-"))
1774 local
= basename(path
);
1775 if (isempty(local
) || streq(local
, "-"))
1779 log_error("Need either path or local name.");
1783 r
= tar_strip_suffixes(local
, &ll
);
1789 if (!machine_name_is_valid(local
)) {
1790 log_error("Local name %s is not a suitable machine name.", local
);
1795 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1797 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1800 r
= sd_bus_message_new_method_call(
1803 "org.freedesktop.import1",
1804 "/org/freedesktop/import1",
1805 "org.freedesktop.import1.Manager",
1808 return bus_log_create_error(r
);
1810 r
= sd_bus_message_append(
1813 fd
>= 0 ? fd
: STDIN_FILENO
,
1818 return bus_log_create_error(r
);
1820 return transfer_image_common(bus
, m
);
1823 static int import_raw(int argc
, char *argv
[], void *userdata
) {
1824 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1825 _cleanup_free_
char *ll
= NULL
;
1826 _cleanup_close_
int fd
= -1;
1827 const char *local
= NULL
, *path
= NULL
;
1828 sd_bus
*bus
= userdata
;
1835 if (isempty(path
) || streq(path
, "-"))
1841 local
= basename(path
);
1842 if (isempty(local
) || streq(local
, "-"))
1846 log_error("Need either path or local name.");
1850 r
= raw_strip_suffixes(local
, &ll
);
1856 if (!machine_name_is_valid(local
)) {
1857 log_error("Local name %s is not a suitable machine name.", local
);
1862 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1864 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1867 r
= sd_bus_message_new_method_call(
1870 "org.freedesktop.import1",
1871 "/org/freedesktop/import1",
1872 "org.freedesktop.import1.Manager",
1875 return bus_log_create_error(r
);
1877 r
= sd_bus_message_append(
1880 fd
>= 0 ? fd
: STDIN_FILENO
,
1885 return bus_log_create_error(r
);
1887 return transfer_image_common(bus
, m
);
1890 static void determine_compression_from_filename(const char *p
) {
1897 if (endswith(p
, ".xz"))
1899 else if (endswith(p
, ".gz"))
1900 arg_format
= "gzip";
1901 else if (endswith(p
, ".bz2"))
1902 arg_format
= "bzip2";
1905 static int export_tar(int argc
, char *argv
[], void *userdata
) {
1906 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1907 _cleanup_close_
int fd
= -1;
1908 const char *local
= NULL
, *path
= NULL
;
1909 sd_bus
*bus
= userdata
;
1915 if (!machine_name_is_valid(local
)) {
1916 log_error("Machine name %s is not valid.", local
);
1922 if (isempty(path
) || streq(path
, "-"))
1926 determine_compression_from_filename(path
);
1928 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1930 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1933 r
= sd_bus_message_new_method_call(
1936 "org.freedesktop.import1",
1937 "/org/freedesktop/import1",
1938 "org.freedesktop.import1.Manager",
1941 return bus_log_create_error(r
);
1943 r
= sd_bus_message_append(
1947 fd
>= 0 ? fd
: STDOUT_FILENO
,
1950 return bus_log_create_error(r
);
1952 return transfer_image_common(bus
, m
);
1955 static int export_raw(int argc
, char *argv
[], void *userdata
) {
1956 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1957 _cleanup_close_
int fd
= -1;
1958 const char *local
= NULL
, *path
= NULL
;
1959 sd_bus
*bus
= userdata
;
1965 if (!machine_name_is_valid(local
)) {
1966 log_error("Machine name %s is not valid.", local
);
1972 if (isempty(path
) || streq(path
, "-"))
1976 determine_compression_from_filename(path
);
1978 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1980 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1983 r
= sd_bus_message_new_method_call(
1986 "org.freedesktop.import1",
1987 "/org/freedesktop/import1",
1988 "org.freedesktop.import1.Manager",
1991 return bus_log_create_error(r
);
1993 r
= sd_bus_message_append(
1997 fd
>= 0 ? fd
: STDOUT_FILENO
,
2000 return bus_log_create_error(r
);
2002 return transfer_image_common(bus
, m
);
2005 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
2006 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2007 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2008 const char *local
, *remote
;
2009 sd_bus
*bus
= userdata
;
2015 if (!http_url_is_valid(remote
)) {
2016 log_error("URL '%s' is not valid.", remote
);
2023 r
= import_url_last_component(remote
, &l
);
2025 return log_error_errno(r
, "Failed to get final component of URL: %m");
2030 if (isempty(local
) || streq(local
, "-"))
2034 r
= tar_strip_suffixes(local
, &ll
);
2040 if (!machine_name_is_valid(local
)) {
2041 log_error("Local name %s is not a suitable machine name.", local
);
2046 r
= sd_bus_message_new_method_call(
2049 "org.freedesktop.import1",
2050 "/org/freedesktop/import1",
2051 "org.freedesktop.import1.Manager",
2054 return bus_log_create_error(r
);
2056 r
= sd_bus_message_append(
2061 import_verify_to_string(arg_verify
),
2064 return bus_log_create_error(r
);
2066 return transfer_image_common(bus
, m
);
2069 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
2070 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2071 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2072 const char *local
, *remote
;
2073 sd_bus
*bus
= userdata
;
2079 if (!http_url_is_valid(remote
)) {
2080 log_error("URL '%s' is not valid.", remote
);
2087 r
= import_url_last_component(remote
, &l
);
2089 return log_error_errno(r
, "Failed to get final component of URL: %m");
2094 if (isempty(local
) || streq(local
, "-"))
2098 r
= raw_strip_suffixes(local
, &ll
);
2104 if (!machine_name_is_valid(local
)) {
2105 log_error("Local name %s is not a suitable machine name.", local
);
2110 r
= sd_bus_message_new_method_call(
2113 "org.freedesktop.import1",
2114 "/org/freedesktop/import1",
2115 "org.freedesktop.import1.Manager",
2118 return bus_log_create_error(r
);
2120 r
= sd_bus_message_append(
2125 import_verify_to_string(arg_verify
),
2128 return bus_log_create_error(r
);
2130 return transfer_image_common(bus
, m
);
2133 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
2134 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2135 const char *local
, *remote
, *tag
;
2136 sd_bus
*bus
= userdata
;
2139 if (arg_verify
!= IMPORT_VERIFY_NO
) {
2140 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2145 tag
= strchr(remote
, ':');
2147 remote
= strndupa(remote
, tag
- remote
);
2151 if (!dkr_name_is_valid(remote
)) {
2152 log_error("DKR name '%s' is invalid.", remote
);
2155 if (tag
&& !dkr_tag_is_valid(tag
)) {
2156 log_error("DKR tag '%s' is invalid.", remote
);
2163 local
= strchr(remote
, '/');
2170 if (isempty(local
) || streq(local
, "-"))
2174 if (!machine_name_is_valid(local
)) {
2175 log_error("Local name %s is not a suitable machine name.", local
);
2180 r
= sd_bus_message_new_method_call(
2183 "org.freedesktop.import1",
2184 "/org/freedesktop/import1",
2185 "org.freedesktop.import1.Manager",
2188 return bus_log_create_error(r
);
2190 r
= sd_bus_message_append(
2197 import_verify_to_string(arg_verify
),
2200 return bus_log_create_error(r
);
2202 return transfer_image_common(bus
, m
);
2205 typedef struct TransferInfo
{
2213 static int compare_transfer_info(const void *a
, const void *b
) {
2214 const TransferInfo
*x
= a
, *y
= b
;
2216 return strcmp(x
->local
, y
->local
);
2219 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2220 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
2221 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
2222 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2223 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2224 size_t n_transfers
= 0, n_allocated
= 0, j
;
2225 const char *type
, *remote
, *local
, *object
;
2226 sd_bus
*bus
= userdata
;
2227 uint32_t id
, max_id
= 0;
2231 pager_open_if_enabled();
2233 r
= sd_bus_call_method(
2235 "org.freedesktop.import1",
2236 "/org/freedesktop/import1",
2237 "org.freedesktop.import1.Manager",
2243 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2247 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2249 return bus_log_parse_error(r
);
2251 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2254 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2257 transfers
[n_transfers
].id
= id
;
2258 transfers
[n_transfers
].type
= type
;
2259 transfers
[n_transfers
].remote
= remote
;
2260 transfers
[n_transfers
].local
= local
;
2261 transfers
[n_transfers
].progress
= progress
;
2281 return bus_log_parse_error(r
);
2283 r
= sd_bus_message_exit_container(reply
);
2285 return bus_log_parse_error(r
);
2287 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2290 printf("%-*s %-*s %-*s %-*s %-*s\n",
2291 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2293 (int) max_type
, "TYPE",
2294 (int) max_local
, "LOCAL",
2295 (int) max_remote
, "REMOTE");
2297 for (j
= 0; j
< n_transfers
; j
++)
2298 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2299 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2300 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2301 (int) max_type
, transfers
[j
].type
,
2302 (int) max_local
, transfers
[j
].local
,
2303 (int) max_remote
, transfers
[j
].remote
);
2306 printf("\n%zu transfers listed.\n", n_transfers
);
2311 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2312 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2313 sd_bus
*bus
= userdata
;
2318 polkit_agent_open_if_enabled();
2320 for (i
= 1; i
< argc
; i
++) {
2323 r
= safe_atou32(argv
[i
], &id
);
2325 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2327 r
= sd_bus_call_method(
2329 "org.freedesktop.import1",
2330 "/org/freedesktop/import1",
2331 "org.freedesktop.import1.Manager",
2337 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2345 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2346 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2347 sd_bus
*bus
= userdata
;
2351 if (streq(argv
[argc
-1], "-"))
2352 limit
= (uint64_t) -1;
2356 r
= parse_size(argv
[argc
-1], 1024, &off
);
2358 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2360 limit
= (uint64_t) off
;
2364 /* With two arguments changes the quota limit of the
2365 * specified image */
2366 r
= sd_bus_call_method(
2368 "org.freedesktop.machine1",
2369 "/org/freedesktop/machine1",
2370 "org.freedesktop.machine1.Manager",
2374 "st", argv
[1], limit
);
2376 /* With one argument changes the pool quota limit */
2377 r
= sd_bus_call_method(
2379 "org.freedesktop.machine1",
2380 "/org/freedesktop/machine1",
2381 "org.freedesktop.machine1.Manager",
2388 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2395 static int help(int argc
, char *argv
[], void *userdata
) {
2397 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2398 "Send control commands to or query the virtual machine and container\n"
2399 "registration manager.\n\n"
2400 " -h --help Show this help\n"
2401 " --version Show package version\n"
2402 " --no-pager Do not pipe output into a pager\n"
2403 " --no-legend Do not show the headers and footers\n"
2404 " --no-ask-password Do not ask for system passwords\n"
2405 " -H --host=[USER@]HOST Operate on remote host\n"
2406 " -M --machine=CONTAINER Operate on local container\n"
2407 " -p --property=NAME Show only properties by this name\n"
2408 " -q --quiet Suppress output\n"
2409 " -a --all Show all properties, including empty ones\n"
2410 " -l --full Do not ellipsize output\n"
2411 " --kill-who=WHO Who to send signal to\n"
2412 " -s --signal=SIGNAL Which signal to send\n"
2413 " --uid=USER Specify user ID to invoke shell as\n"
2414 " --setenv=VAR=VALUE Add an environment variable for shell\n"
2415 " --read-only Create read-only bind mount\n"
2416 " --mkdir Create directory before bind mounting, if missing\n"
2417 " -n --lines=INTEGER Number of journal entries to show\n"
2418 " -o --output=STRING Change journal output mode (short,\n"
2419 " short-monotonic, verbose, export, json,\n"
2420 " json-pretty, json-sse, cat)\n"
2421 " --verify=MODE Verification mode for downloaded images (no,\n"
2422 " checksum, signature)\n"
2423 " --force Download image even if already exists\n"
2424 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2426 "Machine Commands:\n"
2427 " list List running VMs and containers\n"
2428 " status NAME... Show VM/container details\n"
2429 " show NAME... Show properties of one or more VMs/containers\n"
2430 " start NAME... Start container as a service\n"
2431 " login NAME Get a login prompt on a container\n"
2432 " shell NAME [COMMAND...] Invoke a shell (or other command) in a container\n"
2433 " enable NAME... Enable automatic container start at boot\n"
2434 " disable NAME... Disable automatic container start at boot\n"
2435 " poweroff NAME... Power off one or more containers\n"
2436 " reboot NAME... Reboot one or more containers\n"
2437 " terminate NAME... Terminate one or more VMs/containers\n"
2438 " kill NAME... Send signal to processes of a VM/container\n"
2439 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2440 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2441 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2443 " list-images Show available container and VM images\n"
2444 " image-status NAME... Show image details\n"
2445 " show-image NAME... Show properties of image\n"
2446 " clone NAME NAME Clone an image\n"
2447 " rename NAME NAME Rename an image\n"
2448 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2449 " remove NAME... Remove an image\n"
2450 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2451 "Image Transfer Commands:\n"
2452 " pull-tar URL [NAME] Download a TAR container image\n"
2453 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2454 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2455 " import-tar FILE [NAME] Import a local TAR container image\n"
2456 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2457 " export-tar NAME [FILE] Export a TAR container image locally\n"
2458 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2459 " list-transfers Show list of downloads in progress\n"
2460 " cancel-transfer Cancel a download\n"
2461 , program_invocation_short_name
);
2466 static int parse_argv(int argc
, char *argv
[]) {
2469 ARG_VERSION
= 0x100,
2475 ARG_NO_ASK_PASSWORD
,
2484 static const struct option options
[] = {
2485 { "help", no_argument
, NULL
, 'h' },
2486 { "version", no_argument
, NULL
, ARG_VERSION
},
2487 { "property", required_argument
, NULL
, 'p' },
2488 { "all", no_argument
, NULL
, 'a' },
2489 { "full", no_argument
, NULL
, 'l' },
2490 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2491 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2492 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2493 { "signal", required_argument
, NULL
, 's' },
2494 { "host", required_argument
, NULL
, 'H' },
2495 { "machine", required_argument
, NULL
, 'M' },
2496 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2497 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2498 { "quiet", no_argument
, NULL
, 'q' },
2499 { "lines", required_argument
, NULL
, 'n' },
2500 { "output", required_argument
, NULL
, 'o' },
2501 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2502 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2503 { "force", no_argument
, NULL
, ARG_FORCE
},
2504 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
2505 { "format", required_argument
, NULL
, ARG_FORMAT
},
2506 { "uid", required_argument
, NULL
, ARG_UID
},
2507 { "setenv", required_argument
, NULL
, ARG_SETENV
},
2516 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:qn:o:", options
, NULL
)) >= 0)
2521 return help(0, NULL
, NULL
);
2524 puts(PACKAGE_STRING
);
2525 puts(SYSTEMD_FEATURES
);
2529 r
= strv_extend(&arg_property
, optarg
);
2533 /* If the user asked for a particular
2534 * property, show it to him, even if it is
2548 if (safe_atou(optarg
, &arg_lines
) < 0) {
2549 log_error("Failed to parse lines '%s'", optarg
);
2555 arg_output
= output_mode_from_string(optarg
);
2556 if (arg_output
< 0) {
2557 log_error("Unknown output '%s'.", optarg
);
2563 arg_no_pager
= true;
2571 arg_kill_who
= optarg
;
2575 arg_signal
= signal_from_string_try_harder(optarg
);
2576 if (arg_signal
< 0) {
2577 log_error("Failed to parse signal string %s.", optarg
);
2582 case ARG_NO_ASK_PASSWORD
:
2583 arg_ask_password
= false;
2587 arg_transport
= BUS_TRANSPORT_REMOTE
;
2592 arg_transport
= BUS_TRANSPORT_MACHINE
;
2597 arg_read_only
= true;
2609 arg_verify
= import_verify_from_string(optarg
);
2610 if (arg_verify
< 0) {
2611 log_error("Failed to parse --verify= setting: %s", optarg
);
2620 case ARG_DKR_INDEX_URL
:
2621 if (!http_url_is_valid(optarg
)) {
2622 log_error("Index URL is invalid: %s", optarg
);
2626 arg_dkr_index_url
= optarg
;
2630 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
2631 log_error("Unknown format: %s", optarg
);
2635 arg_format
= optarg
;
2643 if (!env_assignment_is_valid(optarg
)) {
2644 log_error("Environment assignment invalid: %s", optarg
);
2648 r
= strv_extend(&arg_setenv
, optarg
);
2657 assert_not_reached("Unhandled option");
2663 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
2665 static const Verb verbs
[] = {
2666 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2667 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
2668 { "list-images", VERB_ANY
, 1, 0, list_images
},
2669 { "status", 2, VERB_ANY
, 0, show_machine
},
2670 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
2671 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
2672 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
2673 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
2674 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
2675 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
2676 { "kill", 2, VERB_ANY
, 0, kill_machine
},
2677 { "login", 2, 2, 0, login_machine
},
2678 { "shell", 2, VERB_ANY
, 0, shell_machine
},
2679 { "bind", 3, 4, 0, bind_mount
},
2680 { "copy-to", 3, 4, 0, copy_files
},
2681 { "copy-from", 3, 4, 0, copy_files
},
2682 { "remove", 2, VERB_ANY
, 0, remove_image
},
2683 { "rename", 3, 3, 0, rename_image
},
2684 { "clone", 3, 3, 0, clone_image
},
2685 { "read-only", 2, 3, 0, read_only_image
},
2686 { "start", 2, VERB_ANY
, 0, start_machine
},
2687 { "enable", 2, VERB_ANY
, 0, enable_machine
},
2688 { "disable", 2, VERB_ANY
, 0, enable_machine
},
2689 { "import-tar", 2, 3, 0, import_tar
},
2690 { "import-raw", 2, 3, 0, import_raw
},
2691 { "export-tar", 2, 3, 0, export_tar
},
2692 { "export-raw", 2, 3, 0, export_raw
},
2693 { "pull-tar", 2, 3, 0, pull_tar
},
2694 { "pull-raw", 2, 3, 0, pull_raw
},
2695 { "pull-dkr", 2, 3, 0, pull_dkr
},
2696 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
2697 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
2698 { "set-limit", 2, 3, 0, set_limit
},
2702 return dispatch_verb(argc
, argv
, verbs
, bus
);
2705 int main(int argc
, char*argv
[]) {
2706 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
2709 setlocale(LC_ALL
, "");
2710 log_parse_environment();
2713 r
= parse_argv(argc
, argv
);
2717 r
= bus_open_transport(arg_transport
, arg_host
, false, &bus
);
2719 log_error_errno(r
, "Failed to create bus connection: %m");
2723 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
2725 r
= machinectl_main(argc
, argv
, bus
);
2729 polkit_agent_close();
2731 strv_free(arg_property
);
2732 strv_free(arg_setenv
);
2734 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;