]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machinectl.c
Merge pull request #7834 from jkloetzke/disable-watchdog
[thirdparty/systemd.git] / src / machine / machinectl.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
1ee306e1
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2013 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
3f6fd1ba 21#include <arpa/inet.h>
1ee306e1 22#include <errno.h>
3f6fd1ba 23#include <fcntl.h>
1ee306e1 24#include <getopt.h>
1ee306e1 25#include <locale.h>
f48e75cb 26#include <net/if.h>
3f6fd1ba
LP
27#include <netinet/in.h>
28#include <string.h>
785890ac 29#include <sys/mount.h>
3f6fd1ba
LP
30#include <sys/socket.h>
31#include <unistd.h>
eef46c37 32
a1da8583 33#include "sd-bus.h"
3f6fd1ba 34
b5efdb8a 35#include "alloc-util.h"
68ce459f 36#include "bus-common-errors.h"
a1da8583 37#include "bus-error.h"
a0e27019 38#include "bus-unit-util.h"
3f6fd1ba 39#include "bus-util.h"
1ee306e1 40#include "cgroup-show.h"
9d127096 41#include "cgroup-util.h"
f2cbe59e 42#include "copy.h"
3f6fd1ba 43#include "env-util.h"
3ffd4af2 44#include "fd-util.h"
3f6fd1ba 45#include "hostname-util.h"
3d7415f4 46#include "import-util.h"
3f6fd1ba
LP
47#include "log.h"
48#include "logs-show.h"
49#include "macro.h"
50#include "mkdir.h"
51#include "pager.h"
6bedfcbb 52#include "parse-util.h"
3f6fd1ba 53#include "path-util.h"
0b452006 54#include "process-util.h"
3f6fd1ba 55#include "ptyfwd.h"
9e29521e 56#include "sigbus.h"
24882e06 57#include "signal-util.h"
3f6fd1ba 58#include "spawn-polkit-agent.h"
9e29521e 59#include "stdio-util.h"
3f6fd1ba
LP
60#include "strv.h"
61#include "terminal-util.h"
62#include "unit-name.h"
63#include "util.h"
64#include "verbs.h"
49cf4170 65#include "web-util.h"
07b0b339
SK
66
67#define ALL_IP_ADDRESSES -1
1ee306e1
LP
68
69static char **arg_property = NULL;
70static bool arg_all = false;
85500523 71static bool arg_value = false;
1ee306e1
LP
72static bool arg_full = false;
73static bool arg_no_pager = false;
e56056e9 74static bool arg_legend = true;
1ee306e1
LP
75static const char *arg_kill_who = NULL;
76static int arg_signal = SIGTERM;
d21ed1ea 77static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
1ee306e1 78static char *arg_host = NULL;
785890ac
LP
79static bool arg_read_only = false;
80static bool arg_mkdir = false;
d8f52ed2 81static bool arg_quiet = false;
acf97e21 82static bool arg_ask_password = true;
8b0cc9a3
LP
83static unsigned arg_lines = 10;
84static OutputMode arg_output = OUTPUT_SHORT;
3d7415f4 85static bool arg_force = false;
6e18cc9f 86static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
587fec42 87static const char* arg_format = NULL;
c454426c
LP
88static const char *arg_uid = NULL;
89static char **arg_setenv = NULL;
07b0b339
SK
90static int arg_addrs = 1;
91
92static int print_addresses(sd_bus *bus, const char *name, int, const char *pr1, const char *pr2, int n_addr);
1ee306e1 93
8b0cc9a3
LP
94static OutputFlags get_output_flags(void) {
95 return
96 arg_all * OUTPUT_SHOW_ALL |
459b9f9f 97 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
40c9fe4c 98 colors_enabled() * OUTPUT_COLOR |
8b0cc9a3
LP
99 !arg_quiet * OUTPUT_WARN_CUTOFF;
100}
101
0b63e278
LP
102typedef struct MachineInfo {
103 const char *name;
104 const char *class;
105 const char *service;
e3fc2b02 106 char *os;
07b0b339 107 char *version_id;
0b63e278
LP
108} MachineInfo;
109
110static int compare_machine_info(const void *a, const void *b) {
111 const MachineInfo *x = a, *y = b;
112
113 return strcmp(x->name, y->name);
114}
115
e3fc2b02
SK
116static void clean_machine_info(MachineInfo *machines, size_t n_machines) {
117 size_t i;
118
119 if (!machines || n_machines == 0)
120 return;
121
07b0b339 122 for (i = 0; i < n_machines; i++) {
e3fc2b02 123 free(machines[i].os);
07b0b339
SK
124 free(machines[i].version_id);
125 }
e3fc2b02
SK
126 free(machines);
127}
128
9153b02b 129static int call_get_os_release(sd_bus *bus, const char *method, const char *name, const char *query, ...) {
e3fc2b02 130 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
07b0b339
SK
131 const char *k, *v, *iter, **query_res = NULL;
132 size_t count = 0, awaited_args = 0;
133 va_list ap;
e3fc2b02
SK
134 int r;
135
136 assert(bus);
137 assert(name);
07b0b339
SK
138 assert(query);
139
140 NULSTR_FOREACH(iter, query)
141 awaited_args++;
142 query_res = newa0(const char *, awaited_args);
e3fc2b02 143
9153b02b
LP
144 r = sd_bus_call_method(
145 bus,
146 "org.freedesktop.machine1",
147 "/org/freedesktop/machine1",
148 "org.freedesktop.machine1.Manager",
149 method,
150 NULL, &reply, "s", name);
e3fc2b02
SK
151 if (r < 0)
152 return r;
153
154 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
155 if (r < 0)
156 return bus_log_parse_error(r);
157
158 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
07b0b339
SK
159 count = 0;
160 NULSTR_FOREACH(iter, query) {
161 if (streq(k, iter)) {
162 query_res[count] = v;
163 break;
164 }
165 count++;
166 }
e3fc2b02
SK
167 }
168 if (r < 0)
169 return bus_log_parse_error(r);
170
171 r = sd_bus_message_exit_container(reply);
172 if (r < 0)
173 return bus_log_parse_error(r);
174
07b0b339
SK
175 va_start(ap, query);
176 for (count = 0; count < awaited_args; count++) {
177 char *val, **out;
178
179 out = va_arg(ap, char **);
180 assert(out);
181 if (query_res[count]) {
182 val = strdup(query_res[count]);
183 if (!val) {
184 va_end(ap);
9153b02b 185 return -ENOMEM;
07b0b339
SK
186 }
187 *out = val;
188 }
189 }
190 va_end(ap);
e3fc2b02
SK
191
192 return 0;
193}
194
56159e0d
LP
195static int list_machines(int argc, char *argv[], void *userdata) {
196
fbd0b64f
LP
197 size_t max_name = STRLEN("MACHINE"), max_class = STRLEN("CLASS"),
198 max_service = STRLEN("SERVICE"), max_os = STRLEN("OS"), max_version_id = STRLEN("VERSION");
4afd3348
LP
199 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
200 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
07b0b339 201 _cleanup_free_ char *prefix = NULL;
e3fc2b02 202 MachineInfo *machines = NULL;
a1da8583 203 const char *name, *class, *service, *object;
0b63e278 204 size_t n_machines = 0, n_allocated = 0, j;
56159e0d 205 sd_bus *bus = userdata;
1ee306e1
LP
206 int r;
207
56159e0d
LP
208 assert(bus);
209
ea4b98e6 210 pager_open(arg_no_pager, false);
1ee306e1 211
e138e7d7
ZJS
212 r = sd_bus_call_method(bus,
213 "org.freedesktop.machine1",
214 "/org/freedesktop/machine1",
215 "org.freedesktop.machine1.Manager",
216 "ListMachines",
217 &error,
218 &reply,
219 NULL);
a1da8583
TG
220 if (r < 0) {
221 log_error("Could not get machines: %s", bus_error_message(&error, -r));
1ee306e1 222 return r;
1ee306e1
LP
223 }
224
3d7415f4 225 r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
a1da8583 226 if (r < 0)
5b30bef8 227 return bus_log_parse_error(r);
a1da8583 228 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
0b63e278
LP
229 size_t l;
230
fee6d013
LP
231 if (name[0] == '.' && !arg_all)
232 continue;
233
4b241916 234 if (!GREEDY_REALLOC0(machines, n_allocated, n_machines + 1)) {
e3fc2b02
SK
235 r = log_oom();
236 goto out;
237 }
238
4b241916
LP
239 machines[n_machines].name = name;
240 machines[n_machines].class = class;
241 machines[n_machines].service = service;
242
243 (void) call_get_os_release(
9153b02b
LP
244 bus,
245 "GetMachineOSRelease",
246 name,
247 "ID\0"
248 "VERSION_ID\0",
07b0b339
SK
249 &machines[n_machines].os,
250 &machines[n_machines].version_id);
0b63e278
LP
251
252 l = strlen(name);
253 if (l > max_name)
254 max_name = l;
255
256 l = strlen(class);
257 if (l > max_class)
258 max_class = l;
259
260 l = strlen(service);
261 if (l > max_service)
262 max_service = l;
263
07b0b339 264 l = machines[n_machines].os ? strlen(machines[n_machines].os) : 1;
e3fc2b02
SK
265 if (l > max_os)
266 max_os = l;
267
07b0b339
SK
268 l = machines[n_machines].version_id ? strlen(machines[n_machines].version_id) : 1;
269 if (l > max_version_id)
270 max_version_id = l;
271
313cefa1 272 n_machines++;
1ee306e1 273 }
e3fc2b02
SK
274 if (r < 0) {
275 r = bus_log_parse_error(r);
276 goto out;
277 }
a1da8583
TG
278
279 r = sd_bus_message_exit_container(reply);
e3fc2b02
SK
280 if (r < 0) {
281 r = bus_log_parse_error(r);
282 goto out;
283 }
1ee306e1 284
0b63e278
LP
285 qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
286
fbd0b64f 287 /* Allocate for prefix max characters for all fields + spaces between them + STRLEN(",\n") */
07b0b339
SK
288 r = asprintf(&prefix, "%-*s",
289 (int) (max_name +
290 max_class +
291 max_service +
292 max_os +
fbd0b64f 293 max_version_id + 5 + STRLEN(",\n")),
07b0b339
SK
294 ",\n");
295 if (r < 0) {
296 r = log_oom();
297 goto out;
298 }
299
a912ab04 300 if (arg_legend && n_machines > 0)
07b0b339 301 printf("%-*s %-*s %-*s %-*s %-*s %s\n",
0b63e278
LP
302 (int) max_name, "MACHINE",
303 (int) max_class, "CLASS",
e3fc2b02 304 (int) max_service, "SERVICE",
07b0b339
SK
305 (int) max_os, "OS",
306 (int) max_version_id, "VERSION",
307 "ADDRESSES");
0b63e278 308
07b0b339
SK
309 for (j = 0; j < n_machines; j++) {
310 printf("%-*s %-*s %-*s %-*s %-*s ",
0b63e278
LP
311 (int) max_name, machines[j].name,
312 (int) max_class, machines[j].class,
07b0b339
SK
313 (int) max_service, strdash_if_empty(machines[j].service),
314 (int) max_os, strdash_if_empty(machines[j].os),
315 (int) max_version_id, strdash_if_empty(machines[j].version_id));
316
317 r = print_addresses(bus, machines[j].name, 0, "", prefix, arg_addrs);
7026a775
LP
318 if (r <= 0) /* error or no addresses defined? */
319 fputs("-\n", stdout);
320 else
321 fputc('\n', stdout);
07b0b339 322 }
0b63e278 323
f9b1947f
VV
324 if (arg_legend) {
325 if (n_machines > 0)
326 printf("\n%zu machines listed.\n", n_machines);
327 else
328 printf("No machines.\n");
329 }
1ee306e1 330
3c302cdd 331 r = 0;
e3fc2b02
SK
332out:
333 clean_machine_info(machines, n_machines);
334 return r;
1ee306e1
LP
335}
336
cd61c3bf
LP
337typedef struct ImageInfo {
338 const char *name;
339 const char *type;
340 bool read_only;
10f9c755
LP
341 usec_t crtime;
342 usec_t mtime;
b6b18498 343 uint64_t size;
cd61c3bf
LP
344} ImageInfo;
345
346static int compare_image_info(const void *a, const void *b) {
347 const ImageInfo *x = a, *y = b;
348
349 return strcmp(x->name, y->name);
350}
351
56159e0d 352static int list_images(int argc, char *argv[], void *userdata) {
cd61c3bf 353
4afd3348 354 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
fbd0b64f 355 size_t max_name = STRLEN("NAME"), max_type = STRLEN("TYPE"), max_size = STRLEN("USAGE"), max_crtime = STRLEN("CREATED"), max_mtime = STRLEN("MODIFIED");
4afd3348 356 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
cd61c3bf
LP
357 _cleanup_free_ ImageInfo *images = NULL;
358 size_t n_images = 0, n_allocated = 0, j;
359 const char *name, *type, *object;
56159e0d 360 sd_bus *bus = userdata;
b6b18498 361 uint64_t crtime, mtime, size;
10f9c755 362 int read_only, r;
cd61c3bf 363
56159e0d
LP
364 assert(bus);
365
ea4b98e6 366 pager_open(arg_no_pager, false);
cd61c3bf 367
e138e7d7
ZJS
368 r = sd_bus_call_method(bus,
369 "org.freedesktop.machine1",
370 "/org/freedesktop/machine1",
371 "org.freedesktop.machine1.Manager",
372 "ListImages",
373 &error,
374 &reply,
375 "");
cd61c3bf
LP
376 if (r < 0) {
377 log_error("Could not get images: %s", bus_error_message(&error, -r));
378 return r;
379 }
380
b6b18498 381 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
cd61c3bf
LP
382 if (r < 0)
383 return bus_log_parse_error(r);
384
b6b18498
LP
385 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
386 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
10f9c755 387 size_t l;
cd61c3bf
LP
388
389 if (name[0] == '.' && !arg_all)
390 continue;
391
392 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
393 return log_oom();
394
395 images[n_images].name = name;
396 images[n_images].type = type;
397 images[n_images].read_only = read_only;
10f9c755
LP
398 images[n_images].crtime = crtime;
399 images[n_images].mtime = mtime;
b6b18498 400 images[n_images].size = size;
10f9c755
LP
401
402 l = strlen(name);
403 if (l > max_name)
404 max_name = l;
cd61c3bf 405
10f9c755
LP
406 l = strlen(type);
407 if (l > max_type)
408 max_type = l;
cd61c3bf 409
10f9c755 410 if (crtime != 0) {
b6b18498 411 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
10f9c755
LP
412 if (l > max_crtime)
413 max_crtime = l;
414 }
415
416 if (mtime != 0) {
b6b18498 417 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
10f9c755
LP
418 if (l > max_mtime)
419 max_mtime = l;
420 }
cd61c3bf 421
b6b18498
LP
422 if (size != (uint64_t) -1) {
423 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
424 if (l > max_size)
425 max_size = l;
426 }
427
cd61c3bf
LP
428 n_images++;
429 }
430 if (r < 0)
431 return bus_log_parse_error(r);
432
433 r = sd_bus_message_exit_container(reply);
434 if (r < 0)
435 return bus_log_parse_error(r);
436
437 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
438
a912ab04 439 if (arg_legend && n_images > 0)
b6b18498 440 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
10f9c755
LP
441 (int) max_name, "NAME",
442 (int) max_type, "TYPE",
443 "RO",
c19de711 444 (int) max_size, "USAGE",
10f9c755
LP
445 (int) max_crtime, "CREATED",
446 (int) max_mtime, "MODIFIED");
cd61c3bf
LP
447
448 for (j = 0; j < n_images; j++) {
b6b18498 449 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
10f9c755 450
b6b18498 451 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
cd61c3bf
LP
452 (int) max_name, images[j].name,
453 (int) max_type, images[j].type,
1fc464f6 454 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_normal() : "",
b6b18498
LP
455 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
456 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
457 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
cd61c3bf
LP
458 }
459
f9b1947f
VV
460 if (arg_legend) {
461 if (n_images > 0)
462 printf("\n%zu images listed.\n", n_images);
463 else
464 printf("No images.\n");
465 }
cd61c3bf
LP
466
467 return 0;
468}
469
89f7c846 470static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
bc06be75 471 _cleanup_free_ char *cgroup = NULL;
4afd3348 472 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
8b0cc9a3 473 int r;
aa1936ea
LP
474 unsigned c;
475
476 assert(bus);
477 assert(unit);
478
bc06be75 479 r = show_cgroup_get_unit_path_and_warn(bus, unit, &cgroup);
a0e27019 480 if (r < 0)
bc06be75 481 return r;
aa1936ea 482
a0e27019 483 if (isempty(cgroup))
9d127096
LP
484 return 0;
485
aa1936ea
LP
486 c = columns();
487 if (c > 18)
488 c -= 18;
489 else
490 c = 0;
491
a0e27019
LP
492 r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error);
493 if (r == -EBADR) {
494
495 if (arg_transport == BUS_TRANSPORT_REMOTE)
496 return 0;
497
498 /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
499
500 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
501 return 0;
502
0ff308c8 503 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags());
a0e27019
LP
504 } else if (r < 0)
505 return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
506
aa1936ea
LP
507 return 0;
508}
509
07b0b339 510static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2, int n_addr) {
4afd3348 511 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
07b0b339
SK
512 _cleanup_free_ char *addresses = NULL;
513 bool truncate = false;
7026a775 514 unsigned n = 0;
878cd7e9
LP
515 int r;
516
517 assert(bus);
518 assert(name);
519 assert(prefix);
520 assert(prefix2);
521
522 r = sd_bus_call_method(bus,
523 "org.freedesktop.machine1",
524 "/org/freedesktop/machine1",
525 "org.freedesktop.machine1.Manager",
526 "GetMachineAddresses",
527 NULL,
528 &reply,
529 "s", name);
530 if (r < 0)
531 return r;
532
07b0b339
SK
533 addresses = strdup(prefix);
534 if (!addresses)
535 return log_oom();
536 prefix = "";
537
0dd25fb9 538 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
878cd7e9
LP
539 if (r < 0)
540 return bus_log_parse_error(r);
541
0dd25fb9
LP
542 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
543 int family;
878cd7e9
LP
544 const void *a;
545 size_t sz;
07b0b339 546 char buf_ifi[DECIMAL_STR_MAX(int) + 2], buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
878cd7e9 547
0dd25fb9 548 r = sd_bus_message_read(reply, "i", &family);
878cd7e9
LP
549 if (r < 0)
550 return bus_log_parse_error(r);
551
552 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
553 if (r < 0)
554 return bus_log_parse_error(r);
555
07b0b339
SK
556 if (n_addr != 0) {
557 if (family == AF_INET6 && ifi > 0)
558 xsprintf(buf_ifi, "%%%i", ifi);
559 else
560 strcpy(buf_ifi, "");
561
7026a775 562 if (!strextend(&addresses, prefix, inet_ntop(family, a, buffer, sizeof(buffer)), buf_ifi, NULL))
07b0b339
SK
563 return log_oom();
564 } else
565 truncate = true;
878cd7e9
LP
566
567 r = sd_bus_message_exit_container(reply);
568 if (r < 0)
569 return bus_log_parse_error(r);
570
571 if (prefix != prefix2)
572 prefix = prefix2;
07b0b339
SK
573
574 if (n_addr > 0)
575 n_addr -= 1;
7026a775
LP
576
577 n++;
878cd7e9
LP
578 }
579 if (r < 0)
580 return bus_log_parse_error(r);
581
582 r = sd_bus_message_exit_container(reply);
583 if (r < 0)
584 return bus_log_parse_error(r);
585
7026a775
LP
586 if (n > 0)
587 fprintf(stdout, "%s%s", addresses, truncate ? "..." : "");
588
589 return (int) n;
878cd7e9
LP
590}
591
9153b02b 592static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) {
e3fc2b02 593 _cleanup_free_ char *pretty = NULL;
717603e3
LP
594 int r;
595
596 assert(bus);
597 assert(name);
598 assert(prefix);
599
9153b02b 600 r = call_get_os_release(bus, method, name, "PRETTY_NAME\0", &pretty, NULL);
717603e3
LP
601 if (r < 0)
602 return r;
603
717603e3
LP
604 if (pretty)
605 printf("%s%s\n", prefix, pretty);
606
607 return 0;
608}
609
3401419b
LP
610static int print_uid_shift(sd_bus *bus, const char *name) {
611 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
612 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
613 uint32_t shift;
614 int r;
615
616 assert(bus);
617 assert(name);
618
619 r = sd_bus_call_method(bus,
620 "org.freedesktop.machine1",
621 "/org/freedesktop/machine1",
622 "org.freedesktop.machine1.Manager",
623 "GetMachineUIDShift",
624 &error,
625 &reply,
626 "s", name);
627 if (r < 0)
628 return log_debug_errno(r, "Failed to query UID/GID shift: %s", bus_error_message(&error, r));
629
630 r = sd_bus_message_read(reply, "u", &shift);
631 if (r < 0)
632 return r;
633
634 if (shift == 0) /* Don't show trivial mappings */
635 return 0;
636
637 printf(" UID Shift: %" PRIu32 "\n", shift);
638 return 0;
639}
640
1ee306e1 641typedef struct MachineStatusInfo {
9f6eb1cd 642 char *name;
1ee306e1 643 sd_id128_t id;
9f6eb1cd
KS
644 char *class;
645 char *service;
89f7c846 646 char *unit;
9f6eb1cd 647 char *root_directory;
1ee306e1 648 pid_t leader;
8b0cc9a3 649 struct dual_timestamp timestamp;
f48e75cb
LP
650 int *netif;
651 unsigned n_netif;
1ee306e1
LP
652} MachineStatusInfo;
653
e7e55dbd
DH
654static void machine_status_info_clear(MachineStatusInfo *info) {
655 if (info) {
656 free(info->name);
657 free(info->class);
658 free(info->service);
659 free(info->unit);
660 free(info->root_directory);
661 free(info->netif);
662 zero(*info);
663 }
664}
665
a1da8583 666static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
1ee306e1
LP
667 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
668 char since2[FORMAT_TIMESTAMP_MAX], *s2;
f48e75cb
LP
669 int ifi = -1;
670
56159e0d 671 assert(bus);
1ee306e1
LP
672 assert(i);
673
674 fputs(strna(i->name), stdout);
675
3bbaff3e 676 if (!sd_id128_is_null(i->id))
1ee306e1
LP
677 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
678 else
679 putchar('\n');
680
8b0cc9a3
LP
681 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
682 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
1ee306e1
LP
683
684 if (s1)
685 printf("\t Since: %s; %s\n", s2, s1);
686 else if (s2)
687 printf("\t Since: %s\n", s2);
688
689 if (i->leader > 0) {
690 _cleanup_free_ char *t = NULL;
691
692 printf("\t Leader: %u", (unsigned) i->leader);
693
694 get_process_comm(i->leader, &t);
695 if (t)
696 printf(" (%s)", t);
697
698 putchar('\n');
699 }
700
701 if (i->service) {
702 printf("\t Service: %s", i->service);
703
704 if (i->class)
705 printf("; class %s", i->class);
706
707 putchar('\n');
708 } else if (i->class)
709 printf("\t Class: %s\n", i->class);
710
1ee306e1
LP
711 if (i->root_directory)
712 printf("\t Root: %s\n", i->root_directory);
713
f48e75cb
LP
714 if (i->n_netif > 0) {
715 unsigned c;
716
717 fputs("\t Iface:", stdout);
718
719 for (c = 0; c < i->n_netif; c++) {
720 char name[IF_NAMESIZE+1] = "";
721
722 if (if_indextoname(i->netif[c], name)) {
723 fputc(' ', stdout);
724 fputs(name, stdout);
725
726 if (ifi < 0)
727 ifi = i->netif[c];
728 else
729 ifi = 0;
730 } else
731 printf(" %i", i->netif[c]);
732 }
733
734 fputc('\n', stdout);
735 }
736
7026a775
LP
737 if (print_addresses(bus, i->name, ifi,
738 "\t Address: ",
739 "\n\t ",
740 ALL_IP_ADDRESSES) > 0)
741 fputc('\n', stdout);
878cd7e9 742
9153b02b 743 print_os_release(bus, "GetMachineOSRelease", i->name, "\t OS: ");
717603e3 744
3401419b
LP
745 print_uid_shift(bus, i->name);
746
89f7c846
LP
747 if (i->unit) {
748 printf("\t Unit: %s\n", i->unit);
749 show_unit_cgroup(bus, i->unit, i->leader);
8b0cc9a3 750
ece174c5 751 if (arg_transport == BUS_TRANSPORT_LOCAL)
8b0cc9a3
LP
752
753 show_journal_by_unit(
754 stdout,
755 i->unit,
756 arg_output,
757 0,
758 i->timestamp.monotonic,
759 arg_lines,
760 0,
761 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
762 SD_JOURNAL_LOCAL_ONLY,
763 true,
764 NULL);
1ee306e1
LP
765 }
766}
767
f48e75cb
LP
768static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
769 MachineStatusInfo *i = userdata;
770 size_t l;
771 const void *v;
772 int r;
773
774 assert_cc(sizeof(int32_t) == sizeof(int));
775 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
776 if (r < 0)
777 return r;
e7e9b6bb
ZJS
778 if (r == 0)
779 return -EBADMSG;
f48e75cb
LP
780
781 i->n_netif = l / sizeof(int32_t);
782 i->netif = memdup(v, l);
783 if (!i->netif)
784 return -ENOMEM;
785
786 return 0;
787}
788
fefdc04b 789static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
a6c61602 790
9f6eb1cd 791 static const struct bus_properties_map map[] = {
8b0cc9a3
LP
792 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
793 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
794 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
795 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
796 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
797 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
798 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
799 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
800 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
801 { "NetworkInterfaces", "ai", map_netif, 0 },
9f6eb1cd
KS
802 {}
803 };
a6c61602 804
f9e0eefc 805 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
e7e55dbd 806 _cleanup_(machine_status_info_clear) MachineStatusInfo info = {};
a1da8583
TG
807 int r;
808
56159e0d
LP
809 assert(verb);
810 assert(bus);
1ee306e1
LP
811 assert(path);
812 assert(new_line);
813
9f6eb1cd
KS
814 r = bus_map_all_properties(bus,
815 "org.freedesktop.machine1",
816 path,
817 map,
f9e0eefc 818 &error,
9f6eb1cd 819 &info);
f647962d 820 if (r < 0)
f9e0eefc 821 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
1ee306e1 822
1ee306e1
LP
823 if (*new_line)
824 printf("\n");
1ee306e1
LP
825 *new_line = true;
826
9f6eb1cd 827 print_machine_status_info(bus, &info);
1ee306e1 828
9f6eb1cd
KS
829 return r;
830}
1ee306e1 831
fefdc04b 832static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
9f6eb1cd 833 int r;
1ee306e1 834
56159e0d
LP
835 assert(bus);
836 assert(path);
837 assert(new_line);
838
9f6eb1cd
KS
839 if (*new_line)
840 printf("\n");
1ee306e1 841
9f6eb1cd 842 *new_line = true;
a1da8583 843
85500523 844 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_value, arg_all);
a1da8583 845 if (r < 0)
da927ba9 846 log_error_errno(r, "Could not get properties: %m");
1ee306e1 847
a7893c6b 848 return r;
1ee306e1
LP
849}
850
fefdc04b 851static int show_machine(int argc, char *argv[], void *userdata) {
56159e0d 852
4afd3348
LP
853 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
854 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
9f6eb1cd 855 bool properties, new_line = false;
56159e0d
LP
856 sd_bus *bus = userdata;
857 int r = 0, i;
1ee306e1
LP
858
859 assert(bus);
1ee306e1 860
56159e0d 861 properties = !strstr(argv[0], "status");
1ee306e1 862
ea4b98e6 863 pager_open(arg_no_pager, false);
1ee306e1 864
56159e0d 865 if (properties && argc <= 1) {
a1da8583 866
9f6eb1cd 867 /* If no argument is specified, inspect the manager
1ee306e1 868 * itself */
fefdc04b 869 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
8c841f21 870 if (r < 0)
9f6eb1cd 871 return r;
1ee306e1
LP
872 }
873
56159e0d 874 for (i = 1; i < argc; i++) {
1ee306e1
LP
875 const char *path = NULL;
876
e138e7d7
ZJS
877 r = sd_bus_call_method(bus,
878 "org.freedesktop.machine1",
879 "/org/freedesktop/machine1",
880 "org.freedesktop.machine1.Manager",
881 "GetMachine",
882 &error,
883 &reply,
884 "s", argv[i]);
a1da8583
TG
885 if (r < 0) {
886 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
887 return r;
888 }
889
890 r = sd_bus_message_read(reply, "o", &path);
5b30bef8
LP
891 if (r < 0)
892 return bus_log_parse_error(r);
1ee306e1 893
9f6eb1cd 894 if (properties)
fefdc04b 895 r = show_machine_properties(bus, path, &new_line);
9f6eb1cd 896 else
fefdc04b
LP
897 r = show_machine_info(argv[0], bus, path, &new_line);
898 }
899
900 return r;
901}
902
6ca7d231
LP
903static int print_image_hostname(sd_bus *bus, const char *name) {
904 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
905 const char *hn;
906 int r;
907
908 r = sd_bus_call_method(
909 bus,
910 "org.freedesktop.machine1",
911 "/org/freedesktop/machine1",
912 "org.freedesktop.machine1.Manager",
913 "GetImageHostname",
914 NULL, &reply, "s", name);
915 if (r < 0)
916 return r;
917
918 r = sd_bus_message_read(reply, "s", &hn);
919 if (r < 0)
920 return r;
921
922 if (!isempty(hn))
923 printf("\tHostname: %s\n", hn);
924
925 return 0;
926}
927
928static int print_image_machine_id(sd_bus *bus, const char *name) {
929 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
930 sd_id128_t id = SD_ID128_NULL;
931 const void *p;
932 size_t size;
933 int r;
934
935 r = sd_bus_call_method(
936 bus,
937 "org.freedesktop.machine1",
938 "/org/freedesktop/machine1",
939 "org.freedesktop.machine1.Manager",
940 "GetImageMachineID",
941 NULL, &reply, "s", name);
942 if (r < 0)
943 return r;
944
945 r = sd_bus_message_read_array(reply, 'y', &p, &size);
946 if (r < 0)
947 return r;
948
949 if (size == sizeof(sd_id128_t))
950 memcpy(&id, p, size);
951
952 if (!sd_id128_is_null(id))
953 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
954
955 return 0;
956}
957
958static int print_image_machine_info(sd_bus *bus, const char *name) {
959 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
6ca7d231
LP
960 int r;
961
962 r = sd_bus_call_method(
963 bus,
964 "org.freedesktop.machine1",
965 "/org/freedesktop/machine1",
966 "org.freedesktop.machine1.Manager",
967 "GetImageMachineInfo",
968 NULL, &reply, "s", name);
969 if (r < 0)
970 return r;
971
972 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
973 if (r < 0)
974 return r;
975
976 for (;;) {
977 const char *p, *q;
978
979 r = sd_bus_message_read(reply, "{ss}", &p, &q);
980 if (r < 0)
981 return r;
982 if (r == 0)
983 break;
984
985 if (streq(p, "DEPLOYMENT"))
986 printf(" Deployment: %s\n", q);
987 }
988
989 r = sd_bus_message_exit_container(reply);
990 if (r < 0)
991 return r;
992
993 return 0;
994}
995
fefdc04b
LP
996typedef struct ImageStatusInfo {
997 char *name;
998 char *path;
999 char *type;
1000 int read_only;
1001 usec_t crtime;
1002 usec_t mtime;
c19de711 1003 uint64_t usage;
b6b18498 1004 uint64_t limit;
c19de711 1005 uint64_t usage_exclusive;
b6b18498 1006 uint64_t limit_exclusive;
fefdc04b
LP
1007} ImageStatusInfo;
1008
e7e55dbd 1009static void image_status_info_clear(ImageStatusInfo *info) {
6ca7d231
LP
1010 if (!info)
1011 return;
1012
1013 free(info->name);
1014 free(info->path);
1015 free(info->type);
1016 zero(*info);
e7e55dbd
DH
1017}
1018
fefdc04b
LP
1019static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
1020 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
1021 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
b6b18498
LP
1022 char bs[FORMAT_BYTES_MAX], *s3;
1023 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
fefdc04b
LP
1024
1025 assert(bus);
1026 assert(i);
1027
1028 if (i->name) {
1029 fputs(i->name, stdout);
1030 putchar('\n');
1031 }
1032
9a14fb62 1033 if (i->type)
fefdc04b
LP
1034 printf("\t Type: %s\n", i->type);
1035
1036 if (i->path)
1037 printf("\t Path: %s\n", i->path);
1038
6ca7d231
LP
1039 (void) print_image_hostname(bus, i->name);
1040 (void) print_image_machine_id(bus, i->name);
1041 (void) print_image_machine_info(bus, i->name);
1042
9153b02b
LP
1043 print_os_release(bus, "GetImageOSRelease", i->name, "\t OS: ");
1044
fefdc04b
LP
1045 printf("\t RO: %s%s%s\n",
1046 i->read_only ? ansi_highlight_red() : "",
1047 i->read_only ? "read-only" : "writable",
1fc464f6 1048 i->read_only ? ansi_normal() : "");
fefdc04b
LP
1049
1050 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
1051 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
b6b18498 1052 if (s1 && s2)
fefdc04b
LP
1053 printf("\t Created: %s; %s\n", s2, s1);
1054 else if (s2)
1055 printf("\t Created: %s\n", s2);
1056
1057 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
1058 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
b6b18498 1059 if (s1 && s2)
fefdc04b
LP
1060 printf("\tModified: %s; %s\n", s2, s1);
1061 else if (s2)
1062 printf("\tModified: %s\n", s2);
b6b18498 1063
c19de711
LP
1064 s3 = format_bytes(bs, sizeof(bs), i->usage);
1065 s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
b6b18498 1066 if (s3 && s4)
c19de711 1067 printf("\t Usage: %s (exclusive: %s)\n", s3, s4);
b6b18498 1068 else if (s3)
c19de711 1069 printf("\t Usage: %s\n", s3);
b6b18498
LP
1070
1071 s3 = format_bytes(bs, sizeof(bs), i->limit);
1072 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
1073 if (s3 && s4)
1074 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
1075 else if (s3)
1076 printf("\t Limit: %s\n", s3);
fefdc04b
LP
1077}
1078
160e3793 1079static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
fefdc04b
LP
1080
1081 static const struct bus_properties_map map[] = {
b6b18498
LP
1082 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
1083 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
1084 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
1085 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
1086 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
1087 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
c19de711 1088 { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
b6b18498 1089 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
c19de711 1090 { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
b6b18498 1091 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
fefdc04b
LP
1092 {}
1093 };
1094
f9e0eefc 1095 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
e7e55dbd 1096 _cleanup_(image_status_info_clear) ImageStatusInfo info = {};
fefdc04b
LP
1097 int r;
1098
fefdc04b
LP
1099 assert(bus);
1100 assert(path);
1101 assert(new_line);
1102
1103 r = bus_map_all_properties(bus,
1104 "org.freedesktop.machine1",
1105 path,
1106 map,
f9e0eefc 1107 &error,
fefdc04b
LP
1108 &info);
1109 if (r < 0)
f9e0eefc 1110 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
fefdc04b
LP
1111
1112 if (*new_line)
1113 printf("\n");
1114 *new_line = true;
1115
1116 print_image_status_info(bus, &info);
1117
fefdc04b
LP
1118 return r;
1119}
1120
160e3793
LP
1121typedef struct PoolStatusInfo {
1122 char *path;
1123 uint64_t usage;
1124 uint64_t limit;
1125} PoolStatusInfo;
1126
e7e55dbd
DH
1127static void pool_status_info_clear(PoolStatusInfo *info) {
1128 if (info) {
1129 free(info->path);
1130 zero(*info);
1131 info->usage = -1;
1132 info->limit = -1;
1133 }
1134}
1135
160e3793
LP
1136static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
1137 char bs[FORMAT_BYTES_MAX], *s;
1138
1139 if (i->path)
1140 printf("\t Path: %s\n", i->path);
1141
1142 s = format_bytes(bs, sizeof(bs), i->usage);
1143 if (s)
1144 printf("\t Usage: %s\n", s);
1145
1146 s = format_bytes(bs, sizeof(bs), i->limit);
1147 if (s)
1148 printf("\t Limit: %s\n", s);
1149}
1150
1151static int show_pool_info(sd_bus *bus) {
1152
1153 static const struct bus_properties_map map[] = {
1154 { "PoolPath", "s", NULL, offsetof(PoolStatusInfo, path) },
1155 { "PoolUsage", "t", NULL, offsetof(PoolStatusInfo, usage) },
1156 { "PoolLimit", "t", NULL, offsetof(PoolStatusInfo, limit) },
1157 {}
1158 };
1159
e7e55dbd 1160 _cleanup_(pool_status_info_clear) PoolStatusInfo info = {
160e3793
LP
1161 .usage = (uint64_t) -1,
1162 .limit = (uint64_t) -1,
1163 };
f9e0eefc
LP
1164
1165 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
160e3793
LP
1166 int r;
1167
1168 assert(bus);
1169
1170 r = bus_map_all_properties(bus,
1171 "org.freedesktop.machine1",
1172 "/org/freedesktop/machine1",
1173 map,
f9e0eefc 1174 &error,
160e3793
LP
1175 &info);
1176 if (r < 0)
f9e0eefc 1177 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
160e3793
LP
1178
1179 print_pool_status_info(bus, &info);
1180
160e3793
LP
1181 return 0;
1182}
1183
1184
fefdc04b
LP
1185static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
1186 int r;
1187
1188 assert(bus);
1189 assert(path);
1190 assert(new_line);
1191
1192 if (*new_line)
1193 printf("\n");
1194
1195 *new_line = true;
1196
85500523 1197 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_value, arg_all);
fefdc04b
LP
1198 if (r < 0)
1199 log_error_errno(r, "Could not get properties: %m");
1200
1201 return r;
1202}
1203
1204static int show_image(int argc, char *argv[], void *userdata) {
1205
4afd3348
LP
1206 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1207 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
fefdc04b
LP
1208 bool properties, new_line = false;
1209 sd_bus *bus = userdata;
1210 int r = 0, i;
1211
1212 assert(bus);
1213
1214 properties = !strstr(argv[0], "status");
1215
ea4b98e6 1216 pager_open(arg_no_pager, false);
fefdc04b 1217
160e3793 1218 if (argc <= 1) {
fefdc04b
LP
1219
1220 /* If no argument is specified, inspect the manager
1221 * itself */
160e3793
LP
1222
1223 if (properties)
1224 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
1225 else
1226 r = show_pool_info(bus);
fefdc04b
LP
1227 if (r < 0)
1228 return r;
1229 }
1230
1231 for (i = 1; i < argc; i++) {
1232 const char *path = NULL;
1233
1234 r = sd_bus_call_method(
90adaa25
LP
1235 bus,
1236 "org.freedesktop.machine1",
1237 "/org/freedesktop/machine1",
1238 "org.freedesktop.machine1.Manager",
1239 "GetImage",
1240 &error,
1241 &reply,
1242 "s", argv[i]);
fefdc04b
LP
1243 if (r < 0) {
1244 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
1245 return r;
1246 }
1247
1248 r = sd_bus_message_read(reply, "o", &path);
1249 if (r < 0)
1250 return bus_log_parse_error(r);
1251
1252 if (properties)
1253 r = show_image_properties(bus, path, &new_line);
1254 else
160e3793 1255 r = show_image_info(bus, path, &new_line);
1ee306e1
LP
1256 }
1257
9f6eb1cd 1258 return r;
1ee306e1
LP
1259}
1260
56159e0d 1261static int kill_machine(int argc, char *argv[], void *userdata) {
4afd3348 1262 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d 1263 sd_bus *bus = userdata;
90adaa25 1264 int r, i;
1ee306e1 1265
56159e0d 1266 assert(bus);
1ee306e1 1267
8a4b13c5 1268 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1269
1ee306e1
LP
1270 if (!arg_kill_who)
1271 arg_kill_who = "all";
1272
56159e0d 1273 for (i = 1; i < argc; i++) {
a1da8583 1274 r = sd_bus_call_method(
56159e0d
LP
1275 bus,
1276 "org.freedesktop.machine1",
1277 "/org/freedesktop/machine1",
1278 "org.freedesktop.machine1.Manager",
1279 "KillMachine",
1280 &error,
1281 NULL,
1282 "ssi", argv[i], arg_kill_who, arg_signal);
a1da8583
TG
1283 if (r < 0) {
1284 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
1ee306e1 1285 return r;
a1da8583 1286 }
1ee306e1
LP
1287 }
1288
1289 return 0;
1290}
1291
56159e0d 1292static int reboot_machine(int argc, char *argv[], void *userdata) {
1dba654b
LP
1293 arg_kill_who = "leader";
1294 arg_signal = SIGINT; /* sysvinit + systemd */
1ee306e1 1295
56159e0d 1296 return kill_machine(argc, argv, userdata);
1dba654b 1297}
1ee306e1 1298
56159e0d 1299static int poweroff_machine(int argc, char *argv[], void *userdata) {
1dba654b
LP
1300 arg_kill_who = "leader";
1301 arg_signal = SIGRTMIN+4; /* only systemd */
1ee306e1 1302
56159e0d 1303 return kill_machine(argc, argv, userdata);
1ee306e1
LP
1304}
1305
56159e0d 1306static int terminate_machine(int argc, char *argv[], void *userdata) {
4afd3348 1307 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d 1308 sd_bus *bus = userdata;
2723b3b5 1309 int r, i;
923d8fd3 1310
56159e0d 1311 assert(bus);
923d8fd3 1312
8a4b13c5 1313 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1314
56159e0d 1315 for (i = 1; i < argc; i++) {
923d8fd3
LP
1316 r = sd_bus_call_method(
1317 bus,
1318 "org.freedesktop.machine1",
1319 "/org/freedesktop/machine1",
1320 "org.freedesktop.machine1.Manager",
1dba654b 1321 "TerminateMachine",
923d8fd3 1322 &error,
1dba654b 1323 NULL,
56159e0d 1324 "s", argv[i]);
923d8fd3 1325 if (r < 0) {
1dba654b 1326 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
923d8fd3
LP
1327 return r;
1328 }
923d8fd3
LP
1329 }
1330
1331 return 0;
1332}
1333
0370612e 1334static int copy_files(int argc, char *argv[], void *userdata) {
4afd3348 1335 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3d87174d 1336 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1fe6fa16
RM
1337 _cleanup_free_ char *abs_host_path = NULL;
1338 char *dest, *host_path, *container_path;
0370612e
LP
1339 sd_bus *bus = userdata;
1340 bool copy_from;
785890ac
LP
1341 int r;
1342
1343 assert(bus);
0370612e 1344
8a4b13c5 1345 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2723b3b5 1346
0370612e 1347 copy_from = streq(argv[0], "copy-from");
1fe6fa16
RM
1348 dest = argv[3] ?: argv[2];
1349 host_path = copy_from ? dest : argv[2];
1350 container_path = copy_from ? argv[2] : dest;
1351
1352 if (!path_is_absolute(host_path)) {
0f474365
LP
1353 r = path_make_absolute_cwd(host_path, &abs_host_path);
1354 if (r < 0)
1355 return log_error_errno(r, "Failed to make path absolute: %m");
1356
1fe6fa16
RM
1357 host_path = abs_host_path;
1358 }
785890ac 1359
3d87174d 1360 r = sd_bus_message_new_method_call(
785890ac 1361 bus,
3d87174d 1362 &m,
785890ac
LP
1363 "org.freedesktop.machine1",
1364 "/org/freedesktop/machine1",
1365 "org.freedesktop.machine1.Manager",
3d87174d
LP
1366 copy_from ? "CopyFromMachine" : "CopyToMachine");
1367 if (r < 0)
1368 return bus_log_create_error(r);
1369
1370 r = sd_bus_message_append(
1371 m,
0370612e
LP
1372 "sss",
1373 argv[1],
1fe6fa16
RM
1374 copy_from ? container_path : host_path,
1375 copy_from ? host_path : container_path);
3d87174d
LP
1376 if (r < 0)
1377 return bus_log_create_error(r);
1378
1379 /* This is a slow operation, hence turn off any method call timeouts */
1380 r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
0f474365
LP
1381 if (r < 0)
1382 return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r));
f2cbe59e
LP
1383
1384 return 0;
1385}
1386
56159e0d 1387static int bind_mount(int argc, char *argv[], void *userdata) {
4afd3348 1388 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d 1389 sd_bus *bus = userdata;
785890ac
LP
1390 int r;
1391
56159e0d
LP
1392 assert(bus);
1393
8a4b13c5 1394 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2723b3b5 1395
90adaa25
LP
1396 r = sd_bus_call_method(
1397 bus,
1398 "org.freedesktop.machine1",
1399 "/org/freedesktop/machine1",
1400 "org.freedesktop.machine1.Manager",
1401 "BindMountMachine",
1402 &error,
1403 NULL,
1404 "sssbb",
1405 argv[1],
1406 argv[2],
1407 argv[3],
1408 arg_read_only,
1409 arg_mkdir);
785890ac 1410 if (r < 0) {
90adaa25
LP
1411 log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
1412 return r;
785890ac
LP
1413 }
1414
90adaa25 1415 return 0;
785890ac
LP
1416}
1417
19070062 1418static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
0ec5543c
LP
1419 PTYForward ** forward = (PTYForward**) userdata;
1420 int r;
1421
0ec5543c
LP
1422 assert(m);
1423 assert(forward);
1424
1425 if (*forward) {
1426 /* If the forwarder is already initialized, tell it to
da054c37
LP
1427 * exit on the next vhangup(), so that we still flush
1428 * out what might be queued and exit then. */
0ec5543c 1429
da054c37 1430 r = pty_forward_set_ignore_vhangup(*forward, false);
0ec5543c
LP
1431 if (r >= 0)
1432 return 0;
1433
da054c37 1434 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
0ec5543c
LP
1435 }
1436
1437 /* On error, or when the forwarder is not initialized yet, quit immediately */
19070062 1438 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
0ec5543c
LP
1439 return 0;
1440}
1441
ae3dde80 1442static int process_forward(sd_event *event, PTYForward **forward, int master, PTYForwardFlags flags, const char *name) {
c454426c
LP
1443 char last_char = 0;
1444 bool machine_died;
1445 int ret = 0, r;
1446
1447 assert(event);
1448 assert(master >= 0);
1449 assert(name);
1450
1451 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
1452
fbdec792
WS
1453 if (!arg_quiet) {
1454 if (streq(name, ".host"))
1455 log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
1456 else
1457 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
1458 }
c454426c
LP
1459
1460 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1461 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1462
ae3dde80 1463 r = pty_forward_new(event, master, flags, forward);
c454426c
LP
1464 if (r < 0)
1465 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1466
1467 r = sd_event_loop(event);
1468 if (r < 0)
1469 return log_error_errno(r, "Failed to run event loop: %m");
1470
1471 pty_forward_get_last_char(*forward, &last_char);
1472
1473 machine_died =
ae3dde80 1474 (flags & PTY_FORWARD_IGNORE_VHANGUP) &&
c454426c
LP
1475 pty_forward_get_ignore_vhangup(*forward) == 0;
1476
1477 *forward = pty_forward_free(*forward);
1478
1479 if (last_char != '\n')
1480 fputc('\n', stdout);
1481
fbdec792
WS
1482 if (!arg_quiet) {
1483 if (machine_died)
1484 log_info("Machine %s terminated.", name);
1485 else if (streq(name, ".host"))
1486 log_info("Connection to the local host terminated.");
1487 else
1488 log_info("Connection to machine %s terminated.", name);
1489 }
c454426c
LP
1490
1491 sd_event_get_exit_code(event, &ret);
1492 return ret;
1493}
1494
bc3bb330
ZJS
1495static int parse_machine_uid(const char *spec, const char **machine, char **uid) {
1496 /*
1497 * Whatever is specified in the spec takes priority over global arguments.
1498 */
1499 char *_uid = NULL;
1500 const char *_machine = NULL;
1501
1502 if (spec) {
1503 const char *at;
1504
1505 at = strchr(spec, '@');
1506 if (at) {
1507 if (at == spec)
1508 /* Do the same as ssh and refuse "@host". */
1509 return -EINVAL;
1510
1511 _machine = at + 1;
1512 _uid = strndup(spec, at - spec);
1513 if (!_uid)
1514 return -ENOMEM;
1515 } else
1516 _machine = spec;
1517 };
1518
1519 if (arg_uid && !_uid) {
1520 _uid = strdup(arg_uid);
1521 if (!_uid)
1522 return -ENOMEM;
1523 }
1524
1525 *uid = _uid;
1526 *machine = isempty(_machine) ? ".host" : _machine;
1527 return 0;
1528}
1529
56159e0d 1530static int login_machine(int argc, char *argv[], void *userdata) {
4afd3348
LP
1531 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1532 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
023fb90b 1533 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
4afd3348
LP
1534 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
1535 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
c454426c 1536 int master = -1, r;
56159e0d 1537 sd_bus *bus = userdata;
91913f58 1538 const char *pty, *match, *machine;
04d39279
LP
1539
1540 assert(bus);
04d39279 1541
c454426c
LP
1542 if (!strv_isempty(arg_setenv) || arg_uid) {
1543 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1544 return -EINVAL;
1545 }
1546
4c701096 1547 if (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) {
923d8fd3 1548 log_error("Login only supported on local machines.");
15411c0c 1549 return -EOPNOTSUPP;
04d39279
LP
1550 }
1551
8a4b13c5 1552 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1553
023fb90b 1554 r = sd_event_default(&event);
f647962d
MS
1555 if (r < 0)
1556 return log_error_errno(r, "Failed to get event loop: %m");
023fb90b
LP
1557
1558 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
1559 if (r < 0)
1560 return log_error_errno(r, "Failed to attach bus to event loop: %m");
023fb90b 1561
91913f58
LP
1562 machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1];
1563
63c372cb 1564 match = strjoina("type='signal',"
c454426c
LP
1565 "sender='org.freedesktop.machine1',"
1566 "path='/org/freedesktop/machine1',",
1567 "interface='org.freedesktop.machine1.Manager',"
1568 "member='MachineRemoved',"
91913f58 1569 "arg0='", machine, "'");
0ec5543c 1570
75152a4d 1571 r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward);
0ec5543c 1572 if (r < 0)
75152a4d 1573 return log_error_errno(r, "Failed to request machine removal match: %m");
0ec5543c 1574
2723b3b5
LP
1575 r = sd_bus_call_method(
1576 bus,
1577 "org.freedesktop.machine1",
1578 "/org/freedesktop/machine1",
1579 "org.freedesktop.machine1.Manager",
1580 "OpenMachineLogin",
1581 &error,
1582 &reply,
91913f58 1583 "s", machine);
40205d70 1584 if (r < 0) {
c454426c 1585 log_error("Failed to get login PTY: %s", bus_error_message(&error, -r));
785890ac 1586 return r;
40205d70 1587 }
04d39279 1588
40205d70
LP
1589 r = sd_bus_message_read(reply, "hs", &master, &pty);
1590 if (r < 0)
ee451d76 1591 return bus_log_parse_error(r);
04d39279 1592
ae3dde80 1593 return process_forward(event, &forward, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
c454426c 1594}
04d39279 1595
c454426c 1596static int shell_machine(int argc, char *argv[], void *userdata) {
4afd3348
LP
1597 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
1598 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
c454426c 1599 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
4afd3348
LP
1600 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
1601 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
c454426c
LP
1602 int master = -1, r;
1603 sd_bus *bus = userdata;
bc3bb330
ZJS
1604 const char *pty, *match, *machine, *path;
1605 _cleanup_free_ char *uid = NULL;
04d39279 1606
c454426c
LP
1607 assert(bus);
1608
4c701096 1609 if (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) {
c454426c
LP
1610 log_error("Shell only supported on local machines.");
1611 return -EOPNOTSUPP;
1612 }
1613
89fec318
LP
1614 /* Pass $TERM to shell session, if not explicitly specified. */
1615 if (!strv_find_prefix(arg_setenv, "TERM=")) {
1616 const char *t;
1617
1618 t = strv_find_prefix(environ, "TERM=");
1619 if (t) {
1620 if (strv_extend(&arg_setenv, t) < 0)
1621 return log_oom();
1622 }
1623 }
1624
8a4b13c5 1625 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
023fb90b 1626
c454426c 1627 r = sd_event_default(&event);
f647962d 1628 if (r < 0)
c454426c 1629 return log_error_errno(r, "Failed to get event loop: %m");
023fb90b 1630
c454426c 1631 r = sd_bus_attach_event(bus, event, 0);
f647962d 1632 if (r < 0)
c454426c 1633 return log_error_errno(r, "Failed to attach bus to event loop: %m");
04d39279 1634
bc3bb330
ZJS
1635 r = parse_machine_uid(argc >= 2 ? argv[1] : NULL, &machine, &uid);
1636 if (r < 0)
1637 return log_error_errno(r, "Failed to parse machine specification: %m");
91913f58 1638
c454426c
LP
1639 match = strjoina("type='signal',"
1640 "sender='org.freedesktop.machine1',"
1641 "path='/org/freedesktop/machine1',",
1642 "interface='org.freedesktop.machine1.Manager',"
1643 "member='MachineRemoved',"
91913f58 1644 "arg0='", machine, "'");
c7b7d449 1645
75152a4d 1646 r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward);
c454426c 1647 if (r < 0)
75152a4d 1648 return log_error_errno(r, "Failed to request machine removal match: %m");
023fb90b 1649
c454426c
LP
1650 r = sd_bus_message_new_method_call(
1651 bus,
1652 &m,
1653 "org.freedesktop.machine1",
1654 "/org/freedesktop/machine1",
1655 "org.freedesktop.machine1.Manager",
1656 "OpenMachineShell");
1657 if (r < 0)
1658 return bus_log_create_error(r);
04d39279 1659
91913f58
LP
1660 path = argc < 3 || isempty(argv[2]) ? NULL : argv[2];
1661
ef3100e9 1662 r = sd_bus_message_append(m, "sss", machine, uid, path);
c454426c
LP
1663 if (r < 0)
1664 return bus_log_create_error(r);
04d39279 1665
91913f58 1666 r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2);
c454426c
LP
1667 if (r < 0)
1668 return bus_log_create_error(r);
1669
1670 r = sd_bus_message_append_strv(m, arg_setenv);
1671 if (r < 0)
1672 return bus_log_create_error(r);
1673
1674 r = sd_bus_call(bus, m, 0, &error, &reply);
1675 if (r < 0) {
1676 log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r));
1677 return r;
1678 }
1679
1680 r = sd_bus_message_read(reply, "hs", &master, &pty);
1681 if (r < 0)
1682 return bus_log_parse_error(r);
1683
40e1f4ea 1684 return process_forward(event, &forward, master, 0, machine);
04d39279
LP
1685}
1686
08682124 1687static int remove_image(int argc, char *argv[], void *userdata) {
08682124 1688 sd_bus *bus = userdata;
ebd93cb6 1689 int r, i;
08682124
LP
1690
1691 assert(bus);
1692
8a4b13c5 1693 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1694
08682124 1695 for (i = 1; i < argc; i++) {
3d87174d
LP
1696 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1697 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1698
1699 r = sd_bus_message_new_method_call(
08682124 1700 bus,
3d87174d 1701 &m,
08682124
LP
1702 "org.freedesktop.machine1",
1703 "/org/freedesktop/machine1",
1704 "org.freedesktop.machine1.Manager",
3d87174d
LP
1705 "RemoveImage");
1706 if (r < 0)
1707 return bus_log_create_error(r);
1708
1709 r = sd_bus_message_append(m, "s", argv[i]);
1710 if (r < 0)
1711 return bus_log_create_error(r);
1712
1713 /* This is a slow operation, hence turn off any method call timeouts */
1714 r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
1715 if (r < 0)
1716 return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r));
08682124
LP
1717 }
1718
1719 return 0;
1720}
1721
ebd93cb6 1722static int rename_image(int argc, char *argv[], void *userdata) {
4afd3348 1723 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
ebd93cb6
LP
1724 sd_bus *bus = userdata;
1725 int r;
1726
8a4b13c5 1727 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1728
ebd93cb6
LP
1729 r = sd_bus_call_method(
1730 bus,
1731 "org.freedesktop.machine1",
1732 "/org/freedesktop/machine1",
1733 "org.freedesktop.machine1.Manager",
1734 "RenameImage",
1735 &error,
1736 NULL,
1737 "ss", argv[1], argv[2]);
1738 if (r < 0) {
1739 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1740 return r;
1741 }
1742
1743 return 0;
1744}
1745
1746static int clone_image(int argc, char *argv[], void *userdata) {
4afd3348 1747 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3d87174d 1748 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
ebd93cb6
LP
1749 sd_bus *bus = userdata;
1750 int r;
1751
8a4b13c5 1752 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1753
3d87174d 1754 r = sd_bus_message_new_method_call(
ebd93cb6 1755 bus,
3d87174d 1756 &m,
ebd93cb6
LP
1757 "org.freedesktop.machine1",
1758 "/org/freedesktop/machine1",
1759 "org.freedesktop.machine1.Manager",
3d87174d
LP
1760 "CloneImage");
1761 if (r < 0)
1762 return bus_log_create_error(r);
1763
1764 r = sd_bus_message_append(m, "ssb", argv[1], argv[2], arg_read_only);
1765 if (r < 0)
1766 return bus_log_create_error(r);
1767
1768 /* This is a slow operation, hence turn off any method call timeouts */
1769 r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
1770 if (r < 0)
1771 return log_error_errno(r, "Could not clone image: %s", bus_error_message(&error, r));
ebd93cb6
LP
1772
1773 return 0;
1774}
1775
1776static int read_only_image(int argc, char *argv[], void *userdata) {
4afd3348 1777 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
ebd93cb6
LP
1778 sd_bus *bus = userdata;
1779 int b = true, r;
1780
1781 if (argc > 2) {
1782 b = parse_boolean(argv[2]);
1783 if (b < 0) {
1784 log_error("Failed to parse boolean argument: %s", argv[2]);
1785 return -EINVAL;
1786 }
1787 }
1788
8a4b13c5 1789 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1790
ebd93cb6
LP
1791 r = sd_bus_call_method(
1792 bus,
1793 "org.freedesktop.machine1",
1794 "/org/freedesktop/machine1",
1795 "org.freedesktop.machine1.Manager",
1796 "MarkImageReadOnly",
1797 &error,
1798 NULL,
1799 "sb", argv[1], b);
1800 if (r < 0) {
1801 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1802 return r;
1803 }
1804
1805 return 0;
1806}
1807
68ce459f
LP
1808static int image_exists(sd_bus *bus, const char *name) {
1809 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1810 int r;
1811
1812 assert(bus);
1813 assert(name);
1814
1815 r = sd_bus_call_method(
1816 bus,
1817 "org.freedesktop.machine1",
1818 "/org/freedesktop/machine1",
1819 "org.freedesktop.machine1.Manager",
1820 "GetImage",
1821 &error,
1822 NULL,
1823 "s", name);
1824 if (r < 0) {
1825 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_IMAGE))
1826 return 0;
1827
1828 return log_error_errno(r, "Failed to check whether image %s exists: %s", name, bus_error_message(&error, -r));
1829 }
1830
1831 return 1;
1832}
1833
7410616c 1834static int make_service_name(const char *name, char **ret) {
7410616c
LP
1835 int r;
1836
1837 assert(name);
1838 assert(ret);
1839
1840 if (!machine_name_is_valid(name)) {
1841 log_error("Invalid machine name %s.", name);
1842 return -EINVAL;
1843 }
1844
1e9707d4 1845 r = unit_name_build("systemd-nspawn", name, ".service", ret);
7410616c
LP
1846 if (r < 0)
1847 return log_error_errno(r, "Failed to build unit name: %m");
1848
1849 return 0;
1850}
1851
ebd011d9 1852static int start_machine(int argc, char *argv[], void *userdata) {
4afd3348 1853 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
ebd011d9
LP
1854 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1855 sd_bus *bus = userdata;
1856 int r, i;
1857
1858 assert(bus);
1859
8a4b13c5 1860 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1861
ebd011d9
LP
1862 r = bus_wait_for_jobs_new(bus, &w);
1863 if (r < 0)
1864 return log_oom();
1865
1866 for (i = 1; i < argc; i++) {
4afd3348 1867 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
7410616c 1868 _cleanup_free_ char *unit = NULL;
ebd011d9
LP
1869 const char *object;
1870
7410616c
LP
1871 r = make_service_name(argv[i], &unit);
1872 if (r < 0)
1873 return r;
ebd011d9 1874
68ce459f
LP
1875 r = image_exists(bus, argv[i]);
1876 if (r < 0)
1877 return r;
1878 if (r == 0) {
1879 log_error("Machine image '%s' does not exist.", argv[1]);
1880 return -ENXIO;
1881 }
1882
2723b3b5 1883 r = sd_bus_call_method(
ebd011d9 1884 bus,
ebd011d9
LP
1885 "org.freedesktop.systemd1",
1886 "/org/freedesktop/systemd1",
1887 "org.freedesktop.systemd1.Manager",
2723b3b5
LP
1888 "StartUnit",
1889 &error,
1890 &reply,
1891 "ss", unit, "fail");
ebd011d9
LP
1892 if (r < 0) {
1893 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1894 return r;
1895 }
1896
1897 r = sd_bus_message_read(reply, "o", &object);
1898 if (r < 0)
1899 return bus_log_parse_error(r);
1900
1901 r = bus_wait_for_jobs_add(w, object);
1902 if (r < 0)
1903 return log_oom();
1904 }
1905
10ba4835 1906 r = bus_wait_for_jobs(w, arg_quiet, NULL);
ebd011d9
LP
1907 if (r < 0)
1908 return r;
1909
1910 return 0;
1911}
1912
d8f52ed2 1913static int enable_machine(int argc, char *argv[], void *userdata) {
4afd3348
LP
1914 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
1915 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
acc0269c
CH
1916 UnitFileChange *changes = NULL;
1917 unsigned n_changes = 0;
d8f52ed2
LP
1918 int carries_install_info = 0;
1919 const char *method = NULL;
1920 sd_bus *bus = userdata;
1921 int r, i;
1922
1923 assert(bus);
1924
8a4b13c5 1925 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1926
d8f52ed2
LP
1927 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1928
1929 r = sd_bus_message_new_method_call(
1930 bus,
1931 &m,
1932 "org.freedesktop.systemd1",
1933 "/org/freedesktop/systemd1",
1934 "org.freedesktop.systemd1.Manager",
1935 method);
1936 if (r < 0)
1937 return bus_log_create_error(r);
1938
d8f52ed2
LP
1939 r = sd_bus_message_open_container(m, 'a', "s");
1940 if (r < 0)
1941 return bus_log_create_error(r);
1942
1943 for (i = 1; i < argc; i++) {
90615ad7 1944 _cleanup_free_ char *unit = NULL;
d8f52ed2 1945
7410616c
LP
1946 r = make_service_name(argv[i], &unit);
1947 if (r < 0)
1948 return r;
d8f52ed2 1949
68ce459f
LP
1950 r = image_exists(bus, argv[i]);
1951 if (r < 0)
1952 return r;
1953 if (r == 0) {
1954 log_error("Machine image '%s' does not exist.", argv[1]);
1955 return -ENXIO;
1956 }
1957
d8f52ed2
LP
1958 r = sd_bus_message_append(m, "s", unit);
1959 if (r < 0)
1960 return bus_log_create_error(r);
1961 }
1962
1963 r = sd_bus_message_close_container(m);
1964 if (r < 0)
1965 return bus_log_create_error(r);
1966
1967 if (streq(argv[0], "enable"))
1968 r = sd_bus_message_append(m, "bb", false, false);
1969 else
1970 r = sd_bus_message_append(m, "b", false);
1971 if (r < 0)
1972 return bus_log_create_error(r);
1973
1974 r = sd_bus_call(bus, m, 0, &error, &reply);
1975 if (r < 0) {
1976 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1977 return r;
1978 }
1979
1980 if (streq(argv[0], "enable")) {
1981 r = sd_bus_message_read(reply, "b", carries_install_info);
1982 if (r < 0)
1983 return bus_log_parse_error(r);
1984 }
1985
acc0269c 1986 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes);
d8f52ed2 1987 if (r < 0)
acc0269c 1988 goto finish;
d8f52ed2 1989
2723b3b5 1990 r = sd_bus_call_method(
d8f52ed2 1991 bus,
d8f52ed2
LP
1992 "org.freedesktop.systemd1",
1993 "/org/freedesktop/systemd1",
1994 "org.freedesktop.systemd1.Manager",
2723b3b5
LP
1995 "Reload",
1996 &error,
1997 NULL,
1998 NULL);
d8f52ed2
LP
1999 if (r < 0) {
2000 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
acc0269c 2001 goto finish;
d8f52ed2
LP
2002 }
2003
acc0269c
CH
2004 r = 0;
2005
2006finish:
2007 unit_file_changes_free(changes, n_changes);
2008
2009 return r;
d8f52ed2
LP
2010}
2011
19070062 2012static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) {
6adf7b5e 2013 const char **our_path = userdata, *line;
3d7415f4
LP
2014 unsigned priority;
2015 int r;
2016
3d7415f4 2017 assert(m);
6adf7b5e 2018 assert(our_path);
3d7415f4
LP
2019
2020 r = sd_bus_message_read(m, "us", &priority, &line);
2021 if (r < 0) {
2022 bus_log_parse_error(r);
2023 return 0;
2024 }
2025
6adf7b5e 2026 if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
3d7415f4
LP
2027 return 0;
2028
2029 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
2030 return 0;
2031
2032 log_full(priority, "%s", line);
2033 return 0;
2034}
2035
19070062 2036static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
6adf7b5e 2037 const char **our_path = userdata, *path, *result;
3d7415f4
LP
2038 uint32_t id;
2039 int r;
2040
3d7415f4 2041 assert(m);
6adf7b5e 2042 assert(our_path);
3d7415f4
LP
2043
2044 r = sd_bus_message_read(m, "uos", &id, &path, &result);
2045 if (r < 0) {
2046 bus_log_parse_error(r);
2047 return 0;
2048 }
2049
6adf7b5e 2050 if (!streq_ptr(*our_path, path))
3d7415f4
LP
2051 return 0;
2052
19070062 2053 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done"));
6adf7b5e
LP
2054 return 0;
2055}
2056
2057static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
2058 assert(s);
2059 assert(si);
2060
2061 if (!arg_quiet)
cc98b302 2062 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
6adf7b5e
LP
2063
2064 sd_event_exit(sd_event_source_get_event(s), EINTR);
3d7415f4
LP
2065 return 0;
2066}
2067
587fec42 2068static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
4afd3348
LP
2069 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
2070 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2071 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2072 _cleanup_(sd_event_unrefp) sd_event* event = NULL;
6adf7b5e 2073 const char *path = NULL;
3d7415f4
LP
2074 uint32_t id;
2075 int r;
2076
2077 assert(bus);
2078 assert(m);
2079
8a4b13c5 2080 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
3d7415f4 2081
6adf7b5e
LP
2082 r = sd_event_default(&event);
2083 if (r < 0)
2084 return log_error_errno(r, "Failed to get event loop: %m");
2085
2086 r = sd_bus_attach_event(bus, event, 0);
2087 if (r < 0)
2088 return log_error_errno(r, "Failed to attach bus to event loop: %m");
2089
75152a4d 2090 r = sd_bus_match_signal_async(
3d7415f4
LP
2091 bus,
2092 &slot_job_removed,
75152a4d
LP
2093 "org.freedesktop.import1",
2094 "/org/freedesktop/import1",
2095 "org.freedesktop.import1.Manager",
2096 "TransferRemoved",
2097 match_transfer_removed, NULL, &path);
3d7415f4 2098 if (r < 0)
75152a4d 2099 return log_error_errno(r, "Failed to request match: %m");
3d7415f4 2100
75152a4d 2101 r = sd_bus_match_signal_async(
3d7415f4
LP
2102 bus,
2103 &slot_log_message,
75152a4d
LP
2104 "org.freedesktop.import1",
2105 NULL,
2106 "org.freedesktop.import1.Transfer",
2107 "LogMessage",
2108 match_log_message, NULL, &path);
3d7415f4 2109 if (r < 0)
75152a4d 2110 return log_error_errno(r, "Failed to request match: %m");
3d7415f4
LP
2111
2112 r = sd_bus_call(bus, m, 0, &error, &reply);
2113 if (r < 0) {
953d28cc 2114 log_error("Failed to transfer image: %s", bus_error_message(&error, -r));
3d7415f4
LP
2115 return r;
2116 }
2117
6adf7b5e 2118 r = sd_bus_message_read(reply, "uo", &id, &path);
3d7415f4
LP
2119 if (r < 0)
2120 return bus_log_parse_error(r);
2121
72c0a2c2 2122 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
3d7415f4 2123
6adf7b5e
LP
2124 if (!arg_quiet)
2125 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
3d7415f4 2126
6adf7b5e
LP
2127 sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
2128 sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
2129
2130 r = sd_event_loop(event);
2131 if (r < 0)
2132 return log_error_errno(r, "Failed to run event loop: %m");
3d7415f4 2133
6adf7b5e 2134 return -r;
3d7415f4
LP
2135}
2136
b6e676ce 2137static int import_tar(int argc, char *argv[], void *userdata) {
4afd3348 2138 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
b6e676ce
LP
2139 _cleanup_free_ char *ll = NULL;
2140 _cleanup_close_ int fd = -1;
2141 const char *local = NULL, *path = NULL;
2142 sd_bus *bus = userdata;
2143 int r;
2144
2145 assert(bus);
2146
2147 if (argc >= 2)
2148 path = argv[1];
2149 if (isempty(path) || streq(path, "-"))
2150 path = NULL;
2151
2152 if (argc >= 3)
2153 local = argv[2];
2154 else if (path)
2155 local = basename(path);
2156 if (isempty(local) || streq(local, "-"))
2157 local = NULL;
2158
2159 if (!local) {
2160 log_error("Need either path or local name.");
2161 return -EINVAL;
2162 }
2163
2164 r = tar_strip_suffixes(local, &ll);
2165 if (r < 0)
2166 return log_oom();
2167
2168 local = ll;
2169
2170 if (!machine_name_is_valid(local)) {
2171 log_error("Local name %s is not a suitable machine name.", local);
2172 return -EINVAL;
2173 }
2174
2175 if (path) {
2176 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
2177 if (fd < 0)
2178 return log_error_errno(errno, "Failed to open %s: %m", path);
2179 }
2180
2181 r = sd_bus_message_new_method_call(
2182 bus,
2183 &m,
2184 "org.freedesktop.import1",
2185 "/org/freedesktop/import1",
2186 "org.freedesktop.import1.Manager",
2187 "ImportTar");
2188 if (r < 0)
2189 return bus_log_create_error(r);
2190
2191 r = sd_bus_message_append(
2192 m,
2193 "hsbb",
2194 fd >= 0 ? fd : STDIN_FILENO,
2195 local,
2196 arg_force,
2197 arg_read_only);
2198 if (r < 0)
2199 return bus_log_create_error(r);
2200
587fec42 2201 return transfer_image_common(bus, m);
b6e676ce
LP
2202}
2203
2204static int import_raw(int argc, char *argv[], void *userdata) {
4afd3348 2205 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
b6e676ce
LP
2206 _cleanup_free_ char *ll = NULL;
2207 _cleanup_close_ int fd = -1;
2208 const char *local = NULL, *path = NULL;
2209 sd_bus *bus = userdata;
2210 int r;
2211
2212 assert(bus);
2213
2214 if (argc >= 2)
2215 path = argv[1];
2216 if (isempty(path) || streq(path, "-"))
2217 path = NULL;
2218
2219 if (argc >= 3)
2220 local = argv[2];
2221 else if (path)
2222 local = basename(path);
2223 if (isempty(local) || streq(local, "-"))
2224 local = NULL;
2225
2226 if (!local) {
2227 log_error("Need either path or local name.");
2228 return -EINVAL;
2229 }
2230
2231 r = raw_strip_suffixes(local, &ll);
2232 if (r < 0)
2233 return log_oom();
2234
2235 local = ll;
2236
2237 if (!machine_name_is_valid(local)) {
2238 log_error("Local name %s is not a suitable machine name.", local);
2239 return -EINVAL;
2240 }
2241
2242 if (path) {
2243 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
2244 if (fd < 0)
2245 return log_error_errno(errno, "Failed to open %s: %m", path);
2246 }
2247
2248 r = sd_bus_message_new_method_call(
2249 bus,
2250 &m,
2251 "org.freedesktop.import1",
2252 "/org/freedesktop/import1",
2253 "org.freedesktop.import1.Manager",
2254 "ImportRaw");
2255 if (r < 0)
2256 return bus_log_create_error(r);
2257
2258 r = sd_bus_message_append(
2259 m,
2260 "hsbb",
2261 fd >= 0 ? fd : STDIN_FILENO,
2262 local,
2263 arg_force,
2264 arg_read_only);
2265 if (r < 0)
2266 return bus_log_create_error(r);
2267
587fec42
LP
2268 return transfer_image_common(bus, m);
2269}
2270
2271static void determine_compression_from_filename(const char *p) {
2272 if (arg_format)
2273 return;
2274
2275 if (!p)
2276 return;
2277
2278 if (endswith(p, ".xz"))
2279 arg_format = "xz";
2280 else if (endswith(p, ".gz"))
2281 arg_format = "gzip";
2282 else if (endswith(p, ".bz2"))
2283 arg_format = "bzip2";
2284}
2285
2286static int export_tar(int argc, char *argv[], void *userdata) {
4afd3348 2287 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
587fec42
LP
2288 _cleanup_close_ int fd = -1;
2289 const char *local = NULL, *path = NULL;
2290 sd_bus *bus = userdata;
2291 int r;
2292
2293 assert(bus);
2294
2295 local = argv[1];
2296 if (!machine_name_is_valid(local)) {
2297 log_error("Machine name %s is not valid.", local);
2298 return -EINVAL;
2299 }
2300
2301 if (argc >= 3)
2302 path = argv[2];
2303 if (isempty(path) || streq(path, "-"))
2304 path = NULL;
2305
2306 if (path) {
2307 determine_compression_from_filename(path);
2308
2309 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
2310 if (fd < 0)
2311 return log_error_errno(errno, "Failed to open %s: %m", path);
2312 }
2313
2314 r = sd_bus_message_new_method_call(
2315 bus,
2316 &m,
2317 "org.freedesktop.import1",
2318 "/org/freedesktop/import1",
2319 "org.freedesktop.import1.Manager",
2320 "ExportTar");
2321 if (r < 0)
2322 return bus_log_create_error(r);
2323
2324 r = sd_bus_message_append(
2325 m,
2326 "shs",
2327 local,
2328 fd >= 0 ? fd : STDOUT_FILENO,
2329 arg_format);
2330 if (r < 0)
2331 return bus_log_create_error(r);
2332
2333 return transfer_image_common(bus, m);
2334}
2335
2336static int export_raw(int argc, char *argv[], void *userdata) {
4afd3348 2337 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
587fec42
LP
2338 _cleanup_close_ int fd = -1;
2339 const char *local = NULL, *path = NULL;
2340 sd_bus *bus = userdata;
2341 int r;
2342
2343 assert(bus);
2344
2345 local = argv[1];
2346 if (!machine_name_is_valid(local)) {
2347 log_error("Machine name %s is not valid.", local);
2348 return -EINVAL;
2349 }
2350
2351 if (argc >= 3)
2352 path = argv[2];
2353 if (isempty(path) || streq(path, "-"))
2354 path = NULL;
2355
2356 if (path) {
2357 determine_compression_from_filename(path);
2358
2359 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
2360 if (fd < 0)
2361 return log_error_errno(errno, "Failed to open %s: %m", path);
2362 }
2363
2364 r = sd_bus_message_new_method_call(
2365 bus,
2366 &m,
2367 "org.freedesktop.import1",
2368 "/org/freedesktop/import1",
2369 "org.freedesktop.import1.Manager",
2370 "ExportRaw");
2371 if (r < 0)
2372 return bus_log_create_error(r);
2373
2374 r = sd_bus_message_append(
2375 m,
2376 "shs",
2377 local,
2378 fd >= 0 ? fd : STDOUT_FILENO,
2379 arg_format);
2380 if (r < 0)
2381 return bus_log_create_error(r);
2382
2383 return transfer_image_common(bus, m);
b6e676ce
LP
2384}
2385
3d7415f4 2386static int pull_tar(int argc, char *argv[], void *userdata) {
4afd3348 2387 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
3d7415f4
LP
2388 _cleanup_free_ char *l = NULL, *ll = NULL;
2389 const char *local, *remote;
2390 sd_bus *bus = userdata;
2391 int r;
2392
2393 assert(bus);
2394
2395 remote = argv[1];
2396 if (!http_url_is_valid(remote)) {
2397 log_error("URL '%s' is not valid.", remote);
2398 return -EINVAL;
2399 }
2400
2401 if (argc >= 3)
2402 local = argv[2];
2403 else {
2404 r = import_url_last_component(remote, &l);
2405 if (r < 0)
2406 return log_error_errno(r, "Failed to get final component of URL: %m");
2407
2408 local = l;
2409 }
2410
2411 if (isempty(local) || streq(local, "-"))
2412 local = NULL;
2413
2414 if (local) {
2415 r = tar_strip_suffixes(local, &ll);
2416 if (r < 0)
b6e676ce 2417 return log_oom();
3d7415f4
LP
2418
2419 local = ll;
2420
2421 if (!machine_name_is_valid(local)) {
2422 log_error("Local name %s is not a suitable machine name.", local);
2423 return -EINVAL;
2424 }
2425 }
2426
2427 r = sd_bus_message_new_method_call(
2428 bus,
2429 &m,
2430 "org.freedesktop.import1",
2431 "/org/freedesktop/import1",
2432 "org.freedesktop.import1.Manager",
2433 "PullTar");
2434 if (r < 0)
2435 return bus_log_create_error(r);
2436
2437 r = sd_bus_message_append(
2438 m,
2439 "sssb",
2440 remote,
2441 local,
6e18cc9f 2442 import_verify_to_string(arg_verify),
3d7415f4
LP
2443 arg_force);
2444 if (r < 0)
2445 return bus_log_create_error(r);
2446
587fec42 2447 return transfer_image_common(bus, m);
3d7415f4
LP
2448}
2449
2450static int pull_raw(int argc, char *argv[], void *userdata) {
4afd3348 2451 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
3d7415f4
LP
2452 _cleanup_free_ char *l = NULL, *ll = NULL;
2453 const char *local, *remote;
2454 sd_bus *bus = userdata;
2455 int r;
2456
2457 assert(bus);
2458
2459 remote = argv[1];
2460 if (!http_url_is_valid(remote)) {
2461 log_error("URL '%s' is not valid.", remote);
2462 return -EINVAL;
2463 }
2464
2465 if (argc >= 3)
2466 local = argv[2];
2467 else {
2468 r = import_url_last_component(remote, &l);
2469 if (r < 0)
2470 return log_error_errno(r, "Failed to get final component of URL: %m");
2471
2472 local = l;
2473 }
2474
2475 if (isempty(local) || streq(local, "-"))
2476 local = NULL;
2477
2478 if (local) {
2479 r = raw_strip_suffixes(local, &ll);
2480 if (r < 0)
b6e676ce 2481 return log_oom();
3d7415f4
LP
2482
2483 local = ll;
2484
2485 if (!machine_name_is_valid(local)) {
2486 log_error("Local name %s is not a suitable machine name.", local);
2487 return -EINVAL;
2488 }
2489 }
2490
2491 r = sd_bus_message_new_method_call(
2492 bus,
2493 &m,
2494 "org.freedesktop.import1",
2495 "/org/freedesktop/import1",
2496 "org.freedesktop.import1.Manager",
2497 "PullRaw");
2498 if (r < 0)
2499 return bus_log_create_error(r);
2500
2501 r = sd_bus_message_append(
2502 m,
2503 "sssb",
2504 remote,
2505 local,
6e18cc9f 2506 import_verify_to_string(arg_verify),
3d7415f4
LP
2507 arg_force);
2508 if (r < 0)
2509 return bus_log_create_error(r);
2510
587fec42 2511 return transfer_image_common(bus, m);
3d7415f4
LP
2512}
2513
3d7415f4
LP
2514typedef struct TransferInfo {
2515 uint32_t id;
2516 const char *type;
2517 const char *remote;
2518 const char *local;
7079cfef 2519 double progress;
3d7415f4
LP
2520} TransferInfo;
2521
2522static int compare_transfer_info(const void *a, const void *b) {
2523 const TransferInfo *x = a, *y = b;
2524
2525 return strcmp(x->local, y->local);
2526}
2527
2528static int list_transfers(int argc, char *argv[], void *userdata) {
fbd0b64f 2529 size_t max_type = STRLEN("TYPE"), max_local = STRLEN("LOCAL"), max_remote = STRLEN("REMOTE");
4afd3348
LP
2530 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2531 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3d7415f4
LP
2532 _cleanup_free_ TransferInfo *transfers = NULL;
2533 size_t n_transfers = 0, n_allocated = 0, j;
2534 const char *type, *remote, *local, *object;
2535 sd_bus *bus = userdata;
2536 uint32_t id, max_id = 0;
7079cfef 2537 double progress;
3d7415f4
LP
2538 int r;
2539
ea4b98e6 2540 pager_open(arg_no_pager, false);
3d7415f4 2541
e138e7d7
ZJS
2542 r = sd_bus_call_method(bus,
2543 "org.freedesktop.import1",
2544 "/org/freedesktop/import1",
2545 "org.freedesktop.import1.Manager",
2546 "ListTransfers",
2547 &error,
2548 &reply,
2549 NULL);
3d7415f4
LP
2550 if (r < 0) {
2551 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2552 return r;
2553 }
2554
7079cfef 2555 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
3d7415f4
LP
2556 if (r < 0)
2557 return bus_log_parse_error(r);
2558
7079cfef 2559 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
3d7415f4
LP
2560 size_t l;
2561
2562 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2563 return log_oom();
2564
2565 transfers[n_transfers].id = id;
2566 transfers[n_transfers].type = type;
2567 transfers[n_transfers].remote = remote;
2568 transfers[n_transfers].local = local;
7079cfef 2569 transfers[n_transfers].progress = progress;
3d7415f4
LP
2570
2571 l = strlen(type);
2572 if (l > max_type)
2573 max_type = l;
2574
2575 l = strlen(remote);
2576 if (l > max_remote)
2577 max_remote = l;
2578
2579 l = strlen(local);
2580 if (l > max_local)
2581 max_local = l;
2582
2583 if (id > max_id)
2584 max_id = id;
2585
313cefa1 2586 n_transfers++;
3d7415f4
LP
2587 }
2588 if (r < 0)
2589 return bus_log_parse_error(r);
2590
2591 r = sd_bus_message_exit_container(reply);
2592 if (r < 0)
2593 return bus_log_parse_error(r);
2594
2595 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2596
a912ab04 2597 if (arg_legend && n_transfers > 0)
7079cfef 2598 printf("%-*s %-*s %-*s %-*s %-*s\n",
3d7415f4 2599 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
7079cfef 2600 (int) 7, "PERCENT",
3d7415f4
LP
2601 (int) max_type, "TYPE",
2602 (int) max_local, "LOCAL",
2603 (int) max_remote, "REMOTE");
2604
2605 for (j = 0; j < n_transfers; j++)
7079cfef 2606 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
3d7415f4 2607 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
7079cfef 2608 (int) 6, (unsigned) (transfers[j].progress * 100),
3d7415f4
LP
2609 (int) max_type, transfers[j].type,
2610 (int) max_local, transfers[j].local,
2611 (int) max_remote, transfers[j].remote);
2612
f9b1947f
VV
2613 if (arg_legend) {
2614 if (n_transfers > 0)
2615 printf("\n%zu transfers listed.\n", n_transfers);
2616 else
2617 printf("No transfers.\n");
2618 }
3d7415f4
LP
2619
2620 return 0;
2621}
2622
2623static int cancel_transfer(int argc, char *argv[], void *userdata) {
4afd3348 2624 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3d7415f4
LP
2625 sd_bus *bus = userdata;
2626 int r, i;
2627
2628 assert(bus);
2629
8a4b13c5 2630 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
3d7415f4
LP
2631
2632 for (i = 1; i < argc; i++) {
2633 uint32_t id;
2634
2635 r = safe_atou32(argv[i], &id);
2636 if (r < 0)
2637 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2638
2639 r = sd_bus_call_method(
2640 bus,
2641 "org.freedesktop.import1",
2642 "/org/freedesktop/import1",
2643 "org.freedesktop.import1.Manager",
2644 "CancelTransfer",
2645 &error,
2646 NULL,
2647 "u", id);
2648 if (r < 0) {
2649 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2650 return r;
2651 }
2652 }
2653
2654 return 0;
2655}
2656
d6ce17c7 2657static int set_limit(int argc, char *argv[], void *userdata) {
4afd3348 2658 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
d6ce17c7
LP
2659 sd_bus *bus = userdata;
2660 uint64_t limit;
2661 int r;
2662
7705a405 2663 if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
d6ce17c7
LP
2664 limit = (uint64_t) -1;
2665 else {
59f448cf 2666 r = parse_size(argv[argc-1], 1024, &limit);
d6ce17c7
LP
2667 if (r < 0)
2668 return log_error("Failed to parse size: %s", argv[argc-1]);
d6ce17c7
LP
2669 }
2670
2671 if (argc > 2)
2672 /* With two arguments changes the quota limit of the
2673 * specified image */
2674 r = sd_bus_call_method(
2675 bus,
2676 "org.freedesktop.machine1",
2677 "/org/freedesktop/machine1",
2678 "org.freedesktop.machine1.Manager",
2679 "SetImageLimit",
2680 &error,
2681 NULL,
2682 "st", argv[1], limit);
2683 else
2684 /* With one argument changes the pool quota limit */
2685 r = sd_bus_call_method(
2686 bus,
2687 "org.freedesktop.machine1",
2688 "/org/freedesktop/machine1",
2689 "org.freedesktop.machine1.Manager",
2690 "SetPoolLimit",
2691 &error,
2692 NULL,
2693 "t", limit);
2694
2695 if (r < 0) {
2696 log_error("Could not set limit: %s", bus_error_message(&error, -r));
2697 return r;
2698 }
2699
2700 return 0;
2701}
2702
d94c2b06 2703static int clean_images(int argc, char *argv[], void *userdata) {
03c2b288 2704 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
d94c2b06
LP
2705 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2706 uint64_t usage, total = 0;
2707 char fb[FORMAT_BYTES_MAX];
2708 sd_bus *bus = userdata;
2709 const char *name;
2710 unsigned c = 0;
2711 int r;
2712
03c2b288 2713 r = sd_bus_message_new_method_call(
d94c2b06 2714 bus,
03c2b288 2715 &m,
d94c2b06
LP
2716 "org.freedesktop.machine1",
2717 "/org/freedesktop/machine1",
2718 "org.freedesktop.machine1.Manager",
03c2b288
LP
2719 "CleanPool");
2720 if (r < 0)
2721 return bus_log_create_error(r);
2722
2723 r = sd_bus_message_append(m, "s", arg_all ? "all" : "hidden");
2724 if (r < 0)
2725 return bus_log_create_error(r);
2726
2727 /* This is a slow operation, hence permit a longer time for completion. */
2728 r = sd_bus_call(bus, m, USEC_INFINITY, &error, &reply);
d94c2b06
LP
2729 if (r < 0)
2730 return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r));
2731
2732 r = sd_bus_message_enter_container(reply, 'a', "(st)");
2733 if (r < 0)
2734 return bus_log_parse_error(r);
2735
2736 while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) {
2737 log_info("Removed image '%s'. Freed exclusive disk space: %s",
2738 name, format_bytes(fb, sizeof(fb), usage));
2739
2740 total += usage;
2741 c++;
2742 }
2743
2744 r = sd_bus_message_exit_container(reply);
2745 if (r < 0)
2746 return bus_log_parse_error(r);
2747
2748 log_info("Removed %u images in total. Total freed exclusive disk space %s.",
2749 c, format_bytes(fb, sizeof(fb), total));
2750
2751 return 0;
2752}
2753
56159e0d 2754static int help(int argc, char *argv[], void *userdata) {
084f5805 2755 pager_open(arg_no_pager, false);
56159e0d 2756
1ee306e1 2757 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
f2cbe59e
LP
2758 "Send control commands to or query the virtual machine and container\n"
2759 "registration manager.\n\n"
2760 " -h --help Show this help\n"
2761 " --version Show package version\n"
2762 " --no-pager Do not pipe output into a pager\n"
2763 " --no-legend Do not show the headers and footers\n"
acf97e21 2764 " --no-ask-password Do not ask for system passwords\n"
f2cbe59e
LP
2765 " -H --host=[USER@]HOST Operate on remote host\n"
2766 " -M --machine=CONTAINER Operate on local container\n"
2767 " -p --property=NAME Show only properties by this name\n"
d8f52ed2 2768 " -q --quiet Suppress output\n"
f2cbe59e 2769 " -a --all Show all properties, including empty ones\n"
85500523 2770 " --value When showing properties, only print the value\n"
f2cbe59e
LP
2771 " -l --full Do not ellipsize output\n"
2772 " --kill-who=WHO Who to send signal to\n"
2773 " -s --signal=SIGNAL Which signal to send\n"
c454426c 2774 " --uid=USER Specify user ID to invoke shell as\n"
4d46e5db 2775 " -E --setenv=VAR=VALUE Add an environment variable for shell\n"
f2cbe59e 2776 " --read-only Create read-only bind mount\n"
8b0cc9a3
LP
2777 " --mkdir Create directory before bind mounting, if missing\n"
2778 " -n --lines=INTEGER Number of journal entries to show\n"
07b0b339 2779 " --max-addresses=INTEGER Number of internet addresses to show at most\n"
7e563bfc
IW
2780 " -o --output=STRING Change journal output mode (short, short-precise,\n"
2781 " short-iso, short-iso-precise, short-full,\n"
2782 " short-monotonic, short-unix, verbose, export,\n"
2783 " json, json-pretty, json-sse, cat)\n"
fc2288f0 2784 " --verify=MODE Verification mode for downloaded images (no,\n"
7f444afa 2785 " checksum, signature)\n"
fc2288f0 2786 " --force Download image even if already exists\n\n"
cd61c3bf 2787 "Machine Commands:\n"
f2cbe59e 2788 " list List running VMs and containers\n"
fefdc04b 2789 " status NAME... Show VM/container details\n"
91913f58 2790 " show [NAME...] Show properties of one or more VMs/containers\n"
ebd011d9 2791 " start NAME... Start container as a service\n"
91913f58
LP
2792 " login [NAME] Get a login prompt in a container or on the\n"
2793 " local host\n"
ef3100e9
LP
2794 " shell [[USER@]NAME [COMMAND...]]\n"
2795 " Invoke a shell (or other command) in a container\n"
2796 " or on the local host\n"
d8f52ed2
LP
2797 " enable NAME... Enable automatic container start at boot\n"
2798 " disable NAME... Disable automatic container start at boot\n"
f2cbe59e
LP
2799 " poweroff NAME... Power off one or more containers\n"
2800 " reboot NAME... Reboot one or more containers\n"
f2cbe59e 2801 " terminate NAME... Terminate one or more VMs/containers\n"
ebd93cb6 2802 " kill NAME... Send signal to processes of a VM/container\n"
f2cbe59e 2803 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
ebd93cb6
LP
2804 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2805 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
fefdc04b 2806 "Image Commands:\n"
56b921c3 2807 " list-images Show available container and VM images\n"
91913f58
LP
2808 " image-status [NAME...] Show image details\n"
2809 " show-image [NAME...] Show properties of image\n"
ebd93cb6
LP
2810 " clone NAME NAME Clone an image\n"
2811 " rename NAME NAME Rename an image\n"
2812 " read-only NAME [BOOL] Mark or unmark image read-only\n"
d6ce17c7 2813 " remove NAME... Remove an image\n"
5bda1f47
LP
2814 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
2815 " clean Remove hidden (or all) images\n\n"
b5b38b41
LP
2816 "Image Transfer Commands:\n"
2817 " pull-tar URL [NAME] Download a TAR container image\n"
2818 " pull-raw URL [NAME] Download a RAW container or VM image\n"
587fec42
LP
2819 " import-tar FILE [NAME] Import a local TAR container image\n"
2820 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
6e9efa59
LP
2821 " export-tar NAME [FILE] Export a TAR container image locally\n"
2822 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
b5b38b41 2823 " list-transfers Show list of downloads in progress\n"
3d7415f4 2824 " cancel-transfer Cancel a download\n"
f7621db0 2825 , program_invocation_short_name);
56159e0d
LP
2826
2827 return 0;
1ee306e1
LP
2828}
2829
2830static int parse_argv(int argc, char *argv[]) {
2831
2832 enum {
2833 ARG_VERSION = 0x100,
2834 ARG_NO_PAGER,
e56056e9 2835 ARG_NO_LEGEND,
85500523 2836 ARG_VALUE,
1ee306e1 2837 ARG_KILL_WHO,
785890ac
LP
2838 ARG_READ_ONLY,
2839 ARG_MKDIR,
acf97e21 2840 ARG_NO_ASK_PASSWORD,
3d7415f4
LP
2841 ARG_VERIFY,
2842 ARG_FORCE,
587fec42 2843 ARG_FORMAT,
c454426c 2844 ARG_UID,
07b0b339 2845 ARG_NUMBER_IPS,
1ee306e1
LP
2846 };
2847
2848 static const struct option options[] = {
2849 { "help", no_argument, NULL, 'h' },
2850 { "version", no_argument, NULL, ARG_VERSION },
2851 { "property", required_argument, NULL, 'p' },
2852 { "all", no_argument, NULL, 'a' },
85500523 2853 { "value", no_argument, NULL, ARG_VALUE },
1ee306e1
LP
2854 { "full", no_argument, NULL, 'l' },
2855 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
e56056e9 2856 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1ee306e1
LP
2857 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2858 { "signal", required_argument, NULL, 's' },
2859 { "host", required_argument, NULL, 'H' },
a7893c6b 2860 { "machine", required_argument, NULL, 'M' },
785890ac
LP
2861 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2862 { "mkdir", no_argument, NULL, ARG_MKDIR },
d8f52ed2 2863 { "quiet", no_argument, NULL, 'q' },
8b0cc9a3
LP
2864 { "lines", required_argument, NULL, 'n' },
2865 { "output", required_argument, NULL, 'o' },
acf97e21 2866 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
3d7415f4
LP
2867 { "verify", required_argument, NULL, ARG_VERIFY },
2868 { "force", no_argument, NULL, ARG_FORCE },
587fec42 2869 { "format", required_argument, NULL, ARG_FORMAT },
c454426c 2870 { "uid", required_argument, NULL, ARG_UID },
4d46e5db 2871 { "setenv", required_argument, NULL, 'E' },
07b0b339 2872 { "max-addresses", required_argument, NULL, ARG_NUMBER_IPS },
eb9da376 2873 {}
1ee306e1
LP
2874 };
2875
368d2643 2876 bool reorder = false;
768c1dec 2877 int c, r, shell = -1;
1ee306e1
LP
2878
2879 assert(argc >= 0);
2880 assert(argv);
2881
368d2643 2882 for (;;) {
61f638e5 2883 static const char option_string[] = "-hp:als:H:M:qn:o:E:";
368d2643 2884
0bf50960 2885 c = getopt_long(argc, argv, option_string + reorder, options, NULL);
768c1dec
LP
2886 if (c < 0)
2887 break;
2888
2889 switch (c) {
2890
2891 case 1: /* getopt_long() returns 1 if "-" was the first character of the option string, and a
2892 * non-option argument was discovered. */
2893
2894 assert(!reorder);
2895
368d2643
LP
2896 /* We generally are fine with the fact that getopt_long() reorders the command line, and looks
2897 * for switches after the main verb. However, for "shell" we really don't want that, since we
768c1dec
LP
2898 * want that switches specified after the machine name are passed to the program to execute,
2899 * and not processed by us. To make this possible, we'll first invoke getopt_long() with
2900 * reordering disabled (i.e. with the "-" prefix in the option string), looking for the first
2901 * non-option parameter. If it's the verb "shell" we remember its position and continue
2902 * processing options. In this case, as soon as we hit the next non-option argument we found
2903 * the machine name, and stop further processing. If the first non-option argument is any other
2904 * verb than "shell" we switch to normal reordering mode and continue processing arguments
2905 * normally. */
2906
2907 if (shell >= 0) {
2908 /* If we already found the "shell" verb on the command line, and now found the next
2909 * non-option argument, then this is the machine name and we should stop processing
2910 * further arguments. */
2911 optind --; /* don't process this argument, go one step back */
2912 goto done;
2913 }
2914 if (streq(optarg, "shell"))
2915 /* Remember the position of the "shell" verb, and continue processing normally. */
2916 shell = optind - 1;
2917 else {
2918 int saved_optind;
2919
2920 /* OK, this is some other verb. In this case, turn on reordering again, and continue
2921 * processing normally. */
368d2643 2922 reorder = true;
768c1dec
LP
2923
2924 /* We changed the option string. getopt_long() only looks at it again if we invoke it
2925 * at least once with a reset option index. Hence, let's reset the option index here,
2926 * then invoke getopt_long() again (ignoring what it has to say, after all we most
2927 * likely already processed it), and the bump the option index so that we read the
2928 * intended argument again. */
2929 saved_optind = optind;
2930 optind = 0;
2931 (void) getopt_long(argc, argv, option_string + reorder, options, NULL);
2932 optind = saved_optind - 1; /* go one step back, process this argument again */
368d2643
LP
2933 }
2934
2935 break;
1ee306e1
LP
2936
2937 case 'h':
56159e0d 2938 return help(0, NULL, NULL);
1ee306e1
LP
2939
2940 case ARG_VERSION:
3f6fd1ba 2941 return version();
1ee306e1 2942
a7893c6b
LP
2943 case 'p':
2944 r = strv_extend(&arg_property, optarg);
2945 if (r < 0)
2946 return log_oom();
1ee306e1
LP
2947
2948 /* If the user asked for a particular
2949 * property, show it to him, even if it is
2950 * empty. */
2951 arg_all = true;
2952 break;
1ee306e1
LP
2953
2954 case 'a':
2955 arg_all = true;
2956 break;
2957
85500523
ZJS
2958 case ARG_VALUE:
2959 arg_value = true;
2960 break;
2961
1ee306e1
LP
2962 case 'l':
2963 arg_full = true;
2964 break;
2965
8b0cc9a3
LP
2966 case 'n':
2967 if (safe_atou(optarg, &arg_lines) < 0) {
2968 log_error("Failed to parse lines '%s'", optarg);
2969 return -EINVAL;
2970 }
2971 break;
2972
2973 case 'o':
2974 arg_output = output_mode_from_string(optarg);
2975 if (arg_output < 0) {
2976 log_error("Unknown output '%s'.", optarg);
2977 return -EINVAL;
2978 }
2979 break;
2980
1ee306e1
LP
2981 case ARG_NO_PAGER:
2982 arg_no_pager = true;
2983 break;
2984
e56056e9
TA
2985 case ARG_NO_LEGEND:
2986 arg_legend = false;
2987 break;
2988
1ee306e1
LP
2989 case ARG_KILL_WHO:
2990 arg_kill_who = optarg;
2991 break;
2992
2993 case 's':
2994 arg_signal = signal_from_string_try_harder(optarg);
2995 if (arg_signal < 0) {
2996 log_error("Failed to parse signal string %s.", optarg);
2997 return -EINVAL;
2998 }
2999 break;
3000
acf97e21
LP
3001 case ARG_NO_ASK_PASSWORD:
3002 arg_ask_password = false;
3003 break;
3004
1ee306e1 3005 case 'H':
d21ed1ea 3006 arg_transport = BUS_TRANSPORT_REMOTE;
a7893c6b
LP
3007 arg_host = optarg;
3008 break;
3009
3010 case 'M':
de33fc62 3011 arg_transport = BUS_TRANSPORT_MACHINE;
a7893c6b 3012 arg_host = optarg;
1ee306e1
LP
3013 break;
3014
785890ac
LP
3015 case ARG_READ_ONLY:
3016 arg_read_only = true;
3017 break;
3018
3019 case ARG_MKDIR:
3020 arg_mkdir = true;
3021 break;
3022
d8f52ed2
LP
3023 case 'q':
3024 arg_quiet = true;
3025 break;
3026
3d7415f4 3027 case ARG_VERIFY:
6e18cc9f
LP
3028 arg_verify = import_verify_from_string(optarg);
3029 if (arg_verify < 0) {
3030 log_error("Failed to parse --verify= setting: %s", optarg);
3031 return -EINVAL;
3032 }
3d7415f4
LP
3033 break;
3034
3035 case ARG_FORCE:
3036 arg_force = true;
3037 break;
3038
587fec42
LP
3039 case ARG_FORMAT:
3040 if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
3041 log_error("Unknown format: %s", optarg);
3042 return -EINVAL;
3043 }
3044
3045 arg_format = optarg;
3046 break;
3047
c454426c
LP
3048 case ARG_UID:
3049 arg_uid = optarg;
3050 break;
3051
4d46e5db 3052 case 'E':
c454426c
LP
3053 if (!env_assignment_is_valid(optarg)) {
3054 log_error("Environment assignment invalid: %s", optarg);
3055 return -EINVAL;
3056 }
3057
3058 r = strv_extend(&arg_setenv, optarg);
3059 if (r < 0)
3060 return log_oom();
3061 break;
3062
07b0b339
SK
3063 case ARG_NUMBER_IPS:
3064 if (streq(optarg, "all"))
3065 arg_addrs = ALL_IP_ADDRESSES;
3066 else if (safe_atoi(optarg, &arg_addrs) < 0) {
3067 log_error("Invalid number of IPs");
3068 return -EINVAL;
3069 } else if (arg_addrs < 0) {
3070 log_error("Number of IPs cannot be negative");
3071 return -EINVAL;
3072 }
3073 break;
3074
1ee306e1
LP
3075 case '?':
3076 return -EINVAL;
3077
3078 default:
eb9da376 3079 assert_not_reached("Unhandled option");
1ee306e1 3080 }
368d2643 3081 }
1ee306e1 3082
768c1dec
LP
3083done:
3084 if (shell >= 0) {
3085 char *t;
3086 int i;
3087
3088 /* We found the "shell" verb while processing the argument list. Since we turned off reordering of the
3089 * argument list initially let's readjust it now, and move the "shell" verb to the back. */
3090
3091 optind -= 1; /* place the option index where the "shell" verb will be placed */
3092
3093 t = argv[shell];
3094 for (i = shell; i < optind; i++)
3095 argv[i] = argv[i+1];
3096 argv[optind] = t;
3097 }
3098
1ee306e1
LP
3099 return 1;
3100}
3101
56159e0d
LP
3102static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
3103
3104 static const Verb verbs[] = {
3d7415f4
LP
3105 { "help", VERB_ANY, VERB_ANY, 0, help },
3106 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
3107 { "list-images", VERB_ANY, 1, 0, list_images },
3108 { "status", 2, VERB_ANY, 0, show_machine },
160e3793 3109 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
3d7415f4
LP
3110 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
3111 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
3112 { "terminate", 2, VERB_ANY, 0, terminate_machine },
3113 { "reboot", 2, VERB_ANY, 0, reboot_machine },
3114 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
b2bb19bb 3115 { "stop", 2, VERB_ANY, 0, poweroff_machine }, /* Convenience alias */
3d7415f4 3116 { "kill", 2, VERB_ANY, 0, kill_machine },
91913f58
LP
3117 { "login", VERB_ANY, 2, 0, login_machine },
3118 { "shell", VERB_ANY, VERB_ANY, 0, shell_machine },
3d7415f4
LP
3119 { "bind", 3, 4, 0, bind_mount },
3120 { "copy-to", 3, 4, 0, copy_files },
3121 { "copy-from", 3, 4, 0, copy_files },
3122 { "remove", 2, VERB_ANY, 0, remove_image },
3123 { "rename", 3, 3, 0, rename_image },
3124 { "clone", 3, 3, 0, clone_image },
3125 { "read-only", 2, 3, 0, read_only_image },
3126 { "start", 2, VERB_ANY, 0, start_machine },
3127 { "enable", 2, VERB_ANY, 0, enable_machine },
3128 { "disable", 2, VERB_ANY, 0, enable_machine },
b6e676ce
LP
3129 { "import-tar", 2, 3, 0, import_tar },
3130 { "import-raw", 2, 3, 0, import_raw },
587fec42
LP
3131 { "export-tar", 2, 3, 0, export_tar },
3132 { "export-raw", 2, 3, 0, export_raw },
3d7415f4
LP
3133 { "pull-tar", 2, 3, 0, pull_tar },
3134 { "pull-raw", 2, 3, 0, pull_raw },
3d7415f4
LP
3135 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
3136 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
d6ce17c7 3137 { "set-limit", 2, 3, 0, set_limit },
d94c2b06 3138 { "clean", VERB_ANY, 1, 0, clean_images },
56159e0d 3139 {}
1ee306e1
LP
3140 };
3141
56159e0d 3142 return dispatch_verb(argc, argv, verbs, bus);
1ee306e1
LP
3143}
3144
3145int main(int argc, char*argv[]) {
a3c56345 3146 sd_bus *bus = NULL;
84f6181c 3147 int r;
1ee306e1
LP
3148
3149 setlocale(LC_ALL, "");
3150 log_parse_environment();
3151 log_open();
9e29521e 3152 sigbus_install();
1ee306e1
LP
3153
3154 r = parse_argv(argc, argv);
84f6181c 3155 if (r <= 0)
1ee306e1 3156 goto finish;
1ee306e1 3157
266f3e26 3158 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
a1da8583 3159 if (r < 0) {
da927ba9 3160 log_error_errno(r, "Failed to create bus connection: %m");
a1da8583
TG
3161 goto finish;
3162 }
1ee306e1 3163
2723b3b5
LP
3164 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
3165
56159e0d 3166 r = machinectl_main(argc, argv, bus);
1ee306e1
LP
3167
3168finish:
0a84daa5
FB
3169 /* make sure we terminate the bus connection first, and then close the
3170 * pager, see issue #3543 for the details. */
a3c56345 3171 sd_bus_flush_close_unref(bus);
1ee306e1 3172 pager_close();
acf97e21 3173 polkit_agent_close();
1ee306e1 3174
84f6181c 3175 strv_free(arg_property);
c454426c 3176 strv_free(arg_setenv);
84f6181c
LP
3177
3178 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1ee306e1 3179}