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" : "org.freedesktop.systemd1.Service",
367 log_error("Failed to query ControlGroup: %s", bus_error_message(&error
, -r
));
371 r
= sd_bus_message_read(reply
, "s", &cgroup
);
373 return bus_log_parse_error(r
);
378 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, false) != 0 && leader
<= 0)
387 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, "\t\t ", c
, false, &leader
, leader
> 0, get_output_flags());
391 static int print_addresses(sd_bus
*bus
, const char *name
, int ifi
, const char *prefix
, const char *prefix2
) {
392 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
400 r
= sd_bus_call_method(bus
,
401 "org.freedesktop.machine1",
402 "/org/freedesktop/machine1",
403 "org.freedesktop.machine1.Manager",
404 "GetMachineAddresses",
411 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
413 return bus_log_parse_error(r
);
415 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
419 char buffer
[MAX(INET6_ADDRSTRLEN
, INET_ADDRSTRLEN
)];
421 r
= sd_bus_message_read(reply
, "i", &family
);
423 return bus_log_parse_error(r
);
425 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
427 return bus_log_parse_error(r
);
429 fputs(prefix
, stdout
);
430 fputs(inet_ntop(family
, a
, buffer
, sizeof(buffer
)), stdout
);
431 if (family
== AF_INET6
&& ifi
> 0)
435 r
= sd_bus_message_exit_container(reply
);
437 return bus_log_parse_error(r
);
439 if (prefix
!= prefix2
)
443 return bus_log_parse_error(r
);
445 r
= sd_bus_message_exit_container(reply
);
447 return bus_log_parse_error(r
);
452 static int print_os_release(sd_bus
*bus
, const char *name
, const char *prefix
) {
453 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
454 const char *k
, *v
, *pretty
= NULL
;
461 r
= sd_bus_call_method(bus
,
462 "org.freedesktop.machine1",
463 "/org/freedesktop/machine1",
464 "org.freedesktop.machine1.Manager",
465 "GetMachineOSRelease",
472 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
474 return bus_log_parse_error(r
);
476 while ((r
= sd_bus_message_read(reply
, "{ss}", &k
, &v
)) > 0) {
477 if (streq(k
, "PRETTY_NAME"))
482 return bus_log_parse_error(r
);
484 r
= sd_bus_message_exit_container(reply
);
486 return bus_log_parse_error(r
);
489 printf("%s%s\n", prefix
, pretty
);
494 typedef struct MachineStatusInfo
{
500 char *root_directory
;
502 struct dual_timestamp timestamp
;
507 static void machine_status_info_clear(MachineStatusInfo
*info
) {
513 free(info
->root_directory
);
519 static void print_machine_status_info(sd_bus
*bus
, MachineStatusInfo
*i
) {
520 char since1
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
521 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
527 fputs(strna(i
->name
), stdout
);
529 if (!sd_id128_equal(i
->id
, SD_ID128_NULL
))
530 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
534 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
.realtime
);
535 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
.realtime
);
538 printf("\t Since: %s; %s\n", s2
, s1
);
540 printf("\t Since: %s\n", s2
);
543 _cleanup_free_
char *t
= NULL
;
545 printf("\t Leader: %u", (unsigned) i
->leader
);
547 get_process_comm(i
->leader
, &t
);
555 printf("\t Service: %s", i
->service
);
558 printf("; class %s", i
->class);
562 printf("\t Class: %s\n", i
->class);
564 if (i
->root_directory
)
565 printf("\t Root: %s\n", i
->root_directory
);
567 if (i
->n_netif
> 0) {
570 fputs("\t Iface:", stdout
);
572 for (c
= 0; c
< i
->n_netif
; c
++) {
573 char name
[IF_NAMESIZE
+1] = "";
575 if (if_indextoname(i
->netif
[c
], name
)) {
584 printf(" %i", i
->netif
[c
]);
590 print_addresses(bus
, i
->name
, ifi
,
594 print_os_release(bus
, i
->name
, "\t OS: ");
597 printf("\t Unit: %s\n", i
->unit
);
598 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
600 if (arg_transport
== BUS_TRANSPORT_LOCAL
) {
602 show_journal_by_unit(
607 i
->timestamp
.monotonic
,
610 get_output_flags() | OUTPUT_BEGIN_NEWLINE
,
611 SD_JOURNAL_LOCAL_ONLY
,
618 static int map_netif(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
619 MachineStatusInfo
*i
= userdata
;
624 assert_cc(sizeof(int32_t) == sizeof(int));
625 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
631 i
->n_netif
= l
/ sizeof(int32_t);
632 i
->netif
= memdup(v
, l
);
639 static int show_machine_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
641 static const struct bus_properties_map map
[] = {
642 { "Name", "s", NULL
, offsetof(MachineStatusInfo
, name
) },
643 { "Class", "s", NULL
, offsetof(MachineStatusInfo
, class) },
644 { "Service", "s", NULL
, offsetof(MachineStatusInfo
, service
) },
645 { "Unit", "s", NULL
, offsetof(MachineStatusInfo
, unit
) },
646 { "RootDirectory", "s", NULL
, offsetof(MachineStatusInfo
, root_directory
) },
647 { "Leader", "u", NULL
, offsetof(MachineStatusInfo
, leader
) },
648 { "Timestamp", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.realtime
) },
649 { "TimestampMonotonic", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.monotonic
) },
650 { "Id", "ay", bus_map_id128
, offsetof(MachineStatusInfo
, id
) },
651 { "NetworkInterfaces", "ai", map_netif
, 0 },
655 _cleanup_(machine_status_info_clear
) MachineStatusInfo info
= {};
663 r
= bus_map_all_properties(bus
,
664 "org.freedesktop.machine1",
669 return log_error_errno(r
, "Could not get properties: %m");
675 print_machine_status_info(bus
, &info
);
680 static int show_machine_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
692 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
694 log_error_errno(r
, "Could not get properties: %m");
699 static int show_machine(int argc
, char *argv
[], void *userdata
) {
701 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
702 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
703 bool properties
, new_line
= false;
704 sd_bus
*bus
= userdata
;
709 properties
= !strstr(argv
[0], "status");
711 pager_open_if_enabled();
713 if (properties
&& argc
<= 1) {
715 /* If no argument is specified, inspect the manager
717 r
= show_machine_properties(bus
, "/org/freedesktop/machine1", &new_line
);
722 for (i
= 1; i
< argc
; i
++) {
723 const char *path
= NULL
;
725 r
= sd_bus_call_method(
727 "org.freedesktop.machine1",
728 "/org/freedesktop/machine1",
729 "org.freedesktop.machine1.Manager",
735 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
739 r
= sd_bus_message_read(reply
, "o", &path
);
741 return bus_log_parse_error(r
);
744 r
= show_machine_properties(bus
, path
, &new_line
);
746 r
= show_machine_info(argv
[0], bus
, path
, &new_line
);
752 typedef struct ImageStatusInfo
{
761 uint64_t usage_exclusive
;
762 uint64_t limit_exclusive
;
765 static void image_status_info_clear(ImageStatusInfo
*info
) {
774 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
775 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
776 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
777 char bs
[FORMAT_BYTES_MAX
], *s3
;
778 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
784 fputs(i
->name
, stdout
);
789 printf("\t Type: %s\n", i
->type
);
792 printf("\t Path: %s\n", i
->path
);
794 printf("\t RO: %s%s%s\n",
795 i
->read_only
? ansi_highlight_red() : "",
796 i
->read_only
? "read-only" : "writable",
797 i
->read_only
? ansi_highlight_off() : "");
799 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
800 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
802 printf("\t Created: %s; %s\n", s2
, s1
);
804 printf("\t Created: %s\n", s2
);
806 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
807 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
809 printf("\tModified: %s; %s\n", s2
, s1
);
811 printf("\tModified: %s\n", s2
);
813 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
814 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
816 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
818 printf("\t Usage: %s\n", s3
);
820 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
821 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
823 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
825 printf("\t Limit: %s\n", s3
);
828 static int show_image_info(sd_bus
*bus
, const char *path
, bool *new_line
) {
830 static const struct bus_properties_map map
[] = {
831 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
832 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
833 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
834 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
835 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
836 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
837 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
838 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
839 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
840 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
844 _cleanup_(image_status_info_clear
) ImageStatusInfo info
= {};
851 r
= bus_map_all_properties(bus
,
852 "org.freedesktop.machine1",
857 return log_error_errno(r
, "Could not get properties: %m");
863 print_image_status_info(bus
, &info
);
868 typedef struct PoolStatusInfo
{
874 static void pool_status_info_clear(PoolStatusInfo
*info
) {
883 static void print_pool_status_info(sd_bus
*bus
, PoolStatusInfo
*i
) {
884 char bs
[FORMAT_BYTES_MAX
], *s
;
887 printf("\t Path: %s\n", i
->path
);
889 s
= format_bytes(bs
, sizeof(bs
), i
->usage
);
891 printf("\t Usage: %s\n", s
);
893 s
= format_bytes(bs
, sizeof(bs
), i
->limit
);
895 printf("\t Limit: %s\n", s
);
898 static int show_pool_info(sd_bus
*bus
) {
900 static const struct bus_properties_map map
[] = {
901 { "PoolPath", "s", NULL
, offsetof(PoolStatusInfo
, path
) },
902 { "PoolUsage", "t", NULL
, offsetof(PoolStatusInfo
, usage
) },
903 { "PoolLimit", "t", NULL
, offsetof(PoolStatusInfo
, limit
) },
907 _cleanup_(pool_status_info_clear
) PoolStatusInfo info
= {
908 .usage
= (uint64_t) -1,
909 .limit
= (uint64_t) -1,
915 r
= bus_map_all_properties(bus
,
916 "org.freedesktop.machine1",
917 "/org/freedesktop/machine1",
921 return log_error_errno(r
, "Could not get properties: %m");
923 print_pool_status_info(bus
, &info
);
929 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
941 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
943 log_error_errno(r
, "Could not get properties: %m");
948 static int show_image(int argc
, char *argv
[], void *userdata
) {
950 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
951 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
952 bool properties
, new_line
= false;
953 sd_bus
*bus
= userdata
;
958 properties
= !strstr(argv
[0], "status");
960 pager_open_if_enabled();
964 /* If no argument is specified, inspect the manager
968 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
970 r
= show_pool_info(bus
);
975 for (i
= 1; i
< argc
; i
++) {
976 const char *path
= NULL
;
978 r
= sd_bus_call_method(
980 "org.freedesktop.machine1",
981 "/org/freedesktop/machine1",
982 "org.freedesktop.machine1.Manager",
988 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
992 r
= sd_bus_message_read(reply
, "o", &path
);
994 return bus_log_parse_error(r
);
997 r
= show_image_properties(bus
, path
, &new_line
);
999 r
= show_image_info(bus
, path
, &new_line
);
1005 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
1006 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1007 sd_bus
*bus
= userdata
;
1012 polkit_agent_open_if_enabled();
1015 arg_kill_who
= "all";
1017 for (i
= 1; i
< argc
; i
++) {
1018 r
= sd_bus_call_method(
1020 "org.freedesktop.machine1",
1021 "/org/freedesktop/machine1",
1022 "org.freedesktop.machine1.Manager",
1026 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
1028 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
1036 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
1037 arg_kill_who
= "leader";
1038 arg_signal
= SIGINT
; /* sysvinit + systemd */
1040 return kill_machine(argc
, argv
, userdata
);
1043 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
1044 arg_kill_who
= "leader";
1045 arg_signal
= SIGRTMIN
+4; /* only systemd */
1047 return kill_machine(argc
, argv
, userdata
);
1050 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
1051 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1052 sd_bus
*bus
= userdata
;
1057 polkit_agent_open_if_enabled();
1059 for (i
= 1; i
< argc
; i
++) {
1060 r
= sd_bus_call_method(
1062 "org.freedesktop.machine1",
1063 "/org/freedesktop/machine1",
1064 "org.freedesktop.machine1.Manager",
1070 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1078 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1079 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1080 _cleanup_free_
char *abs_host_path
= NULL
;
1081 char *dest
, *host_path
, *container_path
;
1082 sd_bus
*bus
= userdata
;
1088 polkit_agent_open_if_enabled();
1090 copy_from
= streq(argv
[0], "copy-from");
1091 dest
= argv
[3] ?: argv
[2];
1092 host_path
= copy_from
? dest
: argv
[2];
1093 container_path
= copy_from
? argv
[2] : dest
;
1095 if (!path_is_absolute(host_path
)) {
1096 abs_host_path
= path_make_absolute_cwd(host_path
);
1099 host_path
= abs_host_path
;
1102 r
= sd_bus_call_method(
1104 "org.freedesktop.machine1",
1105 "/org/freedesktop/machine1",
1106 "org.freedesktop.machine1.Manager",
1107 copy_from
? "CopyFromMachine" : "CopyToMachine",
1112 copy_from
? container_path
: host_path
,
1113 copy_from
? host_path
: container_path
);
1115 log_error("Failed to copy: %s", bus_error_message(&error
, -r
));
1122 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1123 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1124 sd_bus
*bus
= userdata
;
1129 polkit_agent_open_if_enabled();
1131 r
= sd_bus_call_method(
1133 "org.freedesktop.machine1",
1134 "/org/freedesktop/machine1",
1135 "org.freedesktop.machine1.Manager",
1146 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1153 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1154 PTYForward
** forward
= (PTYForward
**) userdata
;
1161 /* If the forwarder is already initialized, tell it to
1162 * exit on the next vhangup(), so that we still flush
1163 * out what might be queued and exit then. */
1165 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1169 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1172 /* On error, or when the forwarder is not initialized yet, quit immediately */
1173 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1177 static int process_forward(sd_event
*event
, PTYForward
**forward
, int master
, bool ignore_vhangup
, const char *name
) {
1183 assert(master
>= 0);
1186 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGWINCH
, SIGTERM
, SIGINT
, -1) >= 0);
1188 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name
);
1190 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1191 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1193 r
= pty_forward_new(event
, master
, ignore_vhangup
, false, forward
);
1195 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1197 r
= sd_event_loop(event
);
1199 return log_error_errno(r
, "Failed to run event loop: %m");
1201 pty_forward_get_last_char(*forward
, &last_char
);
1205 pty_forward_get_ignore_vhangup(*forward
) == 0;
1207 *forward
= pty_forward_free(*forward
);
1209 if (last_char
!= '\n')
1210 fputc('\n', stdout
);
1213 log_info("Machine %s terminated.", name
);
1215 log_info("Connection to machine %s terminated.", name
);
1217 sd_event_get_exit_code(event
, &ret
);
1221 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1222 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
, *m
= NULL
;
1223 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1224 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1225 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1226 _cleanup_event_unref_ sd_event
*event
= NULL
;
1228 sd_bus
*bus
= userdata
;
1229 const char *pty
, *match
;
1233 if (!strv_isempty(arg_setenv
) || arg_uid
) {
1234 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1238 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1239 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1240 log_error("Login only supported on local machines.");
1244 polkit_agent_open_if_enabled();
1246 r
= sd_event_default(&event
);
1248 return log_error_errno(r
, "Failed to get event loop: %m");
1250 r
= sd_bus_attach_event(bus
, event
, 0);
1252 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1254 match
= strjoina("type='signal',"
1255 "sender='org.freedesktop.machine1',"
1256 "path='/org/freedesktop/machine1',",
1257 "interface='org.freedesktop.machine1.Manager',"
1258 "member='MachineRemoved',"
1263 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1265 return log_error_errno(r
, "Failed to add machine removal match: %m");
1267 r
= sd_bus_call_method(
1269 "org.freedesktop.machine1",
1270 "/org/freedesktop/machine1",
1271 "org.freedesktop.machine1.Manager",
1277 log_error("Failed to get login PTY: %s", bus_error_message(&error
, -r
));
1281 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1283 return bus_log_parse_error(r
);
1285 return process_forward(event
, &forward
, master
, true, argv
[1]);
1288 static int shell_machine(int argc
, char *argv
[], void *userdata
) {
1289 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
, *m
= NULL
;
1290 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1291 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1292 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1293 _cleanup_event_unref_ sd_event
*event
= NULL
;
1295 sd_bus
*bus
= userdata
;
1296 const char *pty
, *match
;
1300 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1301 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1302 log_error("Shell only supported on local machines.");
1306 polkit_agent_open_if_enabled();
1308 r
= sd_event_default(&event
);
1310 return log_error_errno(r
, "Failed to get event loop: %m");
1312 r
= sd_bus_attach_event(bus
, event
, 0);
1314 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1316 match
= strjoina("type='signal',"
1317 "sender='org.freedesktop.machine1',"
1318 "path='/org/freedesktop/machine1',",
1319 "interface='org.freedesktop.machine1.Manager',"
1320 "member='MachineRemoved',"
1325 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1327 return log_error_errno(r
, "Failed to add machine removal match: %m");
1329 r
= sd_bus_message_new_method_call(
1332 "org.freedesktop.machine1",
1333 "/org/freedesktop/machine1",
1334 "org.freedesktop.machine1.Manager",
1335 "OpenMachineShell");
1337 return bus_log_create_error(r
);
1339 r
= sd_bus_message_append(m
, "sss", argv
[1], arg_uid
, argv
[2]);
1341 return bus_log_create_error(r
);
1343 r
= sd_bus_message_append_strv(m
, strv_length(argv
+ 2) <= 1 ? NULL
: argv
+ 2);
1345 return bus_log_create_error(r
);
1347 r
= sd_bus_message_append_strv(m
, arg_setenv
);
1349 return bus_log_create_error(r
);
1351 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1353 log_error("Failed to get shell PTY: %s", bus_error_message(&error
, -r
));
1357 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1359 return bus_log_parse_error(r
);
1361 return process_forward(event
, &forward
, master
, false, argv
[1]);
1364 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1365 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1366 sd_bus
*bus
= userdata
;
1371 polkit_agent_open_if_enabled();
1373 for (i
= 1; i
< argc
; i
++) {
1374 r
= sd_bus_call_method(
1376 "org.freedesktop.machine1",
1377 "/org/freedesktop/machine1",
1378 "org.freedesktop.machine1.Manager",
1384 log_error("Could not remove image: %s", bus_error_message(&error
, -r
));
1392 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1393 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1394 sd_bus
*bus
= userdata
;
1397 polkit_agent_open_if_enabled();
1399 r
= sd_bus_call_method(
1401 "org.freedesktop.machine1",
1402 "/org/freedesktop/machine1",
1403 "org.freedesktop.machine1.Manager",
1407 "ss", argv
[1], argv
[2]);
1409 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1416 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1417 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1418 sd_bus
*bus
= userdata
;
1421 polkit_agent_open_if_enabled();
1423 r
= sd_bus_call_method(
1425 "org.freedesktop.machine1",
1426 "/org/freedesktop/machine1",
1427 "org.freedesktop.machine1.Manager",
1431 "ssb", argv
[1], argv
[2], arg_read_only
);
1433 log_error("Could not clone image: %s", bus_error_message(&error
, -r
));
1440 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1441 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1442 sd_bus
*bus
= userdata
;
1446 b
= parse_boolean(argv
[2]);
1448 log_error("Failed to parse boolean argument: %s", argv
[2]);
1453 polkit_agent_open_if_enabled();
1455 r
= sd_bus_call_method(
1457 "org.freedesktop.machine1",
1458 "/org/freedesktop/machine1",
1459 "org.freedesktop.machine1.Manager",
1460 "MarkImageReadOnly",
1465 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1472 static int make_service_name(const char *name
, char **ret
) {
1473 _cleanup_free_
char *e
= NULL
;
1479 if (!machine_name_is_valid(name
)) {
1480 log_error("Invalid machine name %s.", name
);
1484 e
= unit_name_escape(name
);
1488 r
= unit_name_build("systemd-nspawn", e
, ".service", ret
);
1490 return log_error_errno(r
, "Failed to build unit name: %m");
1495 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1496 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1497 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1498 sd_bus
*bus
= userdata
;
1503 polkit_agent_open_if_enabled();
1505 r
= bus_wait_for_jobs_new(bus
, &w
);
1509 for (i
= 1; i
< argc
; i
++) {
1510 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1511 _cleanup_free_
char *unit
= NULL
;
1514 r
= make_service_name(argv
[i
], &unit
);
1518 r
= sd_bus_call_method(
1520 "org.freedesktop.systemd1",
1521 "/org/freedesktop/systemd1",
1522 "org.freedesktop.systemd1.Manager",
1526 "ss", unit
, "fail");
1528 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1532 r
= sd_bus_message_read(reply
, "o", &object
);
1534 return bus_log_parse_error(r
);
1536 r
= bus_wait_for_jobs_add(w
, object
);
1541 r
= bus_wait_for_jobs(w
, arg_quiet
);
1548 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1549 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1550 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1551 int carries_install_info
= 0;
1552 const char *method
= NULL
;
1553 sd_bus
*bus
= userdata
;
1558 polkit_agent_open_if_enabled();
1560 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1562 r
= sd_bus_message_new_method_call(
1565 "org.freedesktop.systemd1",
1566 "/org/freedesktop/systemd1",
1567 "org.freedesktop.systemd1.Manager",
1570 return bus_log_create_error(r
);
1572 r
= sd_bus_message_open_container(m
, 'a', "s");
1574 return bus_log_create_error(r
);
1576 for (i
= 1; i
< argc
; i
++) {
1577 _cleanup_free_
char *unit
= NULL
;
1579 r
= make_service_name(argv
[i
], &unit
);
1583 r
= sd_bus_message_append(m
, "s", unit
);
1585 return bus_log_create_error(r
);
1588 r
= sd_bus_message_close_container(m
);
1590 return bus_log_create_error(r
);
1592 if (streq(argv
[0], "enable"))
1593 r
= sd_bus_message_append(m
, "bb", false, false);
1595 r
= sd_bus_message_append(m
, "b", false);
1597 return bus_log_create_error(r
);
1599 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1601 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1605 if (streq(argv
[0], "enable")) {
1606 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1608 return bus_log_parse_error(r
);
1611 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, NULL
, NULL
);
1615 r
= sd_bus_call_method(
1617 "org.freedesktop.systemd1",
1618 "/org/freedesktop/systemd1",
1619 "org.freedesktop.systemd1.Manager",
1625 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1632 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1633 const char **our_path
= userdata
, *line
;
1640 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1642 bus_log_parse_error(r
);
1646 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
1649 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1652 log_full(priority
, "%s", line
);
1656 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1657 const char **our_path
= userdata
, *path
, *result
;
1664 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1666 bus_log_parse_error(r
);
1670 if (!streq_ptr(*our_path
, path
))
1673 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
1677 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
1682 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
1684 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
1688 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1689 _cleanup_bus_slot_unref_ sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1690 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1691 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1692 _cleanup_event_unref_ sd_event
* event
= NULL
;
1693 const char *path
= NULL
;
1700 polkit_agent_open_if_enabled();
1702 r
= sd_event_default(&event
);
1704 return log_error_errno(r
, "Failed to get event loop: %m");
1706 r
= sd_bus_attach_event(bus
, event
, 0);
1708 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1710 r
= sd_bus_add_match(
1714 "sender='org.freedesktop.import1',"
1715 "interface='org.freedesktop.import1.Manager',"
1716 "member='TransferRemoved',"
1717 "path='/org/freedesktop/import1'",
1718 match_transfer_removed
, &path
);
1720 return log_error_errno(r
, "Failed to install match: %m");
1722 r
= sd_bus_add_match(
1726 "sender='org.freedesktop.import1',"
1727 "interface='org.freedesktop.import1.Transfer',"
1728 "member='LogMessage'",
1729 match_log_message
, &path
);
1731 return log_error_errno(r
, "Failed to install match: %m");
1733 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1735 log_error("Failed transfer image: %s", bus_error_message(&error
, -r
));
1739 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
1741 return bus_log_parse_error(r
);
1743 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
1746 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
1748 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1749 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1751 r
= sd_event_loop(event
);
1753 return log_error_errno(r
, "Failed to run event loop: %m");
1758 static int import_tar(int argc
, char *argv
[], void *userdata
) {
1759 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1760 _cleanup_free_
char *ll
= NULL
;
1761 _cleanup_close_
int fd
= -1;
1762 const char *local
= NULL
, *path
= NULL
;
1763 sd_bus
*bus
= userdata
;
1770 if (isempty(path
) || streq(path
, "-"))
1776 local
= basename(path
);
1777 if (isempty(local
) || streq(local
, "-"))
1781 log_error("Need either path or local name.");
1785 r
= tar_strip_suffixes(local
, &ll
);
1791 if (!machine_name_is_valid(local
)) {
1792 log_error("Local name %s is not a suitable machine name.", local
);
1797 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1799 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1802 r
= sd_bus_message_new_method_call(
1805 "org.freedesktop.import1",
1806 "/org/freedesktop/import1",
1807 "org.freedesktop.import1.Manager",
1810 return bus_log_create_error(r
);
1812 r
= sd_bus_message_append(
1815 fd
>= 0 ? fd
: STDIN_FILENO
,
1820 return bus_log_create_error(r
);
1822 return transfer_image_common(bus
, m
);
1825 static int import_raw(int argc
, char *argv
[], void *userdata
) {
1826 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1827 _cleanup_free_
char *ll
= NULL
;
1828 _cleanup_close_
int fd
= -1;
1829 const char *local
= NULL
, *path
= NULL
;
1830 sd_bus
*bus
= userdata
;
1837 if (isempty(path
) || streq(path
, "-"))
1843 local
= basename(path
);
1844 if (isempty(local
) || streq(local
, "-"))
1848 log_error("Need either path or local name.");
1852 r
= raw_strip_suffixes(local
, &ll
);
1858 if (!machine_name_is_valid(local
)) {
1859 log_error("Local name %s is not a suitable machine name.", local
);
1864 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1866 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1869 r
= sd_bus_message_new_method_call(
1872 "org.freedesktop.import1",
1873 "/org/freedesktop/import1",
1874 "org.freedesktop.import1.Manager",
1877 return bus_log_create_error(r
);
1879 r
= sd_bus_message_append(
1882 fd
>= 0 ? fd
: STDIN_FILENO
,
1887 return bus_log_create_error(r
);
1889 return transfer_image_common(bus
, m
);
1892 static void determine_compression_from_filename(const char *p
) {
1899 if (endswith(p
, ".xz"))
1901 else if (endswith(p
, ".gz"))
1902 arg_format
= "gzip";
1903 else if (endswith(p
, ".bz2"))
1904 arg_format
= "bzip2";
1907 static int export_tar(int argc
, char *argv
[], void *userdata
) {
1908 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1909 _cleanup_close_
int fd
= -1;
1910 const char *local
= NULL
, *path
= NULL
;
1911 sd_bus
*bus
= userdata
;
1917 if (!machine_name_is_valid(local
)) {
1918 log_error("Machine name %s is not valid.", local
);
1924 if (isempty(path
) || streq(path
, "-"))
1928 determine_compression_from_filename(path
);
1930 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1932 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1935 r
= sd_bus_message_new_method_call(
1938 "org.freedesktop.import1",
1939 "/org/freedesktop/import1",
1940 "org.freedesktop.import1.Manager",
1943 return bus_log_create_error(r
);
1945 r
= sd_bus_message_append(
1949 fd
>= 0 ? fd
: STDOUT_FILENO
,
1952 return bus_log_create_error(r
);
1954 return transfer_image_common(bus
, m
);
1957 static int export_raw(int argc
, char *argv
[], void *userdata
) {
1958 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1959 _cleanup_close_
int fd
= -1;
1960 const char *local
= NULL
, *path
= NULL
;
1961 sd_bus
*bus
= userdata
;
1967 if (!machine_name_is_valid(local
)) {
1968 log_error("Machine name %s is not valid.", local
);
1974 if (isempty(path
) || streq(path
, "-"))
1978 determine_compression_from_filename(path
);
1980 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1982 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1985 r
= sd_bus_message_new_method_call(
1988 "org.freedesktop.import1",
1989 "/org/freedesktop/import1",
1990 "org.freedesktop.import1.Manager",
1993 return bus_log_create_error(r
);
1995 r
= sd_bus_message_append(
1999 fd
>= 0 ? fd
: STDOUT_FILENO
,
2002 return bus_log_create_error(r
);
2004 return transfer_image_common(bus
, m
);
2007 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
2008 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2009 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2010 const char *local
, *remote
;
2011 sd_bus
*bus
= userdata
;
2017 if (!http_url_is_valid(remote
)) {
2018 log_error("URL '%s' is not valid.", remote
);
2025 r
= import_url_last_component(remote
, &l
);
2027 return log_error_errno(r
, "Failed to get final component of URL: %m");
2032 if (isempty(local
) || streq(local
, "-"))
2036 r
= tar_strip_suffixes(local
, &ll
);
2042 if (!machine_name_is_valid(local
)) {
2043 log_error("Local name %s is not a suitable machine name.", local
);
2048 r
= sd_bus_message_new_method_call(
2051 "org.freedesktop.import1",
2052 "/org/freedesktop/import1",
2053 "org.freedesktop.import1.Manager",
2056 return bus_log_create_error(r
);
2058 r
= sd_bus_message_append(
2063 import_verify_to_string(arg_verify
),
2066 return bus_log_create_error(r
);
2068 return transfer_image_common(bus
, m
);
2071 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
2072 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2073 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2074 const char *local
, *remote
;
2075 sd_bus
*bus
= userdata
;
2081 if (!http_url_is_valid(remote
)) {
2082 log_error("URL '%s' is not valid.", remote
);
2089 r
= import_url_last_component(remote
, &l
);
2091 return log_error_errno(r
, "Failed to get final component of URL: %m");
2096 if (isempty(local
) || streq(local
, "-"))
2100 r
= raw_strip_suffixes(local
, &ll
);
2106 if (!machine_name_is_valid(local
)) {
2107 log_error("Local name %s is not a suitable machine name.", local
);
2112 r
= sd_bus_message_new_method_call(
2115 "org.freedesktop.import1",
2116 "/org/freedesktop/import1",
2117 "org.freedesktop.import1.Manager",
2120 return bus_log_create_error(r
);
2122 r
= sd_bus_message_append(
2127 import_verify_to_string(arg_verify
),
2130 return bus_log_create_error(r
);
2132 return transfer_image_common(bus
, m
);
2135 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
2136 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2137 const char *local
, *remote
, *tag
;
2138 sd_bus
*bus
= userdata
;
2141 if (arg_verify
!= IMPORT_VERIFY_NO
) {
2142 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2147 tag
= strchr(remote
, ':');
2149 remote
= strndupa(remote
, tag
- remote
);
2153 if (!dkr_name_is_valid(remote
)) {
2154 log_error("DKR name '%s' is invalid.", remote
);
2157 if (tag
&& !dkr_tag_is_valid(tag
)) {
2158 log_error("DKR tag '%s' is invalid.", remote
);
2165 local
= strchr(remote
, '/');
2172 if (isempty(local
) || streq(local
, "-"))
2176 if (!machine_name_is_valid(local
)) {
2177 log_error("Local name %s is not a suitable machine name.", local
);
2182 r
= sd_bus_message_new_method_call(
2185 "org.freedesktop.import1",
2186 "/org/freedesktop/import1",
2187 "org.freedesktop.import1.Manager",
2190 return bus_log_create_error(r
);
2192 r
= sd_bus_message_append(
2199 import_verify_to_string(arg_verify
),
2202 return bus_log_create_error(r
);
2204 return transfer_image_common(bus
, m
);
2207 typedef struct TransferInfo
{
2215 static int compare_transfer_info(const void *a
, const void *b
) {
2216 const TransferInfo
*x
= a
, *y
= b
;
2218 return strcmp(x
->local
, y
->local
);
2221 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2222 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
2223 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
2224 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2225 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2226 size_t n_transfers
= 0, n_allocated
= 0, j
;
2227 const char *type
, *remote
, *local
, *object
;
2228 sd_bus
*bus
= userdata
;
2229 uint32_t id
, max_id
= 0;
2233 pager_open_if_enabled();
2235 r
= sd_bus_call_method(
2237 "org.freedesktop.import1",
2238 "/org/freedesktop/import1",
2239 "org.freedesktop.import1.Manager",
2245 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2249 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2251 return bus_log_parse_error(r
);
2253 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2256 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2259 transfers
[n_transfers
].id
= id
;
2260 transfers
[n_transfers
].type
= type
;
2261 transfers
[n_transfers
].remote
= remote
;
2262 transfers
[n_transfers
].local
= local
;
2263 transfers
[n_transfers
].progress
= progress
;
2283 return bus_log_parse_error(r
);
2285 r
= sd_bus_message_exit_container(reply
);
2287 return bus_log_parse_error(r
);
2289 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2292 printf("%-*s %-*s %-*s %-*s %-*s\n",
2293 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2295 (int) max_type
, "TYPE",
2296 (int) max_local
, "LOCAL",
2297 (int) max_remote
, "REMOTE");
2299 for (j
= 0; j
< n_transfers
; j
++)
2300 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2301 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2302 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2303 (int) max_type
, transfers
[j
].type
,
2304 (int) max_local
, transfers
[j
].local
,
2305 (int) max_remote
, transfers
[j
].remote
);
2308 printf("\n%zu transfers listed.\n", n_transfers
);
2313 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2314 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2315 sd_bus
*bus
= userdata
;
2320 polkit_agent_open_if_enabled();
2322 for (i
= 1; i
< argc
; i
++) {
2325 r
= safe_atou32(argv
[i
], &id
);
2327 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2329 r
= sd_bus_call_method(
2331 "org.freedesktop.import1",
2332 "/org/freedesktop/import1",
2333 "org.freedesktop.import1.Manager",
2339 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2347 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2348 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2349 sd_bus
*bus
= userdata
;
2353 if (streq(argv
[argc
-1], "-"))
2354 limit
= (uint64_t) -1;
2358 r
= parse_size(argv
[argc
-1], 1024, &off
);
2360 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2362 limit
= (uint64_t) off
;
2366 /* With two arguments changes the quota limit of the
2367 * specified image */
2368 r
= sd_bus_call_method(
2370 "org.freedesktop.machine1",
2371 "/org/freedesktop/machine1",
2372 "org.freedesktop.machine1.Manager",
2376 "st", argv
[1], limit
);
2378 /* With one argument changes the pool quota limit */
2379 r
= sd_bus_call_method(
2381 "org.freedesktop.machine1",
2382 "/org/freedesktop/machine1",
2383 "org.freedesktop.machine1.Manager",
2390 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2397 static int help(int argc
, char *argv
[], void *userdata
) {
2399 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2400 "Send control commands to or query the virtual machine and container\n"
2401 "registration manager.\n\n"
2402 " -h --help Show this help\n"
2403 " --version Show package version\n"
2404 " --no-pager Do not pipe output into a pager\n"
2405 " --no-legend Do not show the headers and footers\n"
2406 " --no-ask-password Do not ask for system passwords\n"
2407 " -H --host=[USER@]HOST Operate on remote host\n"
2408 " -M --machine=CONTAINER Operate on local container\n"
2409 " -p --property=NAME Show only properties by this name\n"
2410 " -q --quiet Suppress output\n"
2411 " -a --all Show all properties, including empty ones\n"
2412 " -l --full Do not ellipsize output\n"
2413 " --kill-who=WHO Who to send signal to\n"
2414 " -s --signal=SIGNAL Which signal to send\n"
2415 " --uid=USER Specify user ID to invoke shell as\n"
2416 " --setenv=VAR=VALUE Add an environment variable for shell\n"
2417 " --read-only Create read-only bind mount\n"
2418 " --mkdir Create directory before bind mounting, if missing\n"
2419 " -n --lines=INTEGER Number of journal entries to show\n"
2420 " -o --output=STRING Change journal output mode (short,\n"
2421 " short-monotonic, verbose, export, json,\n"
2422 " json-pretty, json-sse, cat)\n"
2423 " --verify=MODE Verification mode for downloaded images (no,\n"
2424 " checksum, signature)\n"
2425 " --force Download image even if already exists\n"
2426 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2428 "Machine Commands:\n"
2429 " list List running VMs and containers\n"
2430 " status NAME... Show VM/container details\n"
2431 " show NAME... Show properties of one or more VMs/containers\n"
2432 " start NAME... Start container as a service\n"
2433 " login NAME Get a login prompt on a container\n"
2434 " shell NAME [COMMAND...] Invoke a shell (or other command) in a container\n"
2435 " enable NAME... Enable automatic container start at boot\n"
2436 " disable NAME... Disable automatic container start at boot\n"
2437 " poweroff NAME... Power off one or more containers\n"
2438 " reboot NAME... Reboot one or more containers\n"
2439 " terminate NAME... Terminate one or more VMs/containers\n"
2440 " kill NAME... Send signal to processes of a VM/container\n"
2441 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2442 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2443 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2445 " list-images Show available container and VM images\n"
2446 " image-status NAME... Show image details\n"
2447 " show-image NAME... Show properties of image\n"
2448 " clone NAME NAME Clone an image\n"
2449 " rename NAME NAME Rename an image\n"
2450 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2451 " remove NAME... Remove an image\n"
2452 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2453 "Image Transfer Commands:\n"
2454 " pull-tar URL [NAME] Download a TAR container image\n"
2455 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2456 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2457 " import-tar FILE [NAME] Import a local TAR container image\n"
2458 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2459 " export-tar NAME [FILE] Export a TAR container image locally\n"
2460 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2461 " list-transfers Show list of downloads in progress\n"
2462 " cancel-transfer Cancel a download\n"
2463 , program_invocation_short_name
);
2468 static int parse_argv(int argc
, char *argv
[]) {
2471 ARG_VERSION
= 0x100,
2477 ARG_NO_ASK_PASSWORD
,
2486 static const struct option options
[] = {
2487 { "help", no_argument
, NULL
, 'h' },
2488 { "version", no_argument
, NULL
, ARG_VERSION
},
2489 { "property", required_argument
, NULL
, 'p' },
2490 { "all", no_argument
, NULL
, 'a' },
2491 { "full", no_argument
, NULL
, 'l' },
2492 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2493 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2494 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2495 { "signal", required_argument
, NULL
, 's' },
2496 { "host", required_argument
, NULL
, 'H' },
2497 { "machine", required_argument
, NULL
, 'M' },
2498 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2499 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2500 { "quiet", no_argument
, NULL
, 'q' },
2501 { "lines", required_argument
, NULL
, 'n' },
2502 { "output", required_argument
, NULL
, 'o' },
2503 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2504 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2505 { "force", no_argument
, NULL
, ARG_FORCE
},
2506 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
2507 { "format", required_argument
, NULL
, ARG_FORMAT
},
2508 { "uid", required_argument
, NULL
, ARG_UID
},
2509 { "setenv", required_argument
, NULL
, ARG_SETENV
},
2518 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:qn:o:", options
, NULL
)) >= 0)
2523 return help(0, NULL
, NULL
);
2526 puts(PACKAGE_STRING
);
2527 puts(SYSTEMD_FEATURES
);
2531 r
= strv_extend(&arg_property
, optarg
);
2535 /* If the user asked for a particular
2536 * property, show it to him, even if it is
2550 if (safe_atou(optarg
, &arg_lines
) < 0) {
2551 log_error("Failed to parse lines '%s'", optarg
);
2557 arg_output
= output_mode_from_string(optarg
);
2558 if (arg_output
< 0) {
2559 log_error("Unknown output '%s'.", optarg
);
2565 arg_no_pager
= true;
2573 arg_kill_who
= optarg
;
2577 arg_signal
= signal_from_string_try_harder(optarg
);
2578 if (arg_signal
< 0) {
2579 log_error("Failed to parse signal string %s.", optarg
);
2584 case ARG_NO_ASK_PASSWORD
:
2585 arg_ask_password
= false;
2589 arg_transport
= BUS_TRANSPORT_REMOTE
;
2594 arg_transport
= BUS_TRANSPORT_MACHINE
;
2599 arg_read_only
= true;
2611 arg_verify
= import_verify_from_string(optarg
);
2612 if (arg_verify
< 0) {
2613 log_error("Failed to parse --verify= setting: %s", optarg
);
2622 case ARG_DKR_INDEX_URL
:
2623 if (!http_url_is_valid(optarg
)) {
2624 log_error("Index URL is invalid: %s", optarg
);
2628 arg_dkr_index_url
= optarg
;
2632 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
2633 log_error("Unknown format: %s", optarg
);
2637 arg_format
= optarg
;
2645 if (!env_assignment_is_valid(optarg
)) {
2646 log_error("Environment assignment invalid: %s", optarg
);
2650 r
= strv_extend(&arg_setenv
, optarg
);
2659 assert_not_reached("Unhandled option");
2665 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
2667 static const Verb verbs
[] = {
2668 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2669 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
2670 { "list-images", VERB_ANY
, 1, 0, list_images
},
2671 { "status", 2, VERB_ANY
, 0, show_machine
},
2672 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
2673 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
2674 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
2675 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
2676 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
2677 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
2678 { "kill", 2, VERB_ANY
, 0, kill_machine
},
2679 { "login", 2, 2, 0, login_machine
},
2680 { "shell", 2, VERB_ANY
, 0, shell_machine
},
2681 { "bind", 3, 4, 0, bind_mount
},
2682 { "copy-to", 3, 4, 0, copy_files
},
2683 { "copy-from", 3, 4, 0, copy_files
},
2684 { "remove", 2, VERB_ANY
, 0, remove_image
},
2685 { "rename", 3, 3, 0, rename_image
},
2686 { "clone", 3, 3, 0, clone_image
},
2687 { "read-only", 2, 3, 0, read_only_image
},
2688 { "start", 2, VERB_ANY
, 0, start_machine
},
2689 { "enable", 2, VERB_ANY
, 0, enable_machine
},
2690 { "disable", 2, VERB_ANY
, 0, enable_machine
},
2691 { "import-tar", 2, 3, 0, import_tar
},
2692 { "import-raw", 2, 3, 0, import_raw
},
2693 { "export-tar", 2, 3, 0, export_tar
},
2694 { "export-raw", 2, 3, 0, export_raw
},
2695 { "pull-tar", 2, 3, 0, pull_tar
},
2696 { "pull-raw", 2, 3, 0, pull_raw
},
2697 { "pull-dkr", 2, 3, 0, pull_dkr
},
2698 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
2699 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
2700 { "set-limit", 2, 3, 0, set_limit
},
2704 return dispatch_verb(argc
, argv
, verbs
, bus
);
2707 int main(int argc
, char*argv
[]) {
2708 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
2711 setlocale(LC_ALL
, "");
2712 log_parse_environment();
2715 r
= parse_argv(argc
, argv
);
2719 r
= bus_open_transport(arg_transport
, arg_host
, false, &bus
);
2721 log_error_errno(r
, "Failed to create bus connection: %m");
2725 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
2727 r
= machinectl_main(argc
, argv
, bus
);
2731 polkit_agent_close();
2733 strv_free(arg_property
);
2734 strv_free(arg_setenv
);
2736 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;