]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machinectl.c
Merge pull request #1668 from ssahani/net1
[thirdparty/systemd.git] / src / machine / machinectl.c
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
22 #include <arpa/inet.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <getopt.h>
26 #include <locale.h>
27 #include <net/if.h>
28 #include <netinet/in.h>
29 #include <string.h>
30 #include <sys/mount.h>
31 #include <sys/socket.h>
32 #include <unistd.h>
33
34 #include "sd-bus.h"
35
36 #include "bus-error.h"
37 #include "bus-util.h"
38 #include "cgroup-show.h"
39 #include "cgroup-util.h"
40 #include "copy.h"
41 #include "env-util.h"
42 #include "event-util.h"
43 #include "fd-util.h"
44 #include "hostname-util.h"
45 #include "import-util.h"
46 #include "log.h"
47 #include "logs-show.h"
48 #include "macro.h"
49 #include "mkdir.h"
50 #include "pager.h"
51 #include "path-util.h"
52 #include "process-util.h"
53 #include "ptyfwd.h"
54 #include "signal-util.h"
55 #include "spawn-polkit-agent.h"
56 #include "strv.h"
57 #include "terminal-util.h"
58 #include "unit-name.h"
59 #include "util.h"
60 #include "verbs.h"
61
62 static char **arg_property = NULL;
63 static bool arg_all = false;
64 static bool arg_full = false;
65 static bool arg_no_pager = false;
66 static bool arg_legend = true;
67 static const char *arg_kill_who = NULL;
68 static int arg_signal = SIGTERM;
69 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
70 static char *arg_host = NULL;
71 static bool arg_read_only = false;
72 static bool arg_mkdir = false;
73 static bool arg_quiet = false;
74 static bool arg_ask_password = true;
75 static unsigned arg_lines = 10;
76 static OutputMode arg_output = OUTPUT_SHORT;
77 static bool arg_force = false;
78 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
79 static const char* arg_dkr_index_url = NULL;
80 static const char* arg_format = NULL;
81 static const char *arg_uid = NULL;
82 static char **arg_setenv = NULL;
83
84 static void pager_open_if_enabled(void) {
85
86 if (arg_no_pager)
87 return;
88
89 pager_open(false);
90 }
91
92 static void polkit_agent_open_if_enabled(void) {
93
94 /* Open the polkit agent as a child process if necessary */
95
96 if (!arg_ask_password)
97 return;
98
99 if (arg_transport != BUS_TRANSPORT_LOCAL)
100 return;
101
102 polkit_agent_open();
103 }
104
105 static OutputFlags get_output_flags(void) {
106 return
107 arg_all * OUTPUT_SHOW_ALL |
108 arg_full * OUTPUT_FULL_WIDTH |
109 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
110 on_tty() * OUTPUT_COLOR |
111 !arg_quiet * OUTPUT_WARN_CUTOFF;
112 }
113
114 typedef struct MachineInfo {
115 const char *name;
116 const char *class;
117 const char *service;
118 } MachineInfo;
119
120 static int compare_machine_info(const void *a, const void *b) {
121 const MachineInfo *x = a, *y = b;
122
123 return strcmp(x->name, y->name);
124 }
125
126 static int list_machines(int argc, char *argv[], void *userdata) {
127
128 size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
129 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
130 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
131 _cleanup_free_ MachineInfo *machines = NULL;
132 const char *name, *class, *service, *object;
133 size_t n_machines = 0, n_allocated = 0, j;
134 sd_bus *bus = userdata;
135 int r;
136
137 assert(bus);
138
139 pager_open_if_enabled();
140
141 r = sd_bus_call_method(
142 bus,
143 "org.freedesktop.machine1",
144 "/org/freedesktop/machine1",
145 "org.freedesktop.machine1.Manager",
146 "ListMachines",
147 &error,
148 &reply,
149 NULL);
150 if (r < 0) {
151 log_error("Could not get machines: %s", bus_error_message(&error, -r));
152 return r;
153 }
154
155 r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
156 if (r < 0)
157 return bus_log_parse_error(r);
158
159 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
160 size_t l;
161
162 if (name[0] == '.' && !arg_all)
163 continue;
164
165 if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
166 return log_oom();
167
168 machines[n_machines].name = name;
169 machines[n_machines].class = class;
170 machines[n_machines].service = service;
171
172 l = strlen(name);
173 if (l > max_name)
174 max_name = l;
175
176 l = strlen(class);
177 if (l > max_class)
178 max_class = l;
179
180 l = strlen(service);
181 if (l > max_service)
182 max_service = l;
183
184 n_machines ++;
185 }
186 if (r < 0)
187 return bus_log_parse_error(r);
188
189 r = sd_bus_message_exit_container(reply);
190 if (r < 0)
191 return bus_log_parse_error(r);
192
193 qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
194
195 if (arg_legend)
196 printf("%-*s %-*s %-*s\n",
197 (int) max_name, "MACHINE",
198 (int) max_class, "CLASS",
199 (int) max_service, "SERVICE");
200
201 for (j = 0; j < n_machines; j++)
202 printf("%-*s %-*s %-*s\n",
203 (int) max_name, machines[j].name,
204 (int) max_class, machines[j].class,
205 (int) max_service, machines[j].service);
206
207 if (arg_legend)
208 printf("\n%zu machines listed.\n", n_machines);
209
210 return 0;
211 }
212
213 typedef struct ImageInfo {
214 const char *name;
215 const char *type;
216 bool read_only;
217 usec_t crtime;
218 usec_t mtime;
219 uint64_t size;
220 } ImageInfo;
221
222 static int compare_image_info(const void *a, const void *b) {
223 const ImageInfo *x = a, *y = b;
224
225 return strcmp(x->name, y->name);
226 }
227
228 static int list_images(int argc, char *argv[], void *userdata) {
229
230 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
231 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("USAGE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
232 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
233 _cleanup_free_ ImageInfo *images = NULL;
234 size_t n_images = 0, n_allocated = 0, j;
235 const char *name, *type, *object;
236 sd_bus *bus = userdata;
237 uint64_t crtime, mtime, size;
238 int read_only, r;
239
240 assert(bus);
241
242 pager_open_if_enabled();
243
244 r = sd_bus_call_method(
245 bus,
246 "org.freedesktop.machine1",
247 "/org/freedesktop/machine1",
248 "org.freedesktop.machine1.Manager",
249 "ListImages",
250 &error,
251 &reply,
252 "");
253 if (r < 0) {
254 log_error("Could not get images: %s", bus_error_message(&error, -r));
255 return r;
256 }
257
258 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
259 if (r < 0)
260 return bus_log_parse_error(r);
261
262 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
263 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
264 size_t l;
265
266 if (name[0] == '.' && !arg_all)
267 continue;
268
269 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
270 return log_oom();
271
272 images[n_images].name = name;
273 images[n_images].type = type;
274 images[n_images].read_only = read_only;
275 images[n_images].crtime = crtime;
276 images[n_images].mtime = mtime;
277 images[n_images].size = size;
278
279 l = strlen(name);
280 if (l > max_name)
281 max_name = l;
282
283 l = strlen(type);
284 if (l > max_type)
285 max_type = l;
286
287 if (crtime != 0) {
288 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
289 if (l > max_crtime)
290 max_crtime = l;
291 }
292
293 if (mtime != 0) {
294 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
295 if (l > max_mtime)
296 max_mtime = l;
297 }
298
299 if (size != (uint64_t) -1) {
300 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
301 if (l > max_size)
302 max_size = l;
303 }
304
305 n_images++;
306 }
307 if (r < 0)
308 return bus_log_parse_error(r);
309
310 r = sd_bus_message_exit_container(reply);
311 if (r < 0)
312 return bus_log_parse_error(r);
313
314 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
315
316 if (arg_legend)
317 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
318 (int) max_name, "NAME",
319 (int) max_type, "TYPE",
320 "RO",
321 (int) max_size, "USAGE",
322 (int) max_crtime, "CREATED",
323 (int) max_mtime, "MODIFIED");
324
325 for (j = 0; j < n_images; j++) {
326 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
327
328 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
329 (int) max_name, images[j].name,
330 (int) max_type, images[j].type,
331 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_normal() : "",
332 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
333 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
334 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
335 }
336
337 if (arg_legend)
338 printf("\n%zu images listed.\n", n_images);
339
340 return 0;
341 }
342
343 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
344 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
345 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
346 _cleanup_free_ char *path = NULL;
347 const char *cgroup;
348 int r;
349 unsigned c;
350
351 assert(bus);
352 assert(unit);
353
354 if (arg_transport == BUS_TRANSPORT_REMOTE)
355 return 0;
356
357 path = unit_dbus_path_from_name(unit);
358 if (!path)
359 return log_oom();
360
361 r = sd_bus_get_property(
362 bus,
363 "org.freedesktop.systemd1",
364 path,
365 unit_dbus_interface_from_name(unit),
366 "ControlGroup",
367 &error,
368 &reply,
369 "s");
370 if (r < 0) {
371 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
372 return r;
373 }
374
375 r = sd_bus_message_read(reply, "s", &cgroup);
376 if (r < 0)
377 return bus_log_parse_error(r);
378
379 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
380 return 0;
381
382 c = columns();
383 if (c > 18)
384 c -= 18;
385 else
386 c = 0;
387
388 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
389 return 0;
390 }
391
392 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
393 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
394 int r;
395
396 assert(bus);
397 assert(name);
398 assert(prefix);
399 assert(prefix2);
400
401 r = sd_bus_call_method(bus,
402 "org.freedesktop.machine1",
403 "/org/freedesktop/machine1",
404 "org.freedesktop.machine1.Manager",
405 "GetMachineAddresses",
406 NULL,
407 &reply,
408 "s", name);
409 if (r < 0)
410 return r;
411
412 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
413 if (r < 0)
414 return bus_log_parse_error(r);
415
416 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
417 int family;
418 const void *a;
419 size_t sz;
420 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
421
422 r = sd_bus_message_read(reply, "i", &family);
423 if (r < 0)
424 return bus_log_parse_error(r);
425
426 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
427 if (r < 0)
428 return bus_log_parse_error(r);
429
430 fputs(prefix, stdout);
431 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
432 if (family == AF_INET6 && ifi > 0)
433 printf("%%%i", ifi);
434 fputc('\n', stdout);
435
436 r = sd_bus_message_exit_container(reply);
437 if (r < 0)
438 return bus_log_parse_error(r);
439
440 if (prefix != prefix2)
441 prefix = prefix2;
442 }
443 if (r < 0)
444 return bus_log_parse_error(r);
445
446 r = sd_bus_message_exit_container(reply);
447 if (r < 0)
448 return bus_log_parse_error(r);
449
450 return 0;
451 }
452
453 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
454 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
455 const char *k, *v, *pretty = NULL;
456 int r;
457
458 assert(bus);
459 assert(name);
460 assert(prefix);
461
462 r = sd_bus_call_method(bus,
463 "org.freedesktop.machine1",
464 "/org/freedesktop/machine1",
465 "org.freedesktop.machine1.Manager",
466 "GetMachineOSRelease",
467 NULL,
468 &reply,
469 "s", name);
470 if (r < 0)
471 return r;
472
473 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
474 if (r < 0)
475 return bus_log_parse_error(r);
476
477 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
478 if (streq(k, "PRETTY_NAME"))
479 pretty = v;
480
481 }
482 if (r < 0)
483 return bus_log_parse_error(r);
484
485 r = sd_bus_message_exit_container(reply);
486 if (r < 0)
487 return bus_log_parse_error(r);
488
489 if (pretty)
490 printf("%s%s\n", prefix, pretty);
491
492 return 0;
493 }
494
495 typedef struct MachineStatusInfo {
496 char *name;
497 sd_id128_t id;
498 char *class;
499 char *service;
500 char *unit;
501 char *root_directory;
502 pid_t leader;
503 struct dual_timestamp timestamp;
504 int *netif;
505 unsigned n_netif;
506 } MachineStatusInfo;
507
508 static void machine_status_info_clear(MachineStatusInfo *info) {
509 if (info) {
510 free(info->name);
511 free(info->class);
512 free(info->service);
513 free(info->unit);
514 free(info->root_directory);
515 free(info->netif);
516 zero(*info);
517 }
518 }
519
520 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
521 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
522 char since2[FORMAT_TIMESTAMP_MAX], *s2;
523 int ifi = -1;
524
525 assert(bus);
526 assert(i);
527
528 fputs(strna(i->name), stdout);
529
530 if (!sd_id128_equal(i->id, SD_ID128_NULL))
531 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
532 else
533 putchar('\n');
534
535 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
536 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
537
538 if (s1)
539 printf("\t Since: %s; %s\n", s2, s1);
540 else if (s2)
541 printf("\t Since: %s\n", s2);
542
543 if (i->leader > 0) {
544 _cleanup_free_ char *t = NULL;
545
546 printf("\t Leader: %u", (unsigned) i->leader);
547
548 get_process_comm(i->leader, &t);
549 if (t)
550 printf(" (%s)", t);
551
552 putchar('\n');
553 }
554
555 if (i->service) {
556 printf("\t Service: %s", i->service);
557
558 if (i->class)
559 printf("; class %s", i->class);
560
561 putchar('\n');
562 } else if (i->class)
563 printf("\t Class: %s\n", i->class);
564
565 if (i->root_directory)
566 printf("\t Root: %s\n", i->root_directory);
567
568 if (i->n_netif > 0) {
569 unsigned c;
570
571 fputs("\t Iface:", stdout);
572
573 for (c = 0; c < i->n_netif; c++) {
574 char name[IF_NAMESIZE+1] = "";
575
576 if (if_indextoname(i->netif[c], name)) {
577 fputc(' ', stdout);
578 fputs(name, stdout);
579
580 if (ifi < 0)
581 ifi = i->netif[c];
582 else
583 ifi = 0;
584 } else
585 printf(" %i", i->netif[c]);
586 }
587
588 fputc('\n', stdout);
589 }
590
591 print_addresses(bus, i->name, ifi,
592 "\t Address: ",
593 "\t ");
594
595 print_os_release(bus, i->name, "\t OS: ");
596
597 if (i->unit) {
598 printf("\t Unit: %s\n", i->unit);
599 show_unit_cgroup(bus, i->unit, i->leader);
600
601 if (arg_transport == BUS_TRANSPORT_LOCAL)
602
603 show_journal_by_unit(
604 stdout,
605 i->unit,
606 arg_output,
607 0,
608 i->timestamp.monotonic,
609 arg_lines,
610 0,
611 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
612 SD_JOURNAL_LOCAL_ONLY,
613 true,
614 NULL);
615 }
616 }
617
618 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
619 MachineStatusInfo *i = userdata;
620 size_t l;
621 const void *v;
622 int r;
623
624 assert_cc(sizeof(int32_t) == sizeof(int));
625 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
626 if (r < 0)
627 return r;
628 if (r == 0)
629 return -EBADMSG;
630
631 i->n_netif = l / sizeof(int32_t);
632 i->netif = memdup(v, l);
633 if (!i->netif)
634 return -ENOMEM;
635
636 return 0;
637 }
638
639 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
640
641 static const struct bus_properties_map map[] = {
642 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
643 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
644 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
645 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
646 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
647 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
648 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
649 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
650 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
651 { "NetworkInterfaces", "ai", map_netif, 0 },
652 {}
653 };
654
655 _cleanup_(machine_status_info_clear) MachineStatusInfo info = {};
656 int r;
657
658 assert(verb);
659 assert(bus);
660 assert(path);
661 assert(new_line);
662
663 r = bus_map_all_properties(bus,
664 "org.freedesktop.machine1",
665 path,
666 map,
667 &info);
668 if (r < 0)
669 return log_error_errno(r, "Could not get properties: %m");
670
671 if (*new_line)
672 printf("\n");
673 *new_line = true;
674
675 print_machine_status_info(bus, &info);
676
677 return r;
678 }
679
680 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
681 int r;
682
683 assert(bus);
684 assert(path);
685 assert(new_line);
686
687 if (*new_line)
688 printf("\n");
689
690 *new_line = true;
691
692 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
693 if (r < 0)
694 log_error_errno(r, "Could not get properties: %m");
695
696 return r;
697 }
698
699 static int show_machine(int argc, char *argv[], void *userdata) {
700
701 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
702 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
703 bool properties, new_line = false;
704 sd_bus *bus = userdata;
705 int r = 0, i;
706
707 assert(bus);
708
709 properties = !strstr(argv[0], "status");
710
711 pager_open_if_enabled();
712
713 if (properties && argc <= 1) {
714
715 /* If no argument is specified, inspect the manager
716 * itself */
717 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
718 if (r < 0)
719 return r;
720 }
721
722 for (i = 1; i < argc; i++) {
723 const char *path = NULL;
724
725 r = sd_bus_call_method(
726 bus,
727 "org.freedesktop.machine1",
728 "/org/freedesktop/machine1",
729 "org.freedesktop.machine1.Manager",
730 "GetMachine",
731 &error,
732 &reply,
733 "s", argv[i]);
734 if (r < 0) {
735 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
736 return r;
737 }
738
739 r = sd_bus_message_read(reply, "o", &path);
740 if (r < 0)
741 return bus_log_parse_error(r);
742
743 if (properties)
744 r = show_machine_properties(bus, path, &new_line);
745 else
746 r = show_machine_info(argv[0], bus, path, &new_line);
747 }
748
749 return r;
750 }
751
752 typedef struct ImageStatusInfo {
753 char *name;
754 char *path;
755 char *type;
756 int read_only;
757 usec_t crtime;
758 usec_t mtime;
759 uint64_t usage;
760 uint64_t limit;
761 uint64_t usage_exclusive;
762 uint64_t limit_exclusive;
763 } ImageStatusInfo;
764
765 static void image_status_info_clear(ImageStatusInfo *info) {
766 if (info) {
767 free(info->name);
768 free(info->path);
769 free(info->type);
770 zero(*info);
771 }
772 }
773
774 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
775 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
776 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
777 char bs[FORMAT_BYTES_MAX], *s3;
778 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
779
780 assert(bus);
781 assert(i);
782
783 if (i->name) {
784 fputs(i->name, stdout);
785 putchar('\n');
786 }
787
788 if (i->type)
789 printf("\t Type: %s\n", i->type);
790
791 if (i->path)
792 printf("\t Path: %s\n", i->path);
793
794 printf("\t RO: %s%s%s\n",
795 i->read_only ? ansi_highlight_red() : "",
796 i->read_only ? "read-only" : "writable",
797 i->read_only ? ansi_normal() : "");
798
799 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
800 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
801 if (s1 && s2)
802 printf("\t Created: %s; %s\n", s2, s1);
803 else if (s2)
804 printf("\t Created: %s\n", s2);
805
806 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
807 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
808 if (s1 && s2)
809 printf("\tModified: %s; %s\n", s2, s1);
810 else if (s2)
811 printf("\tModified: %s\n", s2);
812
813 s3 = format_bytes(bs, sizeof(bs), i->usage);
814 s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
815 if (s3 && s4)
816 printf("\t Usage: %s (exclusive: %s)\n", s3, s4);
817 else if (s3)
818 printf("\t Usage: %s\n", s3);
819
820 s3 = format_bytes(bs, sizeof(bs), i->limit);
821 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
822 if (s3 && s4)
823 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
824 else if (s3)
825 printf("\t Limit: %s\n", s3);
826 }
827
828 static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
829
830 static const struct bus_properties_map map[] = {
831 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
832 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
833 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
834 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
835 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
836 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
837 { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
838 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
839 { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
840 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
841 {}
842 };
843
844 _cleanup_(image_status_info_clear) ImageStatusInfo info = {};
845 int r;
846
847 assert(bus);
848 assert(path);
849 assert(new_line);
850
851 r = bus_map_all_properties(bus,
852 "org.freedesktop.machine1",
853 path,
854 map,
855 &info);
856 if (r < 0)
857 return log_error_errno(r, "Could not get properties: %m");
858
859 if (*new_line)
860 printf("\n");
861 *new_line = true;
862
863 print_image_status_info(bus, &info);
864
865 return r;
866 }
867
868 typedef struct PoolStatusInfo {
869 char *path;
870 uint64_t usage;
871 uint64_t limit;
872 } PoolStatusInfo;
873
874 static void pool_status_info_clear(PoolStatusInfo *info) {
875 if (info) {
876 free(info->path);
877 zero(*info);
878 info->usage = -1;
879 info->limit = -1;
880 }
881 }
882
883 static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
884 char bs[FORMAT_BYTES_MAX], *s;
885
886 if (i->path)
887 printf("\t Path: %s\n", i->path);
888
889 s = format_bytes(bs, sizeof(bs), i->usage);
890 if (s)
891 printf("\t Usage: %s\n", s);
892
893 s = format_bytes(bs, sizeof(bs), i->limit);
894 if (s)
895 printf("\t Limit: %s\n", s);
896 }
897
898 static int show_pool_info(sd_bus *bus) {
899
900 static const struct bus_properties_map map[] = {
901 { "PoolPath", "s", NULL, offsetof(PoolStatusInfo, path) },
902 { "PoolUsage", "t", NULL, offsetof(PoolStatusInfo, usage) },
903 { "PoolLimit", "t", NULL, offsetof(PoolStatusInfo, limit) },
904 {}
905 };
906
907 _cleanup_(pool_status_info_clear) PoolStatusInfo info = {
908 .usage = (uint64_t) -1,
909 .limit = (uint64_t) -1,
910 };
911 int r;
912
913 assert(bus);
914
915 r = bus_map_all_properties(bus,
916 "org.freedesktop.machine1",
917 "/org/freedesktop/machine1",
918 map,
919 &info);
920 if (r < 0)
921 return log_error_errno(r, "Could not get properties: %m");
922
923 print_pool_status_info(bus, &info);
924
925 return 0;
926 }
927
928
929 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
930 int r;
931
932 assert(bus);
933 assert(path);
934 assert(new_line);
935
936 if (*new_line)
937 printf("\n");
938
939 *new_line = true;
940
941 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
942 if (r < 0)
943 log_error_errno(r, "Could not get properties: %m");
944
945 return r;
946 }
947
948 static int show_image(int argc, char *argv[], void *userdata) {
949
950 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
951 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
952 bool properties, new_line = false;
953 sd_bus *bus = userdata;
954 int r = 0, i;
955
956 assert(bus);
957
958 properties = !strstr(argv[0], "status");
959
960 pager_open_if_enabled();
961
962 if (argc <= 1) {
963
964 /* If no argument is specified, inspect the manager
965 * itself */
966
967 if (properties)
968 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
969 else
970 r = show_pool_info(bus);
971 if (r < 0)
972 return r;
973 }
974
975 for (i = 1; i < argc; i++) {
976 const char *path = NULL;
977
978 r = sd_bus_call_method(
979 bus,
980 "org.freedesktop.machine1",
981 "/org/freedesktop/machine1",
982 "org.freedesktop.machine1.Manager",
983 "GetImage",
984 &error,
985 &reply,
986 "s", argv[i]);
987 if (r < 0) {
988 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
989 return r;
990 }
991
992 r = sd_bus_message_read(reply, "o", &path);
993 if (r < 0)
994 return bus_log_parse_error(r);
995
996 if (properties)
997 r = show_image_properties(bus, path, &new_line);
998 else
999 r = show_image_info(bus, path, &new_line);
1000 }
1001
1002 return r;
1003 }
1004
1005 static int kill_machine(int argc, char *argv[], void *userdata) {
1006 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1007 sd_bus *bus = userdata;
1008 int r, i;
1009
1010 assert(bus);
1011
1012 polkit_agent_open_if_enabled();
1013
1014 if (!arg_kill_who)
1015 arg_kill_who = "all";
1016
1017 for (i = 1; i < argc; i++) {
1018 r = sd_bus_call_method(
1019 bus,
1020 "org.freedesktop.machine1",
1021 "/org/freedesktop/machine1",
1022 "org.freedesktop.machine1.Manager",
1023 "KillMachine",
1024 &error,
1025 NULL,
1026 "ssi", argv[i], arg_kill_who, arg_signal);
1027 if (r < 0) {
1028 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
1029 return r;
1030 }
1031 }
1032
1033 return 0;
1034 }
1035
1036 static int reboot_machine(int argc, char *argv[], void *userdata) {
1037 arg_kill_who = "leader";
1038 arg_signal = SIGINT; /* sysvinit + systemd */
1039
1040 return kill_machine(argc, argv, userdata);
1041 }
1042
1043 static int poweroff_machine(int argc, char *argv[], void *userdata) {
1044 arg_kill_who = "leader";
1045 arg_signal = SIGRTMIN+4; /* only systemd */
1046
1047 return kill_machine(argc, argv, userdata);
1048 }
1049
1050 static int terminate_machine(int argc, char *argv[], void *userdata) {
1051 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1052 sd_bus *bus = userdata;
1053 int r, i;
1054
1055 assert(bus);
1056
1057 polkit_agent_open_if_enabled();
1058
1059 for (i = 1; i < argc; i++) {
1060 r = sd_bus_call_method(
1061 bus,
1062 "org.freedesktop.machine1",
1063 "/org/freedesktop/machine1",
1064 "org.freedesktop.machine1.Manager",
1065 "TerminateMachine",
1066 &error,
1067 NULL,
1068 "s", argv[i]);
1069 if (r < 0) {
1070 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
1071 return r;
1072 }
1073 }
1074
1075 return 0;
1076 }
1077
1078 static int copy_files(int argc, char *argv[], void *userdata) {
1079 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1080 _cleanup_free_ char *abs_host_path = NULL;
1081 char *dest, *host_path, *container_path;
1082 sd_bus *bus = userdata;
1083 bool copy_from;
1084 int r;
1085
1086 assert(bus);
1087
1088 polkit_agent_open_if_enabled();
1089
1090 copy_from = streq(argv[0], "copy-from");
1091 dest = argv[3] ?: argv[2];
1092 host_path = copy_from ? dest : argv[2];
1093 container_path = copy_from ? argv[2] : dest;
1094
1095 if (!path_is_absolute(host_path)) {
1096 r = path_make_absolute_cwd(host_path, &abs_host_path);
1097 if (r < 0)
1098 return log_error_errno(r, "Failed to make path absolute: %m");
1099
1100 host_path = abs_host_path;
1101 }
1102
1103 r = sd_bus_call_method(
1104 bus,
1105 "org.freedesktop.machine1",
1106 "/org/freedesktop/machine1",
1107 "org.freedesktop.machine1.Manager",
1108 copy_from ? "CopyFromMachine" : "CopyToMachine",
1109 &error,
1110 NULL,
1111 "sss",
1112 argv[1],
1113 copy_from ? container_path : host_path,
1114 copy_from ? host_path : container_path);
1115 if (r < 0)
1116 return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r));
1117
1118 return 0;
1119 }
1120
1121 static int bind_mount(int argc, char *argv[], void *userdata) {
1122 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1123 sd_bus *bus = userdata;
1124 int r;
1125
1126 assert(bus);
1127
1128 polkit_agent_open_if_enabled();
1129
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);
1144 if (r < 0) {
1145 log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
1146 return r;
1147 }
1148
1149 return 0;
1150 }
1151
1152 static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1153 PTYForward ** forward = (PTYForward**) userdata;
1154 int r;
1155
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 */
1172 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
1173 return 0;
1174 }
1175
1176 static int process_forward(sd_event *event, PTYForward **forward, int master, PTYForwardFlags flags, 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, flags, 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 (flags & PTY_FORWARD_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
1225 static int login_machine(int argc, char *argv[], void *userdata) {
1226 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1227 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1228 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1229 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1230 _cleanup_event_unref_ sd_event *event = NULL;
1231 int master = -1, r;
1232 sd_bus *bus = userdata;
1233 const char *pty, *match, *machine;
1234
1235 assert(bus);
1236
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
1242 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1243 arg_transport != BUS_TRANSPORT_MACHINE) {
1244 log_error("Login only supported on local machines.");
1245 return -EOPNOTSUPP;
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
1258 machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1];
1259
1260 match = strjoina("type='signal',"
1261 "sender='org.freedesktop.machine1',"
1262 "path='/org/freedesktop/machine1',",
1263 "interface='org.freedesktop.machine1.Manager',"
1264 "member='MachineRemoved',"
1265 "arg0='", machine, "'");
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
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,
1279 "s", machine);
1280 if (r < 0) {
1281 log_error("Failed to get login PTY: %s", bus_error_message(&error, -r));
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
1289 return process_forward(event, &forward, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
1290 }
1291
1292 static 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;
1301
1302 assert(bus);
1303
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 }
1309
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);
1324 if (r < 0)
1325 return log_error_errno(r, "Failed to get event loop: %m");
1326
1327 r = sd_bus_attach_event(bus, event, 0);
1328 if (r < 0)
1329 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1330
1331 machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1];
1332
1333 if (arg_uid)
1334 uid = arg_uid;
1335 else if (machine) {
1336 const char *at;
1337
1338 at = strchr(machine, '@');
1339 if (at) {
1340 uid = strndupa(machine, at - machine);
1341 machine = at + 1;
1342 }
1343 }
1344
1345 if (isempty(machine))
1346 machine = ".host";
1347
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, 0, machine);
1394 }
1395
1396 static 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
1424 static 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
1448 static 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
1472 static 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
1504 static 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
1527 static 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++) {
1542 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1543 _cleanup_free_ char *unit = NULL;
1544 const char *object;
1545
1546 r = make_service_name(argv[i], &unit);
1547 if (r < 0)
1548 return r;
1549
1550 r = sd_bus_call_method(
1551 bus,
1552 "org.freedesktop.systemd1",
1553 "/org/freedesktop/systemd1",
1554 "org.freedesktop.systemd1.Manager",
1555 "StartUnit",
1556 &error,
1557 &reply,
1558 "ss", unit, "fail");
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
1580 static 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
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++) {
1609 _cleanup_free_ char *unit = NULL;
1610
1611 r = make_service_name(argv[i], &unit);
1612 if (r < 0)
1613 return r;
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
1643 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
1644 if (r < 0)
1645 return r;
1646
1647 r = sd_bus_call_method(
1648 bus,
1649 "org.freedesktop.systemd1",
1650 "/org/freedesktop/systemd1",
1651 "org.freedesktop.systemd1.Manager",
1652 "Reload",
1653 &error,
1654 NULL,
1655 NULL);
1656 if (r < 0) {
1657 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1658 return r;
1659 }
1660
1661 return 0;
1662 }
1663
1664 static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1665 const char **our_path = userdata, *line;
1666 unsigned priority;
1667 int r;
1668
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;
1676 }
1677
1678 if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
1679 return 0;
1680
1681 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1682 return 0;
1683
1684 log_full(priority, "%s", line);
1685 return 0;
1686 }
1687
1688 static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1689 const char **our_path = userdata, *path, *result;
1690 uint32_t id;
1691 int r;
1692
1693 assert(m);
1694 assert(our_path);
1695
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 }
1701
1702 if (!streq_ptr(*our_path, path))
1703 return 0;
1704
1705 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done"));
1706 return 0;
1707 }
1708
1709 static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1710 assert(s);
1711 assert(si);
1712
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;
1718 }
1719
1720 static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
1721 _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1722 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
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;
1728
1729 assert(bus);
1730 assert(m);
1731
1732 polkit_agent_open_if_enabled();
1733
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
1742 r = sd_bus_add_match(
1743 bus,
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);
1766 if (r < 0) {
1767 log_error("Failed transfer image: %s", bus_error_message(&error, -r));
1768 return r;
1769 }
1770
1771 r = sd_bus_message_read(reply, "uo", &id, &path);
1772 if (r < 0)
1773 return bus_log_parse_error(r);
1774
1775 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
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
1790 static 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
1857 static 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
1924 static 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
1939 static 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
1989 static 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
2039 static 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)
2070 return log_oom();
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(
2081 bus,
2082 &m,
2083 "org.freedesktop.import1",
2084 "/org/freedesktop/import1",
2085 "org.freedesktop.import1.Manager",
2086 "PullTar");
2087 if (r < 0)
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);
2099
2100 return transfer_image_common(bus, m);
2101 }
2102
2103 static 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)
2134 return log_oom();
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");
2151 if (r < 0)
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
2164 return transfer_image_common(bus, m);
2165 }
2166
2167 static 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;
2172
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 }
2177
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 }
2212 }
2213
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");
2221 if (r < 0)
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);
2235
2236 return transfer_image_common(bus, m);
2237 }
2238
2239 typedef struct TransferInfo {
2240 uint32_t id;
2241 const char *type;
2242 const char *remote;
2243 const char *local;
2244 double progress;
2245 } TransferInfo;
2246
2247 static int compare_transfer_info(const void *a, const void *b) {
2248 const TransferInfo *x = a, *y = b;
2249
2250 return strcmp(x->local, y->local);
2251 }
2252
2253 static 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);
2276 if (r < 0) {
2277 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2278 return r;
2279 }
2280
2281 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
2282 if (r < 0)
2283 return bus_log_parse_error(r);
2284
2285 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
2286 size_t l;
2287
2288 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2289 return log_oom();
2290
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;
2296
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 }
2314 if (r < 0)
2315 return bus_log_parse_error(r);
2316
2317 r = sd_bus_message_exit_container(reply);
2318 if (r < 0)
2319 return bus_log_parse_error(r);
2320
2321 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2322
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);
2338
2339 if (arg_legend)
2340 printf("\n%zu transfers listed.\n", n_transfers);
2341
2342 return 0;
2343 }
2344
2345 static 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;
2377 }
2378
2379 static 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 (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
2386 limit = (uint64_t) -1;
2387 else {
2388 r = parse_size(argv[argc-1], 1024, &limit);
2389 if (r < 0)
2390 return log_error("Failed to parse size: %s", argv[argc-1]);
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
2425 static int help(int argc, char *argv[], void *userdata) {
2426
2427 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
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"
2443 " --uid=USER Specify user ID to invoke shell as\n"
2444 " --setenv=VAR=VALUE Add an environment variable for shell\n"
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"
2459 " show [NAME...] Show properties of one or more VMs/containers\n"
2460 " start NAME... Start container as a service\n"
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"
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"
2477 " image-status [NAME...] Show image details\n"
2478 " show-image [NAME...] Show properties of image\n"
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"
2482 " remove NAME... Remove an image\n"
2483 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
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"
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"
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;
2497 }
2498
2499 static int parse_argv(int argc, char *argv[]) {
2500
2501 enum {
2502 ARG_VERSION = 0x100,
2503 ARG_NO_PAGER,
2504 ARG_NO_LEGEND,
2505 ARG_KILL_WHO,
2506 ARG_READ_ONLY,
2507 ARG_MKDIR,
2508 ARG_NO_ASK_PASSWORD,
2509 ARG_VERIFY,
2510 ARG_FORCE,
2511 ARG_DKR_INDEX_URL,
2512 ARG_FORMAT,
2513 ARG_UID,
2514 ARG_SETENV,
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 },
2524 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2525 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2526 { "signal", required_argument, NULL, 's' },
2527 { "host", required_argument, NULL, 'H' },
2528 { "machine", required_argument, NULL, 'M' },
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 },
2538 { "format", required_argument, NULL, ARG_FORMAT },
2539 { "uid", required_argument, NULL, ARG_UID },
2540 { "setenv", required_argument, NULL, ARG_SETENV },
2541 {}
2542 };
2543
2544 int c, r;
2545
2546 assert(argc >= 0);
2547 assert(argv);
2548
2549 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2550
2551 switch (c) {
2552
2553 case 'h':
2554 return help(0, NULL, NULL);
2555
2556 case ARG_VERSION:
2557 return version();
2558
2559 case 'p':
2560 r = strv_extend(&arg_property, optarg);
2561 if (r < 0)
2562 return log_oom();
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;
2569
2570 case 'a':
2571 arg_all = true;
2572 break;
2573
2574 case 'l':
2575 arg_full = true;
2576 break;
2577
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
2593 case ARG_NO_PAGER:
2594 arg_no_pager = true;
2595 break;
2596
2597 case ARG_NO_LEGEND:
2598 arg_legend = false;
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
2613 case ARG_NO_ASK_PASSWORD:
2614 arg_ask_password = false;
2615 break;
2616
2617 case 'H':
2618 arg_transport = BUS_TRANSPORT_REMOTE;
2619 arg_host = optarg;
2620 break;
2621
2622 case 'M':
2623 arg_transport = BUS_TRANSPORT_MACHINE;
2624 arg_host = optarg;
2625 break;
2626
2627 case ARG_READ_ONLY:
2628 arg_read_only = true;
2629 break;
2630
2631 case ARG_MKDIR:
2632 arg_mkdir = true;
2633 break;
2634
2635 case 'q':
2636 arg_quiet = true;
2637 break;
2638
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;
2646
2647 case ARG_FORCE:
2648 arg_force = true;
2649 break;
2650
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 }
2656
2657 arg_dkr_index_url = optarg;
2658 break;
2659
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
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
2684 case '?':
2685 return -EINVAL;
2686
2687 default:
2688 assert_not_reached("Unhandled option");
2689 }
2690
2691 return 1;
2692 }
2693
2694 static 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 },
2701 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
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 },
2708 { "login", VERB_ANY, 2, 0, login_machine },
2709 { "shell", VERB_ANY, VERB_ANY, 0, shell_machine },
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 },
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 },
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 },
2729 { "set-limit", 2, 3, 0, set_limit },
2730 {}
2731 };
2732
2733 return dispatch_verb(argc, argv, verbs, bus);
2734 }
2735
2736 int main(int argc, char*argv[]) {
2737 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
2738 int r;
2739
2740 setlocale(LC_ALL, "");
2741 log_parse_environment();
2742 log_open();
2743
2744 r = parse_argv(argc, argv);
2745 if (r <= 0)
2746 goto finish;
2747
2748 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
2749 if (r < 0) {
2750 log_error_errno(r, "Failed to create bus connection: %m");
2751 goto finish;
2752 }
2753
2754 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2755
2756 r = machinectl_main(argc, argv, bus);
2757
2758 finish:
2759 pager_close();
2760 polkit_agent_close();
2761
2762 strv_free(arg_property);
2763 strv_free(arg_setenv);
2764
2765 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2766 }