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