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