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