]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machinectl.c
nspawn: allow access to device nodes listed in --bind= and --bind-ro= switches
[thirdparty/systemd.git] / src / machine / machinectl.c
CommitLineData
1ee306e1
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
8bdbb8d9 22#include <sys/socket.h>
1ee306e1
LP
23#include <unistd.h>
24#include <errno.h>
25#include <string.h>
26#include <getopt.h>
1ee306e1 27#include <locale.h>
04d39279 28#include <fcntl.h>
878cd7e9
LP
29#include <netinet/in.h>
30#include <arpa/inet.h>
f48e75cb 31#include <net/if.h>
785890ac 32#include <sys/mount.h>
eef46c37 33
a1da8583 34#include "sd-bus.h"
1ee306e1
LP
35#include "log.h"
36#include "util.h"
37#include "macro.h"
38#include "pager.h"
acf97e21 39#include "spawn-polkit-agent.h"
a1da8583
TG
40#include "bus-util.h"
41#include "bus-error.h"
1ee306e1
LP
42#include "build.h"
43#include "strv.h"
aa1936ea 44#include "unit-name.h"
1ee306e1 45#include "cgroup-show.h"
8b0cc9a3 46#include "logs-show.h"
9d127096 47#include "cgroup-util.h"
04d39279 48#include "ptyfwd.h"
023fb90b 49#include "event-util.h"
785890ac
LP
50#include "path-util.h"
51#include "mkdir.h"
f2cbe59e 52#include "copy.h"
56159e0d 53#include "verbs.h"
3d7415f4 54#include "import-util.h"
0b452006 55#include "process-util.h"
288a74cc 56#include "terminal-util.h"
1ee306e1
LP
57
58static char **arg_property = NULL;
59static bool arg_all = false;
60static bool arg_full = false;
61static bool arg_no_pager = false;
e56056e9 62static bool arg_legend = true;
1ee306e1
LP
63static const char *arg_kill_who = NULL;
64static int arg_signal = SIGTERM;
d21ed1ea 65static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
1ee306e1 66static char *arg_host = NULL;
785890ac
LP
67static bool arg_read_only = false;
68static bool arg_mkdir = false;
d8f52ed2 69static bool arg_quiet = false;
acf97e21 70static bool arg_ask_password = true;
8b0cc9a3
LP
71static unsigned arg_lines = 10;
72static OutputMode arg_output = OUTPUT_SHORT;
3d7415f4 73static bool arg_force = false;
6e18cc9f 74static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
3d7415f4 75static const char* arg_dkr_index_url = NULL;
587fec42 76static const char* arg_format = NULL;
1ee306e1
LP
77
78static void pager_open_if_enabled(void) {
79
1ee306e1
LP
80 if (arg_no_pager)
81 return;
82
83 pager_open(false);
84}
85
acf97e21
LP
86static void polkit_agent_open_if_enabled(void) {
87
88 /* Open the polkit agent as a child process if necessary */
89
90 if (!arg_ask_password)
91 return;
92
93 if (arg_transport != BUS_TRANSPORT_LOCAL)
94 return;
95
96 polkit_agent_open();
97}
98
8b0cc9a3
LP
99static OutputFlags get_output_flags(void) {
100 return
101 arg_all * OUTPUT_SHOW_ALL |
102 arg_full * OUTPUT_FULL_WIDTH |
103 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
104 on_tty() * OUTPUT_COLOR |
105 !arg_quiet * OUTPUT_WARN_CUTOFF;
106}
107
0b63e278
LP
108typedef struct MachineInfo {
109 const char *name;
110 const char *class;
111 const char *service;
112} MachineInfo;
113
114static int compare_machine_info(const void *a, const void *b) {
115 const MachineInfo *x = a, *y = b;
116
117 return strcmp(x->name, y->name);
118}
119
56159e0d
LP
120static int list_machines(int argc, char *argv[], void *userdata) {
121
0b63e278 122 size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
a1da8583 123 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
0b63e278
LP
124 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
125 _cleanup_free_ MachineInfo *machines = NULL;
a1da8583 126 const char *name, *class, *service, *object;
0b63e278 127 size_t n_machines = 0, n_allocated = 0, j;
56159e0d 128 sd_bus *bus = userdata;
1ee306e1
LP
129 int r;
130
56159e0d
LP
131 assert(bus);
132
1ee306e1
LP
133 pager_open_if_enabled();
134
a1da8583
TG
135 r = sd_bus_call_method(
136 bus,
137 "org.freedesktop.machine1",
138 "/org/freedesktop/machine1",
139 "org.freedesktop.machine1.Manager",
140 "ListMachines",
141 &error,
142 &reply,
3d7415f4 143 NULL);
a1da8583
TG
144 if (r < 0) {
145 log_error("Could not get machines: %s", bus_error_message(&error, -r));
1ee306e1 146 return r;
1ee306e1
LP
147 }
148
3d7415f4 149 r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
a1da8583 150 if (r < 0)
5b30bef8 151 return bus_log_parse_error(r);
1ee306e1 152
a1da8583 153 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
0b63e278
LP
154 size_t l;
155
156 if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
157 return log_oom();
1ee306e1 158
0b63e278
LP
159 machines[n_machines].name = name;
160 machines[n_machines].class = class;
161 machines[n_machines].service = service;
162
163 l = strlen(name);
164 if (l > max_name)
165 max_name = l;
166
167 l = strlen(class);
168 if (l > max_class)
169 max_class = l;
170
171 l = strlen(service);
172 if (l > max_service)
173 max_service = l;
174
175 n_machines ++;
1ee306e1 176 }
a1da8583 177 if (r < 0)
5b30bef8 178 return bus_log_parse_error(r);
a1da8583
TG
179
180 r = sd_bus_message_exit_container(reply);
181 if (r < 0)
5b30bef8 182 return bus_log_parse_error(r);
1ee306e1 183
0b63e278
LP
184 qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
185
186 if (arg_legend)
187 printf("%-*s %-*s %-*s\n",
188 (int) max_name, "MACHINE",
189 (int) max_class, "CLASS",
190 (int) max_service, "SERVICE");
191
192 for (j = 0; j < n_machines; j++)
193 printf("%-*s %-*s %-*s\n",
194 (int) max_name, machines[j].name,
195 (int) max_class, machines[j].class,
196 (int) max_service, machines[j].service);
197
e56056e9 198 if (arg_legend)
0b63e278 199 printf("\n%zu machines listed.\n", n_machines);
1ee306e1
LP
200
201 return 0;
202}
203
cd61c3bf
LP
204typedef struct ImageInfo {
205 const char *name;
206 const char *type;
207 bool read_only;
10f9c755
LP
208 usec_t crtime;
209 usec_t mtime;
b6b18498 210 uint64_t size;
cd61c3bf
LP
211} ImageInfo;
212
213static int compare_image_info(const void *a, const void *b) {
214 const ImageInfo *x = a, *y = b;
215
216 return strcmp(x->name, y->name);
217}
218
56159e0d 219static int list_images(int argc, char *argv[], void *userdata) {
cd61c3bf
LP
220
221 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
c19de711 222 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("USAGE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
cd61c3bf
LP
223 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
224 _cleanup_free_ ImageInfo *images = NULL;
225 size_t n_images = 0, n_allocated = 0, j;
226 const char *name, *type, *object;
56159e0d 227 sd_bus *bus = userdata;
b6b18498 228 uint64_t crtime, mtime, size;
10f9c755 229 int read_only, r;
cd61c3bf 230
56159e0d
LP
231 assert(bus);
232
cd61c3bf
LP
233 pager_open_if_enabled();
234
235 r = sd_bus_call_method(
236 bus,
237 "org.freedesktop.machine1",
238 "/org/freedesktop/machine1",
239 "org.freedesktop.machine1.Manager",
240 "ListImages",
241 &error,
242 &reply,
243 "");
244 if (r < 0) {
245 log_error("Could not get images: %s", bus_error_message(&error, -r));
246 return r;
247 }
248
b6b18498 249 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
cd61c3bf
LP
250 if (r < 0)
251 return bus_log_parse_error(r);
252
b6b18498
LP
253 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
254 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
10f9c755 255 size_t l;
cd61c3bf
LP
256
257 if (name[0] == '.' && !arg_all)
258 continue;
259
260 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
261 return log_oom();
262
263 images[n_images].name = name;
264 images[n_images].type = type;
265 images[n_images].read_only = read_only;
10f9c755
LP
266 images[n_images].crtime = crtime;
267 images[n_images].mtime = mtime;
b6b18498 268 images[n_images].size = size;
10f9c755
LP
269
270 l = strlen(name);
271 if (l > max_name)
272 max_name = l;
cd61c3bf 273
10f9c755
LP
274 l = strlen(type);
275 if (l > max_type)
276 max_type = l;
cd61c3bf 277
10f9c755 278 if (crtime != 0) {
b6b18498 279 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
10f9c755
LP
280 if (l > max_crtime)
281 max_crtime = l;
282 }
283
284 if (mtime != 0) {
b6b18498 285 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
10f9c755
LP
286 if (l > max_mtime)
287 max_mtime = l;
288 }
cd61c3bf 289
b6b18498
LP
290 if (size != (uint64_t) -1) {
291 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
292 if (l > max_size)
293 max_size = l;
294 }
295
cd61c3bf
LP
296 n_images++;
297 }
298 if (r < 0)
299 return bus_log_parse_error(r);
300
301 r = sd_bus_message_exit_container(reply);
302 if (r < 0)
303 return bus_log_parse_error(r);
304
305 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
306
307 if (arg_legend)
b6b18498 308 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
10f9c755
LP
309 (int) max_name, "NAME",
310 (int) max_type, "TYPE",
311 "RO",
c19de711 312 (int) max_size, "USAGE",
10f9c755
LP
313 (int) max_crtime, "CREATED",
314 (int) max_mtime, "MODIFIED");
cd61c3bf
LP
315
316 for (j = 0; j < n_images; j++) {
b6b18498 317 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
10f9c755 318
b6b18498 319 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
cd61c3bf
LP
320 (int) max_name, images[j].name,
321 (int) max_type, images[j].type,
8937e7b6 322 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
b6b18498
LP
323 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
324 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
325 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
cd61c3bf
LP
326 }
327
cd61c3bf
LP
328 if (arg_legend)
329 printf("\n%zu images listed.\n", n_images);
330
331 return 0;
332}
333
89f7c846 334static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
a1da8583
TG
335 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
336 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
aa1936ea 337 _cleanup_free_ char *path = NULL;
aa1936ea 338 const char *cgroup;
8b0cc9a3 339 int r;
aa1936ea
LP
340 unsigned c;
341
342 assert(bus);
343 assert(unit);
344
d21ed1ea 345 if (arg_transport == BUS_TRANSPORT_REMOTE)
aa1936ea
LP
346 return 0;
347
348 path = unit_dbus_path_from_name(unit);
349 if (!path)
350 return log_oom();
351
a7893c6b 352 r = sd_bus_get_property(
aa1936ea
LP
353 bus,
354 "org.freedesktop.systemd1",
355 path,
89f7c846 356 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
a7893c6b 357 "ControlGroup",
aa1936ea 358 &error,
a1da8583 359 &reply,
a7893c6b 360 "s");
aa1936ea 361 if (r < 0) {
a1da8583 362 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
aa1936ea
LP
363 return r;
364 }
365
a7893c6b 366 r = sd_bus_message_read(reply, "s", &cgroup);
5b30bef8
LP
367 if (r < 0)
368 return bus_log_parse_error(r);
aa1936ea 369
9d127096
LP
370 if (isempty(cgroup))
371 return 0;
372
373 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
374 return 0;
375
aa1936ea
LP
376 c = columns();
377 if (c > 18)
378 c -= 18;
379 else
380 c = 0;
381
8b0cc9a3 382 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
aa1936ea
LP
383 return 0;
384}
385
f48e75cb 386static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
878cd7e9
LP
387 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
388 int r;
389
390 assert(bus);
391 assert(name);
392 assert(prefix);
393 assert(prefix2);
394
395 r = sd_bus_call_method(bus,
396 "org.freedesktop.machine1",
397 "/org/freedesktop/machine1",
398 "org.freedesktop.machine1.Manager",
399 "GetMachineAddresses",
400 NULL,
401 &reply,
402 "s", name);
403 if (r < 0)
404 return r;
405
0dd25fb9 406 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
878cd7e9
LP
407 if (r < 0)
408 return bus_log_parse_error(r);
409
0dd25fb9
LP
410 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
411 int family;
878cd7e9
LP
412 const void *a;
413 size_t sz;
414 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
415
0dd25fb9 416 r = sd_bus_message_read(reply, "i", &family);
878cd7e9
LP
417 if (r < 0)
418 return bus_log_parse_error(r);
419
420 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
421 if (r < 0)
422 return bus_log_parse_error(r);
423
f48e75cb
LP
424 fputs(prefix, stdout);
425 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
426 if (family == AF_INET6 && ifi > 0)
427 printf("%%%i", ifi);
428 fputc('\n', stdout);
878cd7e9
LP
429
430 r = sd_bus_message_exit_container(reply);
431 if (r < 0)
432 return bus_log_parse_error(r);
433
434 if (prefix != prefix2)
435 prefix = prefix2;
436 }
437 if (r < 0)
438 return bus_log_parse_error(r);
439
440 r = sd_bus_message_exit_container(reply);
441 if (r < 0)
442 return bus_log_parse_error(r);
443
444 return 0;
445}
446
717603e3
LP
447static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
448 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
449 const char *k, *v, *pretty = NULL;
450 int r;
451
452 assert(bus);
453 assert(name);
454 assert(prefix);
455
456 r = sd_bus_call_method(bus,
457 "org.freedesktop.machine1",
458 "/org/freedesktop/machine1",
459 "org.freedesktop.machine1.Manager",
460 "GetMachineOSRelease",
461 NULL,
462 &reply,
463 "s", name);
464 if (r < 0)
465 return r;
466
467 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
468 if (r < 0)
469 return bus_log_parse_error(r);
470
471 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
472 if (streq(k, "PRETTY_NAME"))
473 pretty = v;
474
475 }
476 if (r < 0)
477 return bus_log_parse_error(r);
478
479 r = sd_bus_message_exit_container(reply);
480 if (r < 0)
481 return bus_log_parse_error(r);
482
483 if (pretty)
484 printf("%s%s\n", prefix, pretty);
485
486 return 0;
487}
488
1ee306e1 489typedef struct MachineStatusInfo {
9f6eb1cd 490 char *name;
1ee306e1 491 sd_id128_t id;
9f6eb1cd
KS
492 char *class;
493 char *service;
89f7c846 494 char *unit;
9f6eb1cd 495 char *root_directory;
1ee306e1 496 pid_t leader;
8b0cc9a3 497 struct dual_timestamp timestamp;
f48e75cb
LP
498 int *netif;
499 unsigned n_netif;
1ee306e1
LP
500} MachineStatusInfo;
501
a1da8583 502static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
1ee306e1
LP
503 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
504 char since2[FORMAT_TIMESTAMP_MAX], *s2;
f48e75cb
LP
505 int ifi = -1;
506
56159e0d 507 assert(bus);
1ee306e1
LP
508 assert(i);
509
510 fputs(strna(i->name), stdout);
511
512 if (!sd_id128_equal(i->id, SD_ID128_NULL))
513 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
514 else
515 putchar('\n');
516
8b0cc9a3
LP
517 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
518 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
1ee306e1
LP
519
520 if (s1)
521 printf("\t Since: %s; %s\n", s2, s1);
522 else if (s2)
523 printf("\t Since: %s\n", s2);
524
525 if (i->leader > 0) {
526 _cleanup_free_ char *t = NULL;
527
528 printf("\t Leader: %u", (unsigned) i->leader);
529
530 get_process_comm(i->leader, &t);
531 if (t)
532 printf(" (%s)", t);
533
534 putchar('\n');
535 }
536
537 if (i->service) {
538 printf("\t Service: %s", i->service);
539
540 if (i->class)
541 printf("; class %s", i->class);
542
543 putchar('\n');
544 } else if (i->class)
545 printf("\t Class: %s\n", i->class);
546
1ee306e1
LP
547 if (i->root_directory)
548 printf("\t Root: %s\n", i->root_directory);
549
f48e75cb
LP
550 if (i->n_netif > 0) {
551 unsigned c;
552
553 fputs("\t Iface:", stdout);
554
555 for (c = 0; c < i->n_netif; c++) {
556 char name[IF_NAMESIZE+1] = "";
557
558 if (if_indextoname(i->netif[c], name)) {
559 fputc(' ', stdout);
560 fputs(name, stdout);
561
562 if (ifi < 0)
563 ifi = i->netif[c];
564 else
565 ifi = 0;
566 } else
567 printf(" %i", i->netif[c]);
568 }
569
570 fputc('\n', stdout);
571 }
572
573 print_addresses(bus, i->name, ifi,
878cd7e9
LP
574 "\t Address: ",
575 "\t ");
576
717603e3
LP
577 print_os_release(bus, i->name, "\t OS: ");
578
89f7c846
LP
579 if (i->unit) {
580 printf("\t Unit: %s\n", i->unit);
581 show_unit_cgroup(bus, i->unit, i->leader);
8b0cc9a3
LP
582
583 if (arg_transport == BUS_TRANSPORT_LOCAL) {
584
585 show_journal_by_unit(
586 stdout,
587 i->unit,
588 arg_output,
589 0,
590 i->timestamp.monotonic,
591 arg_lines,
592 0,
593 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
594 SD_JOURNAL_LOCAL_ONLY,
595 true,
596 NULL);
597 }
1ee306e1
LP
598 }
599}
600
f48e75cb
LP
601static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
602 MachineStatusInfo *i = userdata;
603 size_t l;
604 const void *v;
605 int r;
606
607 assert_cc(sizeof(int32_t) == sizeof(int));
608 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
609 if (r < 0)
610 return r;
e7e9b6bb
ZJS
611 if (r == 0)
612 return -EBADMSG;
f48e75cb
LP
613
614 i->n_netif = l / sizeof(int32_t);
615 i->netif = memdup(v, l);
616 if (!i->netif)
617 return -ENOMEM;
618
619 return 0;
620}
621
fefdc04b 622static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
a6c61602 623
9f6eb1cd 624 static const struct bus_properties_map map[] = {
8b0cc9a3
LP
625 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
626 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
627 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
628 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
629 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
630 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
631 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
632 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
633 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
634 { "NetworkInterfaces", "ai", map_netif, 0 },
9f6eb1cd
KS
635 {}
636 };
a6c61602
LP
637
638 MachineStatusInfo info = {};
a1da8583
TG
639 int r;
640
56159e0d
LP
641 assert(verb);
642 assert(bus);
1ee306e1
LP
643 assert(path);
644 assert(new_line);
645
9f6eb1cd
KS
646 r = bus_map_all_properties(bus,
647 "org.freedesktop.machine1",
648 path,
649 map,
650 &info);
f647962d
MS
651 if (r < 0)
652 return log_error_errno(r, "Could not get properties: %m");
1ee306e1 653
1ee306e1
LP
654 if (*new_line)
655 printf("\n");
1ee306e1
LP
656 *new_line = true;
657
9f6eb1cd 658 print_machine_status_info(bus, &info);
1ee306e1 659
9f6eb1cd
KS
660 free(info.name);
661 free(info.class);
662 free(info.service);
89f7c846 663 free(info.unit);
9f6eb1cd 664 free(info.root_directory);
f48e75cb 665 free(info.netif);
1ee306e1 666
9f6eb1cd
KS
667 return r;
668}
1ee306e1 669
fefdc04b 670static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
9f6eb1cd 671 int r;
1ee306e1 672
56159e0d
LP
673 assert(bus);
674 assert(path);
675 assert(new_line);
676
9f6eb1cd
KS
677 if (*new_line)
678 printf("\n");
1ee306e1 679
9f6eb1cd 680 *new_line = true;
a1da8583 681
27e72d6b 682 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
a1da8583 683 if (r < 0)
da927ba9 684 log_error_errno(r, "Could not get properties: %m");
1ee306e1 685
a7893c6b 686 return r;
1ee306e1
LP
687}
688
fefdc04b 689static int show_machine(int argc, char *argv[], void *userdata) {
56159e0d 690
a1da8583 691 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d 692 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
9f6eb1cd 693 bool properties, new_line = false;
56159e0d
LP
694 sd_bus *bus = userdata;
695 int r = 0, i;
1ee306e1
LP
696
697 assert(bus);
1ee306e1 698
56159e0d 699 properties = !strstr(argv[0], "status");
1ee306e1
LP
700
701 pager_open_if_enabled();
702
56159e0d 703 if (properties && argc <= 1) {
a1da8583 704
9f6eb1cd 705 /* If no argument is specified, inspect the manager
1ee306e1 706 * itself */
fefdc04b 707 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
8c841f21 708 if (r < 0)
9f6eb1cd 709 return r;
1ee306e1
LP
710 }
711
56159e0d 712 for (i = 1; i < argc; i++) {
1ee306e1
LP
713 const char *path = NULL;
714
a1da8583
TG
715 r = sd_bus_call_method(
716 bus,
717 "org.freedesktop.machine1",
718 "/org/freedesktop/machine1",
719 "org.freedesktop.machine1.Manager",
720 "GetMachine",
721 &error,
722 &reply,
56159e0d 723 "s", argv[i]);
a1da8583
TG
724 if (r < 0) {
725 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
726 return r;
727 }
728
729 r = sd_bus_message_read(reply, "o", &path);
5b30bef8
LP
730 if (r < 0)
731 return bus_log_parse_error(r);
1ee306e1 732
9f6eb1cd 733 if (properties)
fefdc04b 734 r = show_machine_properties(bus, path, &new_line);
9f6eb1cd 735 else
fefdc04b
LP
736 r = show_machine_info(argv[0], bus, path, &new_line);
737 }
738
739 return r;
740}
741
742typedef struct ImageStatusInfo {
743 char *name;
744 char *path;
745 char *type;
746 int read_only;
747 usec_t crtime;
748 usec_t mtime;
c19de711 749 uint64_t usage;
b6b18498 750 uint64_t limit;
c19de711 751 uint64_t usage_exclusive;
b6b18498 752 uint64_t limit_exclusive;
fefdc04b
LP
753} ImageStatusInfo;
754
755static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
756 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
757 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
b6b18498
LP
758 char bs[FORMAT_BYTES_MAX], *s3;
759 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
fefdc04b
LP
760
761 assert(bus);
762 assert(i);
763
764 if (i->name) {
765 fputs(i->name, stdout);
766 putchar('\n');
767 }
768
9a14fb62 769 if (i->type)
fefdc04b
LP
770 printf("\t Type: %s\n", i->type);
771
772 if (i->path)
773 printf("\t Path: %s\n", i->path);
774
775 printf("\t RO: %s%s%s\n",
776 i->read_only ? ansi_highlight_red() : "",
777 i->read_only ? "read-only" : "writable",
778 i->read_only ? ansi_highlight_off() : "");
779
780 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
781 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
b6b18498 782 if (s1 && s2)
fefdc04b
LP
783 printf("\t Created: %s; %s\n", s2, s1);
784 else if (s2)
785 printf("\t Created: %s\n", s2);
786
787 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
788 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
b6b18498 789 if (s1 && s2)
fefdc04b
LP
790 printf("\tModified: %s; %s\n", s2, s1);
791 else if (s2)
792 printf("\tModified: %s\n", s2);
b6b18498 793
c19de711
LP
794 s3 = format_bytes(bs, sizeof(bs), i->usage);
795 s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
b6b18498 796 if (s3 && s4)
c19de711 797 printf("\t Usage: %s (exclusive: %s)\n", s3, s4);
b6b18498 798 else if (s3)
c19de711 799 printf("\t Usage: %s\n", s3);
b6b18498
LP
800
801 s3 = format_bytes(bs, sizeof(bs), i->limit);
802 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
803 if (s3 && s4)
804 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
805 else if (s3)
806 printf("\t Limit: %s\n", s3);
fefdc04b
LP
807}
808
160e3793 809static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
fefdc04b
LP
810
811 static const struct bus_properties_map map[] = {
b6b18498
LP
812 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
813 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
814 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
815 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
816 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
817 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
c19de711 818 { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
b6b18498 819 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
c19de711 820 { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
b6b18498 821 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
fefdc04b
LP
822 {}
823 };
824
825 ImageStatusInfo info = {};
826 int r;
827
fefdc04b
LP
828 assert(bus);
829 assert(path);
830 assert(new_line);
831
832 r = bus_map_all_properties(bus,
833 "org.freedesktop.machine1",
834 path,
835 map,
836 &info);
837 if (r < 0)
838 return log_error_errno(r, "Could not get properties: %m");
839
840 if (*new_line)
841 printf("\n");
842 *new_line = true;
843
844 print_image_status_info(bus, &info);
845
846 free(info.name);
847 free(info.path);
848 free(info.type);
849
850 return r;
851}
852
160e3793
LP
853typedef struct PoolStatusInfo {
854 char *path;
855 uint64_t usage;
856 uint64_t limit;
857} PoolStatusInfo;
858
859static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
860 char bs[FORMAT_BYTES_MAX], *s;
861
862 if (i->path)
863 printf("\t Path: %s\n", i->path);
864
865 s = format_bytes(bs, sizeof(bs), i->usage);
866 if (s)
867 printf("\t Usage: %s\n", s);
868
869 s = format_bytes(bs, sizeof(bs), i->limit);
870 if (s)
871 printf("\t Limit: %s\n", s);
872}
873
874static int show_pool_info(sd_bus *bus) {
875
876 static const struct bus_properties_map map[] = {
877 { "PoolPath", "s", NULL, offsetof(PoolStatusInfo, path) },
878 { "PoolUsage", "t", NULL, offsetof(PoolStatusInfo, usage) },
879 { "PoolLimit", "t", NULL, offsetof(PoolStatusInfo, limit) },
880 {}
881 };
882
883 PoolStatusInfo info = {
884 .usage = (uint64_t) -1,
885 .limit = (uint64_t) -1,
886 };
887 int r;
888
889 assert(bus);
890
891 r = bus_map_all_properties(bus,
892 "org.freedesktop.machine1",
893 "/org/freedesktop/machine1",
894 map,
895 &info);
896 if (r < 0)
897 return log_error_errno(r, "Could not get properties: %m");
898
899 print_pool_status_info(bus, &info);
900
901 free(info.path);
902 return 0;
903}
904
905
fefdc04b
LP
906static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
907 int r;
908
909 assert(bus);
910 assert(path);
911 assert(new_line);
912
913 if (*new_line)
914 printf("\n");
915
916 *new_line = true;
917
918 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
919 if (r < 0)
920 log_error_errno(r, "Could not get properties: %m");
921
922 return r;
923}
924
925static int show_image(int argc, char *argv[], void *userdata) {
926
927 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
928 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
929 bool properties, new_line = false;
930 sd_bus *bus = userdata;
931 int r = 0, i;
932
933 assert(bus);
934
935 properties = !strstr(argv[0], "status");
936
937 pager_open_if_enabled();
938
160e3793 939 if (argc <= 1) {
fefdc04b
LP
940
941 /* If no argument is specified, inspect the manager
942 * itself */
160e3793
LP
943
944 if (properties)
945 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
946 else
947 r = show_pool_info(bus);
fefdc04b
LP
948 if (r < 0)
949 return r;
950 }
951
952 for (i = 1; i < argc; i++) {
953 const char *path = NULL;
954
955 r = sd_bus_call_method(
90adaa25
LP
956 bus,
957 "org.freedesktop.machine1",
958 "/org/freedesktop/machine1",
959 "org.freedesktop.machine1.Manager",
960 "GetImage",
961 &error,
962 &reply,
963 "s", argv[i]);
fefdc04b
LP
964 if (r < 0) {
965 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
966 return r;
967 }
968
969 r = sd_bus_message_read(reply, "o", &path);
970 if (r < 0)
971 return bus_log_parse_error(r);
972
973 if (properties)
974 r = show_image_properties(bus, path, &new_line);
975 else
160e3793 976 r = show_image_info(bus, path, &new_line);
1ee306e1
LP
977 }
978
9f6eb1cd 979 return r;
1ee306e1
LP
980}
981
56159e0d 982static int kill_machine(int argc, char *argv[], void *userdata) {
a1da8583 983 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d 984 sd_bus *bus = userdata;
90adaa25 985 int r, i;
1ee306e1 986
56159e0d 987 assert(bus);
1ee306e1 988
acf97e21
LP
989 polkit_agent_open_if_enabled();
990
1ee306e1
LP
991 if (!arg_kill_who)
992 arg_kill_who = "all";
993
56159e0d 994 for (i = 1; i < argc; i++) {
a1da8583 995 r = sd_bus_call_method(
56159e0d
LP
996 bus,
997 "org.freedesktop.machine1",
998 "/org/freedesktop/machine1",
999 "org.freedesktop.machine1.Manager",
1000 "KillMachine",
1001 &error,
1002 NULL,
1003 "ssi", argv[i], arg_kill_who, arg_signal);
a1da8583
TG
1004 if (r < 0) {
1005 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
1ee306e1 1006 return r;
a1da8583 1007 }
1ee306e1
LP
1008 }
1009
1010 return 0;
1011}
1012
56159e0d 1013static int reboot_machine(int argc, char *argv[], void *userdata) {
1dba654b
LP
1014 arg_kill_who = "leader";
1015 arg_signal = SIGINT; /* sysvinit + systemd */
1ee306e1 1016
56159e0d 1017 return kill_machine(argc, argv, userdata);
1dba654b 1018}
1ee306e1 1019
56159e0d 1020static int poweroff_machine(int argc, char *argv[], void *userdata) {
1dba654b
LP
1021 arg_kill_who = "leader";
1022 arg_signal = SIGRTMIN+4; /* only systemd */
1ee306e1 1023
56159e0d 1024 return kill_machine(argc, argv, userdata);
1ee306e1
LP
1025}
1026
56159e0d 1027static int terminate_machine(int argc, char *argv[], void *userdata) {
923d8fd3 1028 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d 1029 sd_bus *bus = userdata;
2723b3b5 1030 int r, i;
923d8fd3 1031
56159e0d 1032 assert(bus);
923d8fd3 1033
acf97e21
LP
1034 polkit_agent_open_if_enabled();
1035
56159e0d 1036 for (i = 1; i < argc; i++) {
923d8fd3
LP
1037 r = sd_bus_call_method(
1038 bus,
1039 "org.freedesktop.machine1",
1040 "/org/freedesktop/machine1",
1041 "org.freedesktop.machine1.Manager",
1dba654b 1042 "TerminateMachine",
923d8fd3 1043 &error,
1dba654b 1044 NULL,
56159e0d 1045 "s", argv[i]);
923d8fd3 1046 if (r < 0) {
1dba654b 1047 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
923d8fd3
LP
1048 return r;
1049 }
923d8fd3
LP
1050 }
1051
1052 return 0;
1053}
1054
0370612e 1055static int copy_files(int argc, char *argv[], void *userdata) {
785890ac 1056 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
0370612e
LP
1057 sd_bus *bus = userdata;
1058 bool copy_from;
785890ac
LP
1059 int r;
1060
1061 assert(bus);
0370612e 1062
2723b3b5
LP
1063 polkit_agent_open_if_enabled();
1064
0370612e 1065 copy_from = streq(argv[0], "copy-from");
785890ac
LP
1066
1067 r = sd_bus_call_method(
1068 bus,
1069 "org.freedesktop.machine1",
1070 "/org/freedesktop/machine1",
1071 "org.freedesktop.machine1.Manager",
0370612e 1072 copy_from ? "CopyFromMachine" : "CopyToMachine",
785890ac 1073 &error,
0370612e
LP
1074 NULL,
1075 "sss",
1076 argv[1],
1077 argv[2],
1078 argv[3]);
785890ac 1079 if (r < 0) {
0370612e 1080 log_error("Failed to copy: %s", bus_error_message(&error, -r));
f2cbe59e 1081 return r;
f2cbe59e 1082 }
f2cbe59e
LP
1083
1084 return 0;
1085}
1086
56159e0d 1087static int bind_mount(int argc, char *argv[], void *userdata) {
90adaa25 1088 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d 1089 sd_bus *bus = userdata;
785890ac
LP
1090 int r;
1091
56159e0d
LP
1092 assert(bus);
1093
2723b3b5
LP
1094 polkit_agent_open_if_enabled();
1095
90adaa25
LP
1096 r = sd_bus_call_method(
1097 bus,
1098 "org.freedesktop.machine1",
1099 "/org/freedesktop/machine1",
1100 "org.freedesktop.machine1.Manager",
1101 "BindMountMachine",
1102 &error,
1103 NULL,
1104 "sssbb",
1105 argv[1],
1106 argv[2],
1107 argv[3],
1108 arg_read_only,
1109 arg_mkdir);
785890ac 1110 if (r < 0) {
90adaa25
LP
1111 log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
1112 return r;
785890ac
LP
1113 }
1114
90adaa25 1115 return 0;
785890ac
LP
1116}
1117
19070062 1118static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
0ec5543c
LP
1119 PTYForward ** forward = (PTYForward**) userdata;
1120 int r;
1121
0ec5543c
LP
1122 assert(m);
1123 assert(forward);
1124
1125 if (*forward) {
1126 /* If the forwarder is already initialized, tell it to
da054c37
LP
1127 * exit on the next vhangup(), so that we still flush
1128 * out what might be queued and exit then. */
0ec5543c 1129
da054c37 1130 r = pty_forward_set_ignore_vhangup(*forward, false);
0ec5543c
LP
1131 if (r >= 0)
1132 return 0;
1133
da054c37 1134 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
0ec5543c
LP
1135 }
1136
1137 /* On error, or when the forwarder is not initialized yet, quit immediately */
19070062 1138 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
0ec5543c
LP
1139 return 0;
1140}
1141
56159e0d 1142static int login_machine(int argc, char *argv[], void *userdata) {
04d39279 1143 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2723b3b5 1144 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
0ec5543c 1145 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
023fb90b
LP
1146 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1147 _cleanup_event_unref_ sd_event *event = NULL;
40205d70 1148 int master = -1, r, ret = 0;
56159e0d 1149 sd_bus *bus = userdata;
0ec5543c 1150 const char *pty, *match;
c7b7d449 1151 char last_char = 0;
0ec5543c 1152 bool machine_died;
04d39279
LP
1153
1154 assert(bus);
04d39279 1155
bf441e3d 1156 if (arg_transport != BUS_TRANSPORT_LOCAL &&
de33fc62 1157 arg_transport != BUS_TRANSPORT_MACHINE) {
923d8fd3 1158 log_error("Login only supported on local machines.");
15411c0c 1159 return -EOPNOTSUPP;
04d39279
LP
1160 }
1161
acf97e21
LP
1162 polkit_agent_open_if_enabled();
1163
023fb90b 1164 r = sd_event_default(&event);
f647962d
MS
1165 if (r < 0)
1166 return log_error_errno(r, "Failed to get event loop: %m");
023fb90b
LP
1167
1168 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
1169 if (r < 0)
1170 return log_error_errno(r, "Failed to attach bus to event loop: %m");
023fb90b 1171
63c372cb 1172 match = strjoina("type='signal',"
0ec5543c
LP
1173 "sender='org.freedesktop.machine1',"
1174 "path='/org/freedesktop/machine1',",
1175 "interface='org.freedesktop.machine1.Manager',"
1176 "member='MachineRemoved',"
1177 "arg0='",
1178 argv[1],
1179 "'");
1180
1181 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1182 if (r < 0)
1183 return log_error_errno(r, "Failed to add machine removal match: %m");
1184
2723b3b5
LP
1185 r = sd_bus_call_method(
1186 bus,
1187 "org.freedesktop.machine1",
1188 "/org/freedesktop/machine1",
1189 "org.freedesktop.machine1.Manager",
1190 "OpenMachineLogin",
1191 &error,
1192 &reply,
1193 "s", argv[1]);
40205d70
LP
1194 if (r < 0) {
1195 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
785890ac 1196 return r;
40205d70 1197 }
04d39279 1198
40205d70
LP
1199 r = sd_bus_message_read(reply, "hs", &master, &pty);
1200 if (r < 0)
ee451d76 1201 return bus_log_parse_error(r);
04d39279 1202
813c65c3 1203 sigprocmask_many(SIG_BLOCK, SIGWINCH, SIGTERM, SIGINT, -1);
04d39279 1204
0ec5543c 1205 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
04d39279 1206
023fb90b
LP
1207 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1208 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1209
9c857b9d 1210 r = pty_forward_new(event, master, true, false, &forward);
f647962d
MS
1211 if (r < 0)
1212 return log_error_errno(r, "Failed to create PTY forwarder: %m");
023fb90b
LP
1213
1214 r = sd_event_loop(event);
f647962d
MS
1215 if (r < 0)
1216 return log_error_errno(r, "Failed to run event loop: %m");
04d39279 1217
0ec5543c 1218 pty_forward_get_last_char(forward, &last_char);
da054c37 1219 machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
c7b7d449 1220
023fb90b
LP
1221 forward = pty_forward_free(forward);
1222
c7b7d449
LP
1223 if (last_char != '\n')
1224 fputc('\n', stdout);
04d39279 1225
0ec5543c
LP
1226 if (machine_died)
1227 log_info("Machine %s terminated.", argv[1]);
1228 else
1229 log_info("Connection to machine %s terminated.", argv[1]);
04d39279 1230
023fb90b
LP
1231 sd_event_get_exit_code(event, &ret);
1232 return ret;
04d39279
LP
1233}
1234
08682124
LP
1235static int remove_image(int argc, char *argv[], void *userdata) {
1236 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1237 sd_bus *bus = userdata;
ebd93cb6 1238 int r, i;
08682124
LP
1239
1240 assert(bus);
1241
acf97e21
LP
1242 polkit_agent_open_if_enabled();
1243
08682124 1244 for (i = 1; i < argc; i++) {
08682124
LP
1245 r = sd_bus_call_method(
1246 bus,
1247 "org.freedesktop.machine1",
1248 "/org/freedesktop/machine1",
1249 "org.freedesktop.machine1.Manager",
1250 "RemoveImage",
1251 &error,
1252 NULL,
1253 "s", argv[i]);
1254 if (r < 0) {
1255 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1256 return r;
1257 }
1258 }
1259
1260 return 0;
1261}
1262
ebd93cb6
LP
1263static int rename_image(int argc, char *argv[], void *userdata) {
1264 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1265 sd_bus *bus = userdata;
1266 int r;
1267
acf97e21
LP
1268 polkit_agent_open_if_enabled();
1269
ebd93cb6
LP
1270 r = sd_bus_call_method(
1271 bus,
1272 "org.freedesktop.machine1",
1273 "/org/freedesktop/machine1",
1274 "org.freedesktop.machine1.Manager",
1275 "RenameImage",
1276 &error,
1277 NULL,
1278 "ss", argv[1], argv[2]);
1279 if (r < 0) {
1280 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1281 return r;
1282 }
1283
1284 return 0;
1285}
1286
1287static int clone_image(int argc, char *argv[], void *userdata) {
1288 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1289 sd_bus *bus = userdata;
1290 int r;
1291
acf97e21
LP
1292 polkit_agent_open_if_enabled();
1293
ebd93cb6
LP
1294 r = sd_bus_call_method(
1295 bus,
1296 "org.freedesktop.machine1",
1297 "/org/freedesktop/machine1",
1298 "org.freedesktop.machine1.Manager",
1299 "CloneImage",
1300 &error,
1301 NULL,
1302 "ssb", argv[1], argv[2], arg_read_only);
1303 if (r < 0) {
1304 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1305 return r;
1306 }
1307
1308 return 0;
1309}
1310
1311static int read_only_image(int argc, char *argv[], void *userdata) {
1312 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1313 sd_bus *bus = userdata;
1314 int b = true, r;
1315
1316 if (argc > 2) {
1317 b = parse_boolean(argv[2]);
1318 if (b < 0) {
1319 log_error("Failed to parse boolean argument: %s", argv[2]);
1320 return -EINVAL;
1321 }
1322 }
1323
acf97e21
LP
1324 polkit_agent_open_if_enabled();
1325
ebd93cb6
LP
1326 r = sd_bus_call_method(
1327 bus,
1328 "org.freedesktop.machine1",
1329 "/org/freedesktop/machine1",
1330 "org.freedesktop.machine1.Manager",
1331 "MarkImageReadOnly",
1332 &error,
1333 NULL,
1334 "sb", argv[1], b);
1335 if (r < 0) {
1336 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1337 return r;
1338 }
1339
1340 return 0;
1341}
1342
7410616c
LP
1343static int make_service_name(const char *name, char **ret) {
1344 _cleanup_free_ char *e = NULL;
1345 int r;
1346
1347 assert(name);
1348 assert(ret);
1349
1350 if (!machine_name_is_valid(name)) {
1351 log_error("Invalid machine name %s.", name);
1352 return -EINVAL;
1353 }
1354
1355 e = unit_name_escape(name);
1356 if (!e)
1357 return log_oom();
1358
1359 r = unit_name_build("systemd-nspawn", e, ".service", ret);
1360 if (r < 0)
1361 return log_error_errno(r, "Failed to build unit name: %m");
1362
1363 return 0;
1364}
1365
ebd011d9
LP
1366static int start_machine(int argc, char *argv[], void *userdata) {
1367 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1368 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1369 sd_bus *bus = userdata;
1370 int r, i;
1371
1372 assert(bus);
1373
acf97e21
LP
1374 polkit_agent_open_if_enabled();
1375
ebd011d9
LP
1376 r = bus_wait_for_jobs_new(bus, &w);
1377 if (r < 0)
1378 return log_oom();
1379
1380 for (i = 1; i < argc; i++) {
2723b3b5 1381 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
7410616c 1382 _cleanup_free_ char *unit = NULL;
ebd011d9
LP
1383 const char *object;
1384
7410616c
LP
1385 r = make_service_name(argv[i], &unit);
1386 if (r < 0)
1387 return r;
ebd011d9 1388
2723b3b5 1389 r = sd_bus_call_method(
ebd011d9 1390 bus,
ebd011d9
LP
1391 "org.freedesktop.systemd1",
1392 "/org/freedesktop/systemd1",
1393 "org.freedesktop.systemd1.Manager",
2723b3b5
LP
1394 "StartUnit",
1395 &error,
1396 &reply,
1397 "ss", unit, "fail");
ebd011d9
LP
1398 if (r < 0) {
1399 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1400 return r;
1401 }
1402
1403 r = sd_bus_message_read(reply, "o", &object);
1404 if (r < 0)
1405 return bus_log_parse_error(r);
1406
1407 r = bus_wait_for_jobs_add(w, object);
1408 if (r < 0)
1409 return log_oom();
1410 }
1411
d8f52ed2 1412 r = bus_wait_for_jobs(w, arg_quiet);
ebd011d9
LP
1413 if (r < 0)
1414 return r;
1415
1416 return 0;
1417}
1418
d8f52ed2
LP
1419static int enable_machine(int argc, char *argv[], void *userdata) {
1420 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1421 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1422 int carries_install_info = 0;
1423 const char *method = NULL;
1424 sd_bus *bus = userdata;
1425 int r, i;
1426
1427 assert(bus);
1428
acf97e21
LP
1429 polkit_agent_open_if_enabled();
1430
d8f52ed2
LP
1431 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1432
1433 r = sd_bus_message_new_method_call(
1434 bus,
1435 &m,
1436 "org.freedesktop.systemd1",
1437 "/org/freedesktop/systemd1",
1438 "org.freedesktop.systemd1.Manager",
1439 method);
1440 if (r < 0)
1441 return bus_log_create_error(r);
1442
d8f52ed2
LP
1443 r = sd_bus_message_open_container(m, 'a', "s");
1444 if (r < 0)
1445 return bus_log_create_error(r);
1446
1447 for (i = 1; i < argc; i++) {
90615ad7 1448 _cleanup_free_ char *unit = NULL;
d8f52ed2 1449
7410616c
LP
1450 r = make_service_name(argv[i], &unit);
1451 if (r < 0)
1452 return r;
d8f52ed2
LP
1453
1454 r = sd_bus_message_append(m, "s", unit);
1455 if (r < 0)
1456 return bus_log_create_error(r);
1457 }
1458
1459 r = sd_bus_message_close_container(m);
1460 if (r < 0)
1461 return bus_log_create_error(r);
1462
1463 if (streq(argv[0], "enable"))
1464 r = sd_bus_message_append(m, "bb", false, false);
1465 else
1466 r = sd_bus_message_append(m, "b", false);
1467 if (r < 0)
1468 return bus_log_create_error(r);
1469
1470 r = sd_bus_call(bus, m, 0, &error, &reply);
1471 if (r < 0) {
1472 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1473 return r;
1474 }
1475
1476 if (streq(argv[0], "enable")) {
1477 r = sd_bus_message_read(reply, "b", carries_install_info);
1478 if (r < 0)
1479 return bus_log_parse_error(r);
1480 }
1481
1482 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1483 if (r < 0)
1484 return r;
1485
2723b3b5 1486 r = sd_bus_call_method(
d8f52ed2 1487 bus,
d8f52ed2
LP
1488 "org.freedesktop.systemd1",
1489 "/org/freedesktop/systemd1",
1490 "org.freedesktop.systemd1.Manager",
2723b3b5
LP
1491 "Reload",
1492 &error,
1493 NULL,
1494 NULL);
d8f52ed2
LP
1495 if (r < 0) {
1496 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1497 return r;
1498 }
1499
1500 return 0;
1501}
1502
19070062 1503static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) {
6adf7b5e 1504 const char **our_path = userdata, *line;
3d7415f4
LP
1505 unsigned priority;
1506 int r;
1507
3d7415f4 1508 assert(m);
6adf7b5e 1509 assert(our_path);
3d7415f4
LP
1510
1511 r = sd_bus_message_read(m, "us", &priority, &line);
1512 if (r < 0) {
1513 bus_log_parse_error(r);
1514 return 0;
1515 }
1516
6adf7b5e 1517 if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
3d7415f4
LP
1518 return 0;
1519
1520 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1521 return 0;
1522
1523 log_full(priority, "%s", line);
1524 return 0;
1525}
1526
19070062 1527static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
6adf7b5e 1528 const char **our_path = userdata, *path, *result;
3d7415f4
LP
1529 uint32_t id;
1530 int r;
1531
3d7415f4 1532 assert(m);
6adf7b5e 1533 assert(our_path);
3d7415f4
LP
1534
1535 r = sd_bus_message_read(m, "uos", &id, &path, &result);
1536 if (r < 0) {
1537 bus_log_parse_error(r);
1538 return 0;
1539 }
1540
6adf7b5e 1541 if (!streq_ptr(*our_path, path))
3d7415f4
LP
1542 return 0;
1543
19070062 1544 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done"));
6adf7b5e
LP
1545 return 0;
1546}
1547
1548static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1549 assert(s);
1550 assert(si);
1551
1552 if (!arg_quiet)
cc98b302 1553 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
6adf7b5e
LP
1554
1555 sd_event_exit(sd_event_source_get_event(s), EINTR);
3d7415f4
LP
1556 return 0;
1557}
1558
587fec42 1559static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
3d7415f4
LP
1560 _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1561 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1562 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
6adf7b5e
LP
1563 _cleanup_event_unref_ sd_event* event = NULL;
1564 const char *path = NULL;
3d7415f4
LP
1565 uint32_t id;
1566 int r;
1567
1568 assert(bus);
1569 assert(m);
1570
1571 polkit_agent_open_if_enabled();
1572
6adf7b5e
LP
1573 r = sd_event_default(&event);
1574 if (r < 0)
1575 return log_error_errno(r, "Failed to get event loop: %m");
1576
1577 r = sd_bus_attach_event(bus, event, 0);
1578 if (r < 0)
1579 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1580
3d7415f4
LP
1581 r = sd_bus_add_match(
1582 bus,
1583 &slot_job_removed,
1584 "type='signal',"
1585 "sender='org.freedesktop.import1',"
1586 "interface='org.freedesktop.import1.Manager',"
1587 "member='TransferRemoved',"
1588 "path='/org/freedesktop/import1'",
6adf7b5e 1589 match_transfer_removed, &path);
3d7415f4
LP
1590 if (r < 0)
1591 return log_error_errno(r, "Failed to install match: %m");
1592
1593 r = sd_bus_add_match(
1594 bus,
1595 &slot_log_message,
1596 "type='signal',"
1597 "sender='org.freedesktop.import1',"
1598 "interface='org.freedesktop.import1.Transfer',"
1599 "member='LogMessage'",
6adf7b5e 1600 match_log_message, &path);
3d7415f4
LP
1601 if (r < 0)
1602 return log_error_errno(r, "Failed to install match: %m");
1603
1604 r = sd_bus_call(bus, m, 0, &error, &reply);
1605 if (r < 0) {
587fec42 1606 log_error("Failed transfer image: %s", bus_error_message(&error, -r));
3d7415f4
LP
1607 return r;
1608 }
1609
6adf7b5e 1610 r = sd_bus_message_read(reply, "uo", &id, &path);
3d7415f4
LP
1611 if (r < 0)
1612 return bus_log_parse_error(r);
1613
6adf7b5e 1614 sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
3d7415f4 1615
6adf7b5e
LP
1616 if (!arg_quiet)
1617 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
3d7415f4 1618
6adf7b5e
LP
1619 sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
1620 sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
1621
1622 r = sd_event_loop(event);
1623 if (r < 0)
1624 return log_error_errno(r, "Failed to run event loop: %m");
3d7415f4 1625
6adf7b5e 1626 return -r;
3d7415f4
LP
1627}
1628
b6e676ce
LP
1629static int import_tar(int argc, char *argv[], void *userdata) {
1630 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1631 _cleanup_free_ char *ll = NULL;
1632 _cleanup_close_ int fd = -1;
1633 const char *local = NULL, *path = NULL;
1634 sd_bus *bus = userdata;
1635 int r;
1636
1637 assert(bus);
1638
1639 if (argc >= 2)
1640 path = argv[1];
1641 if (isempty(path) || streq(path, "-"))
1642 path = NULL;
1643
1644 if (argc >= 3)
1645 local = argv[2];
1646 else if (path)
1647 local = basename(path);
1648 if (isempty(local) || streq(local, "-"))
1649 local = NULL;
1650
1651 if (!local) {
1652 log_error("Need either path or local name.");
1653 return -EINVAL;
1654 }
1655
1656 r = tar_strip_suffixes(local, &ll);
1657 if (r < 0)
1658 return log_oom();
1659
1660 local = ll;
1661
1662 if (!machine_name_is_valid(local)) {
1663 log_error("Local name %s is not a suitable machine name.", local);
1664 return -EINVAL;
1665 }
1666
1667 if (path) {
1668 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1669 if (fd < 0)
1670 return log_error_errno(errno, "Failed to open %s: %m", path);
1671 }
1672
1673 r = sd_bus_message_new_method_call(
1674 bus,
1675 &m,
1676 "org.freedesktop.import1",
1677 "/org/freedesktop/import1",
1678 "org.freedesktop.import1.Manager",
1679 "ImportTar");
1680 if (r < 0)
1681 return bus_log_create_error(r);
1682
1683 r = sd_bus_message_append(
1684 m,
1685 "hsbb",
1686 fd >= 0 ? fd : STDIN_FILENO,
1687 local,
1688 arg_force,
1689 arg_read_only);
1690 if (r < 0)
1691 return bus_log_create_error(r);
1692
587fec42 1693 return transfer_image_common(bus, m);
b6e676ce
LP
1694}
1695
1696static int import_raw(int argc, char *argv[], void *userdata) {
1697 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1698 _cleanup_free_ char *ll = NULL;
1699 _cleanup_close_ int fd = -1;
1700 const char *local = NULL, *path = NULL;
1701 sd_bus *bus = userdata;
1702 int r;
1703
1704 assert(bus);
1705
1706 if (argc >= 2)
1707 path = argv[1];
1708 if (isempty(path) || streq(path, "-"))
1709 path = NULL;
1710
1711 if (argc >= 3)
1712 local = argv[2];
1713 else if (path)
1714 local = basename(path);
1715 if (isempty(local) || streq(local, "-"))
1716 local = NULL;
1717
1718 if (!local) {
1719 log_error("Need either path or local name.");
1720 return -EINVAL;
1721 }
1722
1723 r = raw_strip_suffixes(local, &ll);
1724 if (r < 0)
1725 return log_oom();
1726
1727 local = ll;
1728
1729 if (!machine_name_is_valid(local)) {
1730 log_error("Local name %s is not a suitable machine name.", local);
1731 return -EINVAL;
1732 }
1733
1734 if (path) {
1735 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1736 if (fd < 0)
1737 return log_error_errno(errno, "Failed to open %s: %m", path);
1738 }
1739
1740 r = sd_bus_message_new_method_call(
1741 bus,
1742 &m,
1743 "org.freedesktop.import1",
1744 "/org/freedesktop/import1",
1745 "org.freedesktop.import1.Manager",
1746 "ImportRaw");
1747 if (r < 0)
1748 return bus_log_create_error(r);
1749
1750 r = sd_bus_message_append(
1751 m,
1752 "hsbb",
1753 fd >= 0 ? fd : STDIN_FILENO,
1754 local,
1755 arg_force,
1756 arg_read_only);
1757 if (r < 0)
1758 return bus_log_create_error(r);
1759
587fec42
LP
1760 return transfer_image_common(bus, m);
1761}
1762
1763static void determine_compression_from_filename(const char *p) {
1764 if (arg_format)
1765 return;
1766
1767 if (!p)
1768 return;
1769
1770 if (endswith(p, ".xz"))
1771 arg_format = "xz";
1772 else if (endswith(p, ".gz"))
1773 arg_format = "gzip";
1774 else if (endswith(p, ".bz2"))
1775 arg_format = "bzip2";
1776}
1777
1778static int export_tar(int argc, char *argv[], void *userdata) {
1779 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
587fec42
LP
1780 _cleanup_close_ int fd = -1;
1781 const char *local = NULL, *path = NULL;
1782 sd_bus *bus = userdata;
1783 int r;
1784
1785 assert(bus);
1786
1787 local = argv[1];
1788 if (!machine_name_is_valid(local)) {
1789 log_error("Machine name %s is not valid.", local);
1790 return -EINVAL;
1791 }
1792
1793 if (argc >= 3)
1794 path = argv[2];
1795 if (isempty(path) || streq(path, "-"))
1796 path = NULL;
1797
1798 if (path) {
1799 determine_compression_from_filename(path);
1800
1801 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
1802 if (fd < 0)
1803 return log_error_errno(errno, "Failed to open %s: %m", path);
1804 }
1805
1806 r = sd_bus_message_new_method_call(
1807 bus,
1808 &m,
1809 "org.freedesktop.import1",
1810 "/org/freedesktop/import1",
1811 "org.freedesktop.import1.Manager",
1812 "ExportTar");
1813 if (r < 0)
1814 return bus_log_create_error(r);
1815
1816 r = sd_bus_message_append(
1817 m,
1818 "shs",
1819 local,
1820 fd >= 0 ? fd : STDOUT_FILENO,
1821 arg_format);
1822 if (r < 0)
1823 return bus_log_create_error(r);
1824
1825 return transfer_image_common(bus, m);
1826}
1827
1828static int export_raw(int argc, char *argv[], void *userdata) {
1829 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
587fec42
LP
1830 _cleanup_close_ int fd = -1;
1831 const char *local = NULL, *path = NULL;
1832 sd_bus *bus = userdata;
1833 int r;
1834
1835 assert(bus);
1836
1837 local = argv[1];
1838 if (!machine_name_is_valid(local)) {
1839 log_error("Machine name %s is not valid.", local);
1840 return -EINVAL;
1841 }
1842
1843 if (argc >= 3)
1844 path = argv[2];
1845 if (isempty(path) || streq(path, "-"))
1846 path = NULL;
1847
1848 if (path) {
1849 determine_compression_from_filename(path);
1850
1851 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
1852 if (fd < 0)
1853 return log_error_errno(errno, "Failed to open %s: %m", path);
1854 }
1855
1856 r = sd_bus_message_new_method_call(
1857 bus,
1858 &m,
1859 "org.freedesktop.import1",
1860 "/org/freedesktop/import1",
1861 "org.freedesktop.import1.Manager",
1862 "ExportRaw");
1863 if (r < 0)
1864 return bus_log_create_error(r);
1865
1866 r = sd_bus_message_append(
1867 m,
1868 "shs",
1869 local,
1870 fd >= 0 ? fd : STDOUT_FILENO,
1871 arg_format);
1872 if (r < 0)
1873 return bus_log_create_error(r);
1874
1875 return transfer_image_common(bus, m);
b6e676ce
LP
1876}
1877
3d7415f4
LP
1878static int pull_tar(int argc, char *argv[], void *userdata) {
1879 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1880 _cleanup_free_ char *l = NULL, *ll = NULL;
1881 const char *local, *remote;
1882 sd_bus *bus = userdata;
1883 int r;
1884
1885 assert(bus);
1886
1887 remote = argv[1];
1888 if (!http_url_is_valid(remote)) {
1889 log_error("URL '%s' is not valid.", remote);
1890 return -EINVAL;
1891 }
1892
1893 if (argc >= 3)
1894 local = argv[2];
1895 else {
1896 r = import_url_last_component(remote, &l);
1897 if (r < 0)
1898 return log_error_errno(r, "Failed to get final component of URL: %m");
1899
1900 local = l;
1901 }
1902
1903 if (isempty(local) || streq(local, "-"))
1904 local = NULL;
1905
1906 if (local) {
1907 r = tar_strip_suffixes(local, &ll);
1908 if (r < 0)
b6e676ce 1909 return log_oom();
3d7415f4
LP
1910
1911 local = ll;
1912
1913 if (!machine_name_is_valid(local)) {
1914 log_error("Local name %s is not a suitable machine name.", local);
1915 return -EINVAL;
1916 }
1917 }
1918
1919 r = sd_bus_message_new_method_call(
1920 bus,
1921 &m,
1922 "org.freedesktop.import1",
1923 "/org/freedesktop/import1",
1924 "org.freedesktop.import1.Manager",
1925 "PullTar");
1926 if (r < 0)
1927 return bus_log_create_error(r);
1928
1929 r = sd_bus_message_append(
1930 m,
1931 "sssb",
1932 remote,
1933 local,
6e18cc9f 1934 import_verify_to_string(arg_verify),
3d7415f4
LP
1935 arg_force);
1936 if (r < 0)
1937 return bus_log_create_error(r);
1938
587fec42 1939 return transfer_image_common(bus, m);
3d7415f4
LP
1940}
1941
1942static int pull_raw(int argc, char *argv[], void *userdata) {
1943 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1944 _cleanup_free_ char *l = NULL, *ll = NULL;
1945 const char *local, *remote;
1946 sd_bus *bus = userdata;
1947 int r;
1948
1949 assert(bus);
1950
1951 remote = argv[1];
1952 if (!http_url_is_valid(remote)) {
1953 log_error("URL '%s' is not valid.", remote);
1954 return -EINVAL;
1955 }
1956
1957 if (argc >= 3)
1958 local = argv[2];
1959 else {
1960 r = import_url_last_component(remote, &l);
1961 if (r < 0)
1962 return log_error_errno(r, "Failed to get final component of URL: %m");
1963
1964 local = l;
1965 }
1966
1967 if (isempty(local) || streq(local, "-"))
1968 local = NULL;
1969
1970 if (local) {
1971 r = raw_strip_suffixes(local, &ll);
1972 if (r < 0)
b6e676ce 1973 return log_oom();
3d7415f4
LP
1974
1975 local = ll;
1976
1977 if (!machine_name_is_valid(local)) {
1978 log_error("Local name %s is not a suitable machine name.", local);
1979 return -EINVAL;
1980 }
1981 }
1982
1983 r = sd_bus_message_new_method_call(
1984 bus,
1985 &m,
1986 "org.freedesktop.import1",
1987 "/org/freedesktop/import1",
1988 "org.freedesktop.import1.Manager",
1989 "PullRaw");
1990 if (r < 0)
1991 return bus_log_create_error(r);
1992
1993 r = sd_bus_message_append(
1994 m,
1995 "sssb",
1996 remote,
1997 local,
6e18cc9f 1998 import_verify_to_string(arg_verify),
3d7415f4
LP
1999 arg_force);
2000 if (r < 0)
2001 return bus_log_create_error(r);
2002
587fec42 2003 return transfer_image_common(bus, m);
3d7415f4
LP
2004}
2005
2006static int pull_dkr(int argc, char *argv[], void *userdata) {
2007 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
3d7415f4
LP
2008 const char *local, *remote, *tag;
2009 sd_bus *bus = userdata;
2010 int r;
2011
56e6c2ab 2012 if (arg_verify != IMPORT_VERIFY_NO) {
3d7415f4
LP
2013 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2014 return -EINVAL;
2015 }
2016
2017 remote = argv[1];
2018 tag = strchr(remote, ':');
2019 if (tag) {
2020 remote = strndupa(remote, tag - remote);
2021 tag++;
2022 }
2023
2024 if (!dkr_name_is_valid(remote)) {
2025 log_error("DKR name '%s' is invalid.", remote);
2026 return -EINVAL;
2027 }
2028 if (tag && !dkr_tag_is_valid(tag)) {
2029 log_error("DKR tag '%s' is invalid.", remote);
2030 return -EINVAL;
2031 }
2032
2033 if (argc >= 3)
2034 local = argv[2];
2035 else {
2036 local = strchr(remote, '/');
2037 if (local)
2038 local++;
2039 else
2040 local = remote;
2041 }
2042
2043 if (isempty(local) || streq(local, "-"))
2044 local = NULL;
2045
2046 if (local) {
2047 if (!machine_name_is_valid(local)) {
2048 log_error("Local name %s is not a suitable machine name.", local);
2049 return -EINVAL;
2050 }
2051 }
2052
2053 r = sd_bus_message_new_method_call(
2054 bus,
2055 &m,
2056 "org.freedesktop.import1",
2057 "/org/freedesktop/import1",
2058 "org.freedesktop.import1.Manager",
2059 "PullDkr");
2060 if (r < 0)
2061 return bus_log_create_error(r);
2062
2063 r = sd_bus_message_append(
2064 m,
2065 "sssssb",
2066 arg_dkr_index_url,
2067 remote,
2068 tag,
2069 local,
6e18cc9f 2070 import_verify_to_string(arg_verify),
3d7415f4
LP
2071 arg_force);
2072 if (r < 0)
2073 return bus_log_create_error(r);
2074
587fec42 2075 return transfer_image_common(bus, m);
3d7415f4
LP
2076}
2077
2078typedef struct TransferInfo {
2079 uint32_t id;
2080 const char *type;
2081 const char *remote;
2082 const char *local;
7079cfef 2083 double progress;
3d7415f4
LP
2084} TransferInfo;
2085
2086static int compare_transfer_info(const void *a, const void *b) {
2087 const TransferInfo *x = a, *y = b;
2088
2089 return strcmp(x->local, y->local);
2090}
2091
2092static int list_transfers(int argc, char *argv[], void *userdata) {
2093 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
2094 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
2095 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2096 _cleanup_free_ TransferInfo *transfers = NULL;
2097 size_t n_transfers = 0, n_allocated = 0, j;
2098 const char *type, *remote, *local, *object;
2099 sd_bus *bus = userdata;
2100 uint32_t id, max_id = 0;
7079cfef 2101 double progress;
3d7415f4
LP
2102 int r;
2103
2104 pager_open_if_enabled();
2105
2106 r = sd_bus_call_method(
2107 bus,
2108 "org.freedesktop.import1",
2109 "/org/freedesktop/import1",
2110 "org.freedesktop.import1.Manager",
2111 "ListTransfers",
2112 &error,
2113 &reply,
2114 NULL);
2115 if (r < 0) {
2116 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2117 return r;
2118 }
2119
7079cfef 2120 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
3d7415f4
LP
2121 if (r < 0)
2122 return bus_log_parse_error(r);
2123
7079cfef 2124 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
3d7415f4
LP
2125 size_t l;
2126
2127 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2128 return log_oom();
2129
2130 transfers[n_transfers].id = id;
2131 transfers[n_transfers].type = type;
2132 transfers[n_transfers].remote = remote;
2133 transfers[n_transfers].local = local;
7079cfef 2134 transfers[n_transfers].progress = progress;
3d7415f4
LP
2135
2136 l = strlen(type);
2137 if (l > max_type)
2138 max_type = l;
2139
2140 l = strlen(remote);
2141 if (l > max_remote)
2142 max_remote = l;
2143
2144 l = strlen(local);
2145 if (l > max_local)
2146 max_local = l;
2147
2148 if (id > max_id)
2149 max_id = id;
2150
2151 n_transfers ++;
2152 }
2153 if (r < 0)
2154 return bus_log_parse_error(r);
2155
2156 r = sd_bus_message_exit_container(reply);
2157 if (r < 0)
2158 return bus_log_parse_error(r);
2159
2160 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2161
2162 if (arg_legend)
7079cfef 2163 printf("%-*s %-*s %-*s %-*s %-*s\n",
3d7415f4 2164 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
7079cfef 2165 (int) 7, "PERCENT",
3d7415f4
LP
2166 (int) max_type, "TYPE",
2167 (int) max_local, "LOCAL",
2168 (int) max_remote, "REMOTE");
2169
2170 for (j = 0; j < n_transfers; j++)
7079cfef 2171 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
3d7415f4 2172 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
7079cfef 2173 (int) 6, (unsigned) (transfers[j].progress * 100),
3d7415f4
LP
2174 (int) max_type, transfers[j].type,
2175 (int) max_local, transfers[j].local,
2176 (int) max_remote, transfers[j].remote);
2177
2178 if (arg_legend)
2179 printf("\n%zu transfers listed.\n", n_transfers);
2180
2181 return 0;
2182}
2183
2184static int cancel_transfer(int argc, char *argv[], void *userdata) {
2185 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2186 sd_bus *bus = userdata;
2187 int r, i;
2188
2189 assert(bus);
2190
2191 polkit_agent_open_if_enabled();
2192
2193 for (i = 1; i < argc; i++) {
2194 uint32_t id;
2195
2196 r = safe_atou32(argv[i], &id);
2197 if (r < 0)
2198 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2199
2200 r = sd_bus_call_method(
2201 bus,
2202 "org.freedesktop.import1",
2203 "/org/freedesktop/import1",
2204 "org.freedesktop.import1.Manager",
2205 "CancelTransfer",
2206 &error,
2207 NULL,
2208 "u", id);
2209 if (r < 0) {
2210 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2211 return r;
2212 }
2213 }
2214
2215 return 0;
2216}
2217
d6ce17c7
LP
2218static int set_limit(int argc, char *argv[], void *userdata) {
2219 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2220 sd_bus *bus = userdata;
2221 uint64_t limit;
2222 int r;
2223
2224 if (streq(argv[argc-1], "-"))
2225 limit = (uint64_t) -1;
2226 else {
2227 off_t off;
2228
2229 r = parse_size(argv[argc-1], 1024, &off);
2230 if (r < 0)
2231 return log_error("Failed to parse size: %s", argv[argc-1]);
2232
2233 limit = (uint64_t) off;
2234 }
2235
2236 if (argc > 2)
2237 /* With two arguments changes the quota limit of the
2238 * specified image */
2239 r = sd_bus_call_method(
2240 bus,
2241 "org.freedesktop.machine1",
2242 "/org/freedesktop/machine1",
2243 "org.freedesktop.machine1.Manager",
2244 "SetImageLimit",
2245 &error,
2246 NULL,
2247 "st", argv[1], limit);
2248 else
2249 /* With one argument changes the pool quota limit */
2250 r = sd_bus_call_method(
2251 bus,
2252 "org.freedesktop.machine1",
2253 "/org/freedesktop/machine1",
2254 "org.freedesktop.machine1.Manager",
2255 "SetPoolLimit",
2256 &error,
2257 NULL,
2258 "t", limit);
2259
2260 if (r < 0) {
2261 log_error("Could not set limit: %s", bus_error_message(&error, -r));
2262 return r;
2263 }
2264
2265 return 0;
2266}
2267
56159e0d
LP
2268static int help(int argc, char *argv[], void *userdata) {
2269
1ee306e1 2270 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
f2cbe59e
LP
2271 "Send control commands to or query the virtual machine and container\n"
2272 "registration manager.\n\n"
2273 " -h --help Show this help\n"
2274 " --version Show package version\n"
2275 " --no-pager Do not pipe output into a pager\n"
2276 " --no-legend Do not show the headers and footers\n"
acf97e21 2277 " --no-ask-password Do not ask for system passwords\n"
f2cbe59e
LP
2278 " -H --host=[USER@]HOST Operate on remote host\n"
2279 " -M --machine=CONTAINER Operate on local container\n"
2280 " -p --property=NAME Show only properties by this name\n"
d8f52ed2 2281 " -q --quiet Suppress output\n"
f2cbe59e
LP
2282 " -a --all Show all properties, including empty ones\n"
2283 " -l --full Do not ellipsize output\n"
2284 " --kill-who=WHO Who to send signal to\n"
2285 " -s --signal=SIGNAL Which signal to send\n"
2286 " --read-only Create read-only bind mount\n"
8b0cc9a3
LP
2287 " --mkdir Create directory before bind mounting, if missing\n"
2288 " -n --lines=INTEGER Number of journal entries to show\n"
2289 " -o --output=STRING Change journal output mode (short,\n"
2290 " short-monotonic, verbose, export, json,\n"
3d7415f4 2291 " json-pretty, json-sse, cat)\n"
7f444afa
LP
2292 " --verify=MODE Verification mode for downloaded images (no,\n"
2293 " checksum, signature)\n"
3d7415f4
LP
2294 " --force Download image even if already exists\n"
2295 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2296 " downloads\n\n"
cd61c3bf 2297 "Machine Commands:\n"
f2cbe59e 2298 " list List running VMs and containers\n"
fefdc04b 2299 " status NAME... Show VM/container details\n"
f2cbe59e 2300 " show NAME... Show properties of one or more VMs/containers\n"
ebd011d9 2301 " start NAME... Start container as a service\n"
e45fc5e7 2302 " login NAME Get a login prompt on a container\n"
d8f52ed2
LP
2303 " enable NAME... Enable automatic container start at boot\n"
2304 " disable NAME... Disable automatic container start at boot\n"
f2cbe59e
LP
2305 " poweroff NAME... Power off one or more containers\n"
2306 " reboot NAME... Reboot one or more containers\n"
f2cbe59e 2307 " terminate NAME... Terminate one or more VMs/containers\n"
ebd93cb6 2308 " kill NAME... Send signal to processes of a VM/container\n"
f2cbe59e 2309 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
ebd93cb6
LP
2310 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2311 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
fefdc04b 2312 "Image Commands:\n"
56b921c3 2313 " list-images Show available container and VM images\n"
fefdc04b 2314 " image-status NAME... Show image details\n"
08682124 2315 " show-image NAME... Show properties of image\n"
ebd93cb6
LP
2316 " clone NAME NAME Clone an image\n"
2317 " rename NAME NAME Rename an image\n"
2318 " read-only NAME [BOOL] Mark or unmark image read-only\n"
d6ce17c7 2319 " remove NAME... Remove an image\n"
e721d697 2320 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
b5b38b41
LP
2321 "Image Transfer Commands:\n"
2322 " pull-tar URL [NAME] Download a TAR container image\n"
2323 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2324 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
587fec42
LP
2325 " import-tar FILE [NAME] Import a local TAR container image\n"
2326 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
6e9efa59
LP
2327 " export-tar NAME [FILE] Export a TAR container image locally\n"
2328 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
b5b38b41 2329 " list-transfers Show list of downloads in progress\n"
3d7415f4 2330 " cancel-transfer Cancel a download\n"
f7621db0 2331 , program_invocation_short_name);
56159e0d
LP
2332
2333 return 0;
1ee306e1
LP
2334}
2335
2336static int parse_argv(int argc, char *argv[]) {
2337
2338 enum {
2339 ARG_VERSION = 0x100,
2340 ARG_NO_PAGER,
e56056e9 2341 ARG_NO_LEGEND,
1ee306e1 2342 ARG_KILL_WHO,
785890ac
LP
2343 ARG_READ_ONLY,
2344 ARG_MKDIR,
acf97e21 2345 ARG_NO_ASK_PASSWORD,
3d7415f4
LP
2346 ARG_VERIFY,
2347 ARG_FORCE,
2348 ARG_DKR_INDEX_URL,
587fec42 2349 ARG_FORMAT,
1ee306e1
LP
2350 };
2351
2352 static const struct option options[] = {
2353 { "help", no_argument, NULL, 'h' },
2354 { "version", no_argument, NULL, ARG_VERSION },
2355 { "property", required_argument, NULL, 'p' },
2356 { "all", no_argument, NULL, 'a' },
2357 { "full", no_argument, NULL, 'l' },
2358 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
e56056e9 2359 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1ee306e1
LP
2360 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2361 { "signal", required_argument, NULL, 's' },
2362 { "host", required_argument, NULL, 'H' },
a7893c6b 2363 { "machine", required_argument, NULL, 'M' },
785890ac
LP
2364 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2365 { "mkdir", no_argument, NULL, ARG_MKDIR },
d8f52ed2 2366 { "quiet", no_argument, NULL, 'q' },
8b0cc9a3
LP
2367 { "lines", required_argument, NULL, 'n' },
2368 { "output", required_argument, NULL, 'o' },
acf97e21 2369 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
3d7415f4
LP
2370 { "verify", required_argument, NULL, ARG_VERIFY },
2371 { "force", no_argument, NULL, ARG_FORCE },
2372 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
587fec42 2373 { "format", required_argument, NULL, ARG_FORMAT },
eb9da376 2374 {}
1ee306e1
LP
2375 };
2376
a7893c6b 2377 int c, r;
1ee306e1
LP
2378
2379 assert(argc >= 0);
2380 assert(argv);
2381
8b0cc9a3 2382 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
1ee306e1
LP
2383
2384 switch (c) {
2385
2386 case 'h':
56159e0d 2387 return help(0, NULL, NULL);
1ee306e1
LP
2388
2389 case ARG_VERSION:
2390 puts(PACKAGE_STRING);
2391 puts(SYSTEMD_FEATURES);
2392 return 0;
2393
a7893c6b
LP
2394 case 'p':
2395 r = strv_extend(&arg_property, optarg);
2396 if (r < 0)
2397 return log_oom();
1ee306e1
LP
2398
2399 /* If the user asked for a particular
2400 * property, show it to him, even if it is
2401 * empty. */
2402 arg_all = true;
2403 break;
1ee306e1
LP
2404
2405 case 'a':
2406 arg_all = true;
2407 break;
2408
2409 case 'l':
2410 arg_full = true;
2411 break;
2412
8b0cc9a3
LP
2413 case 'n':
2414 if (safe_atou(optarg, &arg_lines) < 0) {
2415 log_error("Failed to parse lines '%s'", optarg);
2416 return -EINVAL;
2417 }
2418 break;
2419
2420 case 'o':
2421 arg_output = output_mode_from_string(optarg);
2422 if (arg_output < 0) {
2423 log_error("Unknown output '%s'.", optarg);
2424 return -EINVAL;
2425 }
2426 break;
2427
1ee306e1
LP
2428 case ARG_NO_PAGER:
2429 arg_no_pager = true;
2430 break;
2431
e56056e9
TA
2432 case ARG_NO_LEGEND:
2433 arg_legend = false;
2434 break;
2435
1ee306e1
LP
2436 case ARG_KILL_WHO:
2437 arg_kill_who = optarg;
2438 break;
2439
2440 case 's':
2441 arg_signal = signal_from_string_try_harder(optarg);
2442 if (arg_signal < 0) {
2443 log_error("Failed to parse signal string %s.", optarg);
2444 return -EINVAL;
2445 }
2446 break;
2447
acf97e21
LP
2448 case ARG_NO_ASK_PASSWORD:
2449 arg_ask_password = false;
2450 break;
2451
1ee306e1 2452 case 'H':
d21ed1ea 2453 arg_transport = BUS_TRANSPORT_REMOTE;
a7893c6b
LP
2454 arg_host = optarg;
2455 break;
2456
2457 case 'M':
de33fc62 2458 arg_transport = BUS_TRANSPORT_MACHINE;
a7893c6b 2459 arg_host = optarg;
1ee306e1
LP
2460 break;
2461
785890ac
LP
2462 case ARG_READ_ONLY:
2463 arg_read_only = true;
2464 break;
2465
2466 case ARG_MKDIR:
2467 arg_mkdir = true;
2468 break;
2469
d8f52ed2
LP
2470 case 'q':
2471 arg_quiet = true;
2472 break;
2473
3d7415f4 2474 case ARG_VERIFY:
6e18cc9f
LP
2475 arg_verify = import_verify_from_string(optarg);
2476 if (arg_verify < 0) {
2477 log_error("Failed to parse --verify= setting: %s", optarg);
2478 return -EINVAL;
2479 }
3d7415f4
LP
2480 break;
2481
2482 case ARG_FORCE:
2483 arg_force = true;
2484 break;
2485
2486 case ARG_DKR_INDEX_URL:
2487 if (!http_url_is_valid(optarg)) {
2488 log_error("Index URL is invalid: %s", optarg);
2489 return -EINVAL;
2490 }
2491
2492 arg_dkr_index_url = optarg;
2493 break;
2494
587fec42
LP
2495 case ARG_FORMAT:
2496 if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
2497 log_error("Unknown format: %s", optarg);
2498 return -EINVAL;
2499 }
2500
2501 arg_format = optarg;
2502 break;
2503
1ee306e1
LP
2504 case '?':
2505 return -EINVAL;
2506
2507 default:
eb9da376 2508 assert_not_reached("Unhandled option");
1ee306e1 2509 }
1ee306e1
LP
2510
2511 return 1;
2512}
2513
56159e0d
LP
2514static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2515
2516 static const Verb verbs[] = {
3d7415f4
LP
2517 { "help", VERB_ANY, VERB_ANY, 0, help },
2518 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2519 { "list-images", VERB_ANY, 1, 0, list_images },
2520 { "status", 2, VERB_ANY, 0, show_machine },
160e3793 2521 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
3d7415f4
LP
2522 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2523 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2524 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2525 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2526 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2527 { "kill", 2, VERB_ANY, 0, kill_machine },
2528 { "login", 2, 2, 0, login_machine },
2529 { "bind", 3, 4, 0, bind_mount },
2530 { "copy-to", 3, 4, 0, copy_files },
2531 { "copy-from", 3, 4, 0, copy_files },
2532 { "remove", 2, VERB_ANY, 0, remove_image },
2533 { "rename", 3, 3, 0, rename_image },
2534 { "clone", 3, 3, 0, clone_image },
2535 { "read-only", 2, 3, 0, read_only_image },
2536 { "start", 2, VERB_ANY, 0, start_machine },
2537 { "enable", 2, VERB_ANY, 0, enable_machine },
2538 { "disable", 2, VERB_ANY, 0, enable_machine },
b6e676ce
LP
2539 { "import-tar", 2, 3, 0, import_tar },
2540 { "import-raw", 2, 3, 0, import_raw },
587fec42
LP
2541 { "export-tar", 2, 3, 0, export_tar },
2542 { "export-raw", 2, 3, 0, export_raw },
3d7415f4
LP
2543 { "pull-tar", 2, 3, 0, pull_tar },
2544 { "pull-raw", 2, 3, 0, pull_raw },
2545 { "pull-dkr", 2, 3, 0, pull_dkr },
2546 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2547 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
d6ce17c7 2548 { "set-limit", 2, 3, 0, set_limit },
56159e0d 2549 {}
1ee306e1
LP
2550 };
2551
56159e0d 2552 return dispatch_verb(argc, argv, verbs, bus);
1ee306e1
LP
2553}
2554
2555int main(int argc, char*argv[]) {
24996861 2556 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
84f6181c 2557 int r;
1ee306e1
LP
2558
2559 setlocale(LC_ALL, "");
2560 log_parse_environment();
2561 log_open();
2562
2563 r = parse_argv(argc, argv);
84f6181c 2564 if (r <= 0)
1ee306e1 2565 goto finish;
1ee306e1 2566
d21ed1ea 2567 r = bus_open_transport(arg_transport, arg_host, false, &bus);
a1da8583 2568 if (r < 0) {
da927ba9 2569 log_error_errno(r, "Failed to create bus connection: %m");
a1da8583
TG
2570 goto finish;
2571 }
1ee306e1 2572
2723b3b5
LP
2573 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2574
56159e0d 2575 r = machinectl_main(argc, argv, bus);
1ee306e1
LP
2576
2577finish:
1ee306e1 2578 pager_close();
acf97e21 2579 polkit_agent_close();
1ee306e1 2580
84f6181c
LP
2581 strv_free(arg_property);
2582
2583 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1ee306e1 2584}