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