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