]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machinectl.c
bb8c5ac64b6f92bbbf3eb779725ec6816287796e
[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 <sys/socket.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <getopt.h>
27 #include <locale.h>
28 #include <fcntl.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <net/if.h>
32 #include <sys/mount.h>
33
34 #include "sd-bus.h"
35 #include "log.h"
36 #include "util.h"
37 #include "macro.h"
38 #include "pager.h"
39 #include "spawn-polkit-agent.h"
40 #include "bus-util.h"
41 #include "bus-error.h"
42 #include "build.h"
43 #include "strv.h"
44 #include "unit-name.h"
45 #include "cgroup-show.h"
46 #include "logs-show.h"
47 #include "cgroup-util.h"
48 #include "ptyfwd.h"
49 #include "event-util.h"
50 #include "path-util.h"
51 #include "mkdir.h"
52 #include "copy.h"
53 #include "verbs.h"
54 #include "import-util.h"
55 #include "process-util.h"
56 #include "terminal-util.h"
57 #include "signal-util.h"
58 #include "env-util.h"
59 #include "hostname-util.h"
60
61 static char **arg_property = NULL;
62 static bool arg_all = false;
63 static bool arg_full = false;
64 static bool arg_no_pager = false;
65 static bool arg_legend = true;
66 static const char *arg_kill_who = NULL;
67 static int arg_signal = SIGTERM;
68 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
69 static char *arg_host = NULL;
70 static bool arg_read_only = false;
71 static bool arg_mkdir = false;
72 static bool arg_quiet = false;
73 static bool arg_ask_password = true;
74 static unsigned arg_lines = 10;
75 static OutputMode arg_output = OUTPUT_SHORT;
76 static bool arg_force = false;
77 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
78 static const char* arg_dkr_index_url = NULL;
79 static const char* arg_format = NULL;
80 static const char *arg_uid = NULL;
81 static char **arg_setenv = NULL;
82
83 static void pager_open_if_enabled(void) {
84
85 if (arg_no_pager)
86 return;
87
88 pager_open(false);
89 }
90
91 static 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
104 static 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
113 typedef struct MachineInfo {
114 const char *name;
115 const char *class;
116 const char *service;
117 } MachineInfo;
118
119 static 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
125 static 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");
128 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
129 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
130 _cleanup_free_ MachineInfo *machines = NULL;
131 const char *name, *class, *service, *object;
132 size_t n_machines = 0, n_allocated = 0, j;
133 sd_bus *bus = userdata;
134 int r;
135
136 assert(bus);
137
138 pager_open_if_enabled();
139
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,
148 NULL);
149 if (r < 0) {
150 log_error("Could not get machines: %s", bus_error_message(&error, -r));
151 return r;
152 }
153
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
161 if (name[0] == '.' && !arg_all)
162 continue;
163
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
206 if (arg_legend)
207 printf("\n%zu machines listed.\n", n_machines);
208
209 return 0;
210 }
211
212 typedef 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
221 static 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
227 static 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 }
256
257 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
258 if (r < 0)
259 return bus_log_parse_error(r);
260
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 }
303
304 n_images++;
305 }
306 if (r < 0)
307 return bus_log_parse_error(r);
308
309 r = sd_bus_message_exit_container(reply);
310 if (r < 0)
311 return bus_log_parse_error(r);
312
313 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
314
315 if (arg_legend)
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,
330 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
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);
338
339 return 0;
340 }
341
342 static 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;
345 _cleanup_free_ char *path = NULL;
346 const char *cgroup;
347 int r;
348 unsigned c;
349
350 assert(bus);
351 assert(unit);
352
353 if (arg_transport == BUS_TRANSPORT_REMOTE)
354 return 0;
355
356 path = unit_dbus_path_from_name(unit);
357 if (!path)
358 return log_oom();
359
360 r = sd_bus_get_property(
361 bus,
362 "org.freedesktop.systemd1",
363 path,
364 unit_dbus_interface_from_name(unit),
365 "ControlGroup",
366 &error,
367 &reply,
368 "s");
369 if (r < 0) {
370 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
371 return r;
372 }
373
374 r = sd_bus_message_read(reply, "s", &cgroup);
375 if (r < 0)
376 return bus_log_parse_error(r);
377
378 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
379 return 0;
380
381 c = columns();
382 if (c > 18)
383 c -= 18;
384 else
385 c = 0;
386
387 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
388 return 0;
389 }
390
391 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
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
411 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
412 if (r < 0)
413 return bus_log_parse_error(r);
414
415 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
416 int family;
417 const void *a;
418 size_t sz;
419 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
420
421 r = sd_bus_message_read(reply, "i", &family);
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
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);
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
452 static 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
494 typedef struct MachineStatusInfo {
495 char *name;
496 sd_id128_t id;
497 char *class;
498 char *service;
499 char *unit;
500 char *root_directory;
501 pid_t leader;
502 struct dual_timestamp timestamp;
503 int *netif;
504 unsigned n_netif;
505 } MachineStatusInfo;
506
507 static 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
519 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
520 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
521 char since2[FORMAT_TIMESTAMP_MAX], *s2;
522 int ifi = -1;
523
524 assert(bus);
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
534 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
535 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
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
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,
591 "\t Address: ",
592 "\t ");
593
594 print_os_release(bus, i->name, "\t OS: ");
595
596 if (i->unit) {
597 printf("\t Unit: %s\n", i->unit);
598 show_unit_cgroup(bus, i->unit, i->leader);
599
600 if (arg_transport == BUS_TRANSPORT_LOCAL) {
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);
614 }
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_highlight_off() : "");
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 abs_host_path = path_make_absolute_cwd(host_path);
1097 if (!abs_host_path)
1098 return log_oom();
1099 host_path = abs_host_path;
1100 }
1101
1102 r = sd_bus_call_method(
1103 bus,
1104 "org.freedesktop.machine1",
1105 "/org/freedesktop/machine1",
1106 "org.freedesktop.machine1.Manager",
1107 copy_from ? "CopyFromMachine" : "CopyToMachine",
1108 &error,
1109 NULL,
1110 "sss",
1111 argv[1],
1112 copy_from ? container_path : host_path,
1113 copy_from ? host_path : container_path);
1114 if (r < 0) {
1115 log_error("Failed to copy: %s", bus_error_message(&error, -r));
1116 return r;
1117 }
1118
1119 return 0;
1120 }
1121
1122 static int bind_mount(int argc, char *argv[], void *userdata) {
1123 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1124 sd_bus *bus = userdata;
1125 int r;
1126
1127 assert(bus);
1128
1129 polkit_agent_open_if_enabled();
1130
1131 r = sd_bus_call_method(
1132 bus,
1133 "org.freedesktop.machine1",
1134 "/org/freedesktop/machine1",
1135 "org.freedesktop.machine1.Manager",
1136 "BindMountMachine",
1137 &error,
1138 NULL,
1139 "sssbb",
1140 argv[1],
1141 argv[2],
1142 argv[3],
1143 arg_read_only,
1144 arg_mkdir);
1145 if (r < 0) {
1146 log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
1147 return r;
1148 }
1149
1150 return 0;
1151 }
1152
1153 static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1154 PTYForward ** forward = (PTYForward**) userdata;
1155 int r;
1156
1157 assert(m);
1158 assert(forward);
1159
1160 if (*forward) {
1161 /* If the forwarder is already initialized, tell it to
1162 * exit on the next vhangup(), so that we still flush
1163 * out what might be queued and exit then. */
1164
1165 r = pty_forward_set_ignore_vhangup(*forward, false);
1166 if (r >= 0)
1167 return 0;
1168
1169 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
1170 }
1171
1172 /* On error, or when the forwarder is not initialized yet, quit immediately */
1173 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
1174 return 0;
1175 }
1176
1177 static int process_forward(sd_event *event, PTYForward **forward, int master, bool ignore_vhangup, const char *name) {
1178 char last_char = 0;
1179 bool machine_died;
1180 int ret = 0, r;
1181
1182 assert(event);
1183 assert(master >= 0);
1184 assert(name);
1185
1186 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
1187
1188 if (streq(name, ".host"))
1189 log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
1190 else
1191 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
1192
1193 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1194 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1195
1196 r = pty_forward_new(event, master, ignore_vhangup, false, forward);
1197 if (r < 0)
1198 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1199
1200 r = sd_event_loop(event);
1201 if (r < 0)
1202 return log_error_errno(r, "Failed to run event loop: %m");
1203
1204 pty_forward_get_last_char(*forward, &last_char);
1205
1206 machine_died =
1207 ignore_vhangup &&
1208 pty_forward_get_ignore_vhangup(*forward) == 0;
1209
1210 *forward = pty_forward_free(*forward);
1211
1212 if (last_char != '\n')
1213 fputc('\n', stdout);
1214
1215 if (machine_died)
1216 log_info("Machine %s terminated.", name);
1217 else if (streq(name, ".host"))
1218 log_info("Connection to the local host terminated.");
1219 else
1220 log_info("Connection to machine %s terminated.", name);
1221
1222 sd_event_get_exit_code(event, &ret);
1223 return ret;
1224 }
1225
1226 static int login_machine(int argc, char *argv[], void *userdata) {
1227 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1228 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1229 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1230 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1231 _cleanup_event_unref_ sd_event *event = NULL;
1232 int master = -1, r;
1233 sd_bus *bus = userdata;
1234 const char *pty, *match, *machine;
1235
1236 assert(bus);
1237
1238 if (!strv_isempty(arg_setenv) || arg_uid) {
1239 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1240 return -EINVAL;
1241 }
1242
1243 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1244 arg_transport != BUS_TRANSPORT_MACHINE) {
1245 log_error("Login only supported on local machines.");
1246 return -EOPNOTSUPP;
1247 }
1248
1249 polkit_agent_open_if_enabled();
1250
1251 r = sd_event_default(&event);
1252 if (r < 0)
1253 return log_error_errno(r, "Failed to get event loop: %m");
1254
1255 r = sd_bus_attach_event(bus, event, 0);
1256 if (r < 0)
1257 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1258
1259 machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1];
1260
1261 match = strjoina("type='signal',"
1262 "sender='org.freedesktop.machine1',"
1263 "path='/org/freedesktop/machine1',",
1264 "interface='org.freedesktop.machine1.Manager',"
1265 "member='MachineRemoved',"
1266 "arg0='", machine, "'");
1267
1268 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1269 if (r < 0)
1270 return log_error_errno(r, "Failed to add machine removal match: %m");
1271
1272 r = sd_bus_call_method(
1273 bus,
1274 "org.freedesktop.machine1",
1275 "/org/freedesktop/machine1",
1276 "org.freedesktop.machine1.Manager",
1277 "OpenMachineLogin",
1278 &error,
1279 &reply,
1280 "s", machine);
1281 if (r < 0) {
1282 log_error("Failed to get login PTY: %s", bus_error_message(&error, -r));
1283 return r;
1284 }
1285
1286 r = sd_bus_message_read(reply, "hs", &master, &pty);
1287 if (r < 0)
1288 return bus_log_parse_error(r);
1289
1290 return process_forward(event, &forward, master, true, machine);
1291 }
1292
1293 static int shell_machine(int argc, char *argv[], void *userdata) {
1294 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
1295 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1296 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1297 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1298 _cleanup_event_unref_ sd_event *event = NULL;
1299 int master = -1, r;
1300 sd_bus *bus = userdata;
1301 const char *pty, *match, *machine, *path, *uid = NULL;
1302
1303 assert(bus);
1304
1305 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1306 arg_transport != BUS_TRANSPORT_MACHINE) {
1307 log_error("Shell only supported on local machines.");
1308 return -EOPNOTSUPP;
1309 }
1310
1311 /* Pass $TERM to shell session, if not explicitly specified. */
1312 if (!strv_find_prefix(arg_setenv, "TERM=")) {
1313 const char *t;
1314
1315 t = strv_find_prefix(environ, "TERM=");
1316 if (t) {
1317 if (strv_extend(&arg_setenv, t) < 0)
1318 return log_oom();
1319 }
1320 }
1321
1322 polkit_agent_open_if_enabled();
1323
1324 r = sd_event_default(&event);
1325 if (r < 0)
1326 return log_error_errno(r, "Failed to get event loop: %m");
1327
1328 r = sd_bus_attach_event(bus, event, 0);
1329 if (r < 0)
1330 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1331
1332 machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1];
1333
1334 if (arg_uid)
1335 uid = arg_uid;
1336 else if (machine) {
1337 const char *at;
1338
1339 at = strchr(machine, '@');
1340 if (at) {
1341 uid = strndupa(machine, at - machine);
1342 machine = at + 1;
1343 }
1344 }
1345
1346 if (isempty(machine))
1347 machine = ".host";
1348
1349 match = strjoina("type='signal',"
1350 "sender='org.freedesktop.machine1',"
1351 "path='/org/freedesktop/machine1',",
1352 "interface='org.freedesktop.machine1.Manager',"
1353 "member='MachineRemoved',"
1354 "arg0='", machine, "'");
1355
1356 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1357 if (r < 0)
1358 return log_error_errno(r, "Failed to add machine removal match: %m");
1359
1360 r = sd_bus_message_new_method_call(
1361 bus,
1362 &m,
1363 "org.freedesktop.machine1",
1364 "/org/freedesktop/machine1",
1365 "org.freedesktop.machine1.Manager",
1366 "OpenMachineShell");
1367 if (r < 0)
1368 return bus_log_create_error(r);
1369
1370 path = argc < 3 || isempty(argv[2]) ? NULL : argv[2];
1371
1372 r = sd_bus_message_append(m, "sss", machine, uid, path);
1373 if (r < 0)
1374 return bus_log_create_error(r);
1375
1376 r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2);
1377 if (r < 0)
1378 return bus_log_create_error(r);
1379
1380 r = sd_bus_message_append_strv(m, arg_setenv);
1381 if (r < 0)
1382 return bus_log_create_error(r);
1383
1384 r = sd_bus_call(bus, m, 0, &error, &reply);
1385 if (r < 0) {
1386 log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r));
1387 return r;
1388 }
1389
1390 r = sd_bus_message_read(reply, "hs", &master, &pty);
1391 if (r < 0)
1392 return bus_log_parse_error(r);
1393
1394 return process_forward(event, &forward, master, false, machine);
1395 }
1396
1397 static int remove_image(int argc, char *argv[], void *userdata) {
1398 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1399 sd_bus *bus = userdata;
1400 int r, i;
1401
1402 assert(bus);
1403
1404 polkit_agent_open_if_enabled();
1405
1406 for (i = 1; i < argc; i++) {
1407 r = sd_bus_call_method(
1408 bus,
1409 "org.freedesktop.machine1",
1410 "/org/freedesktop/machine1",
1411 "org.freedesktop.machine1.Manager",
1412 "RemoveImage",
1413 &error,
1414 NULL,
1415 "s", argv[i]);
1416 if (r < 0) {
1417 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1418 return r;
1419 }
1420 }
1421
1422 return 0;
1423 }
1424
1425 static int rename_image(int argc, char *argv[], void *userdata) {
1426 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1427 sd_bus *bus = userdata;
1428 int r;
1429
1430 polkit_agent_open_if_enabled();
1431
1432 r = sd_bus_call_method(
1433 bus,
1434 "org.freedesktop.machine1",
1435 "/org/freedesktop/machine1",
1436 "org.freedesktop.machine1.Manager",
1437 "RenameImage",
1438 &error,
1439 NULL,
1440 "ss", argv[1], argv[2]);
1441 if (r < 0) {
1442 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1443 return r;
1444 }
1445
1446 return 0;
1447 }
1448
1449 static int clone_image(int argc, char *argv[], void *userdata) {
1450 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1451 sd_bus *bus = userdata;
1452 int r;
1453
1454 polkit_agent_open_if_enabled();
1455
1456 r = sd_bus_call_method(
1457 bus,
1458 "org.freedesktop.machine1",
1459 "/org/freedesktop/machine1",
1460 "org.freedesktop.machine1.Manager",
1461 "CloneImage",
1462 &error,
1463 NULL,
1464 "ssb", argv[1], argv[2], arg_read_only);
1465 if (r < 0) {
1466 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1467 return r;
1468 }
1469
1470 return 0;
1471 }
1472
1473 static int read_only_image(int argc, char *argv[], void *userdata) {
1474 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1475 sd_bus *bus = userdata;
1476 int b = true, r;
1477
1478 if (argc > 2) {
1479 b = parse_boolean(argv[2]);
1480 if (b < 0) {
1481 log_error("Failed to parse boolean argument: %s", argv[2]);
1482 return -EINVAL;
1483 }
1484 }
1485
1486 polkit_agent_open_if_enabled();
1487
1488 r = sd_bus_call_method(
1489 bus,
1490 "org.freedesktop.machine1",
1491 "/org/freedesktop/machine1",
1492 "org.freedesktop.machine1.Manager",
1493 "MarkImageReadOnly",
1494 &error,
1495 NULL,
1496 "sb", argv[1], b);
1497 if (r < 0) {
1498 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1499 return r;
1500 }
1501
1502 return 0;
1503 }
1504
1505 static int make_service_name(const char *name, char **ret) {
1506 _cleanup_free_ char *e = NULL;
1507 int r;
1508
1509 assert(name);
1510 assert(ret);
1511
1512 if (!machine_name_is_valid(name)) {
1513 log_error("Invalid machine name %s.", name);
1514 return -EINVAL;
1515 }
1516
1517 e = unit_name_escape(name);
1518 if (!e)
1519 return log_oom();
1520
1521 r = unit_name_build("systemd-nspawn", e, ".service", ret);
1522 if (r < 0)
1523 return log_error_errno(r, "Failed to build unit name: %m");
1524
1525 return 0;
1526 }
1527
1528 static int start_machine(int argc, char *argv[], void *userdata) {
1529 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1530 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1531 sd_bus *bus = userdata;
1532 int r, i;
1533
1534 assert(bus);
1535
1536 polkit_agent_open_if_enabled();
1537
1538 r = bus_wait_for_jobs_new(bus, &w);
1539 if (r < 0)
1540 return log_oom();
1541
1542 for (i = 1; i < argc; i++) {
1543 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1544 _cleanup_free_ char *unit = NULL;
1545 const char *object;
1546
1547 r = make_service_name(argv[i], &unit);
1548 if (r < 0)
1549 return r;
1550
1551 r = sd_bus_call_method(
1552 bus,
1553 "org.freedesktop.systemd1",
1554 "/org/freedesktop/systemd1",
1555 "org.freedesktop.systemd1.Manager",
1556 "StartUnit",
1557 &error,
1558 &reply,
1559 "ss", unit, "fail");
1560 if (r < 0) {
1561 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1562 return r;
1563 }
1564
1565 r = sd_bus_message_read(reply, "o", &object);
1566 if (r < 0)
1567 return bus_log_parse_error(r);
1568
1569 r = bus_wait_for_jobs_add(w, object);
1570 if (r < 0)
1571 return log_oom();
1572 }
1573
1574 r = bus_wait_for_jobs(w, arg_quiet);
1575 if (r < 0)
1576 return r;
1577
1578 return 0;
1579 }
1580
1581 static int enable_machine(int argc, char *argv[], void *userdata) {
1582 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1583 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1584 int carries_install_info = 0;
1585 const char *method = NULL;
1586 sd_bus *bus = userdata;
1587 int r, i;
1588
1589 assert(bus);
1590
1591 polkit_agent_open_if_enabled();
1592
1593 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1594
1595 r = sd_bus_message_new_method_call(
1596 bus,
1597 &m,
1598 "org.freedesktop.systemd1",
1599 "/org/freedesktop/systemd1",
1600 "org.freedesktop.systemd1.Manager",
1601 method);
1602 if (r < 0)
1603 return bus_log_create_error(r);
1604
1605 r = sd_bus_message_open_container(m, 'a', "s");
1606 if (r < 0)
1607 return bus_log_create_error(r);
1608
1609 for (i = 1; i < argc; i++) {
1610 _cleanup_free_ char *unit = NULL;
1611
1612 r = make_service_name(argv[i], &unit);
1613 if (r < 0)
1614 return r;
1615
1616 r = sd_bus_message_append(m, "s", unit);
1617 if (r < 0)
1618 return bus_log_create_error(r);
1619 }
1620
1621 r = sd_bus_message_close_container(m);
1622 if (r < 0)
1623 return bus_log_create_error(r);
1624
1625 if (streq(argv[0], "enable"))
1626 r = sd_bus_message_append(m, "bb", false, false);
1627 else
1628 r = sd_bus_message_append(m, "b", false);
1629 if (r < 0)
1630 return bus_log_create_error(r);
1631
1632 r = sd_bus_call(bus, m, 0, &error, &reply);
1633 if (r < 0) {
1634 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1635 return r;
1636 }
1637
1638 if (streq(argv[0], "enable")) {
1639 r = sd_bus_message_read(reply, "b", carries_install_info);
1640 if (r < 0)
1641 return bus_log_parse_error(r);
1642 }
1643
1644 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
1645 if (r < 0)
1646 return r;
1647
1648 r = sd_bus_call_method(
1649 bus,
1650 "org.freedesktop.systemd1",
1651 "/org/freedesktop/systemd1",
1652 "org.freedesktop.systemd1.Manager",
1653 "Reload",
1654 &error,
1655 NULL,
1656 NULL);
1657 if (r < 0) {
1658 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1659 return r;
1660 }
1661
1662 return 0;
1663 }
1664
1665 static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1666 const char **our_path = userdata, *line;
1667 unsigned priority;
1668 int r;
1669
1670 assert(m);
1671 assert(our_path);
1672
1673 r = sd_bus_message_read(m, "us", &priority, &line);
1674 if (r < 0) {
1675 bus_log_parse_error(r);
1676 return 0;
1677 }
1678
1679 if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
1680 return 0;
1681
1682 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1683 return 0;
1684
1685 log_full(priority, "%s", line);
1686 return 0;
1687 }
1688
1689 static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1690 const char **our_path = userdata, *path, *result;
1691 uint32_t id;
1692 int r;
1693
1694 assert(m);
1695 assert(our_path);
1696
1697 r = sd_bus_message_read(m, "uos", &id, &path, &result);
1698 if (r < 0) {
1699 bus_log_parse_error(r);
1700 return 0;
1701 }
1702
1703 if (!streq_ptr(*our_path, path))
1704 return 0;
1705
1706 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done"));
1707 return 0;
1708 }
1709
1710 static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1711 assert(s);
1712 assert(si);
1713
1714 if (!arg_quiet)
1715 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
1716
1717 sd_event_exit(sd_event_source_get_event(s), EINTR);
1718 return 0;
1719 }
1720
1721 static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
1722 _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1723 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1724 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1725 _cleanup_event_unref_ sd_event* event = NULL;
1726 const char *path = NULL;
1727 uint32_t id;
1728 int r;
1729
1730 assert(bus);
1731 assert(m);
1732
1733 polkit_agent_open_if_enabled();
1734
1735 r = sd_event_default(&event);
1736 if (r < 0)
1737 return log_error_errno(r, "Failed to get event loop: %m");
1738
1739 r = sd_bus_attach_event(bus, event, 0);
1740 if (r < 0)
1741 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1742
1743 r = sd_bus_add_match(
1744 bus,
1745 &slot_job_removed,
1746 "type='signal',"
1747 "sender='org.freedesktop.import1',"
1748 "interface='org.freedesktop.import1.Manager',"
1749 "member='TransferRemoved',"
1750 "path='/org/freedesktop/import1'",
1751 match_transfer_removed, &path);
1752 if (r < 0)
1753 return log_error_errno(r, "Failed to install match: %m");
1754
1755 r = sd_bus_add_match(
1756 bus,
1757 &slot_log_message,
1758 "type='signal',"
1759 "sender='org.freedesktop.import1',"
1760 "interface='org.freedesktop.import1.Transfer',"
1761 "member='LogMessage'",
1762 match_log_message, &path);
1763 if (r < 0)
1764 return log_error_errno(r, "Failed to install match: %m");
1765
1766 r = sd_bus_call(bus, m, 0, &error, &reply);
1767 if (r < 0) {
1768 log_error("Failed transfer image: %s", bus_error_message(&error, -r));
1769 return r;
1770 }
1771
1772 r = sd_bus_message_read(reply, "uo", &id, &path);
1773 if (r < 0)
1774 return bus_log_parse_error(r);
1775
1776 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
1777
1778 if (!arg_quiet)
1779 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
1780
1781 sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
1782 sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
1783
1784 r = sd_event_loop(event);
1785 if (r < 0)
1786 return log_error_errno(r, "Failed to run event loop: %m");
1787
1788 return -r;
1789 }
1790
1791 static int import_tar(int argc, char *argv[], void *userdata) {
1792 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1793 _cleanup_free_ char *ll = NULL;
1794 _cleanup_close_ int fd = -1;
1795 const char *local = NULL, *path = NULL;
1796 sd_bus *bus = userdata;
1797 int r;
1798
1799 assert(bus);
1800
1801 if (argc >= 2)
1802 path = argv[1];
1803 if (isempty(path) || streq(path, "-"))
1804 path = NULL;
1805
1806 if (argc >= 3)
1807 local = argv[2];
1808 else if (path)
1809 local = basename(path);
1810 if (isempty(local) || streq(local, "-"))
1811 local = NULL;
1812
1813 if (!local) {
1814 log_error("Need either path or local name.");
1815 return -EINVAL;
1816 }
1817
1818 r = tar_strip_suffixes(local, &ll);
1819 if (r < 0)
1820 return log_oom();
1821
1822 local = ll;
1823
1824 if (!machine_name_is_valid(local)) {
1825 log_error("Local name %s is not a suitable machine name.", local);
1826 return -EINVAL;
1827 }
1828
1829 if (path) {
1830 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1831 if (fd < 0)
1832 return log_error_errno(errno, "Failed to open %s: %m", path);
1833 }
1834
1835 r = sd_bus_message_new_method_call(
1836 bus,
1837 &m,
1838 "org.freedesktop.import1",
1839 "/org/freedesktop/import1",
1840 "org.freedesktop.import1.Manager",
1841 "ImportTar");
1842 if (r < 0)
1843 return bus_log_create_error(r);
1844
1845 r = sd_bus_message_append(
1846 m,
1847 "hsbb",
1848 fd >= 0 ? fd : STDIN_FILENO,
1849 local,
1850 arg_force,
1851 arg_read_only);
1852 if (r < 0)
1853 return bus_log_create_error(r);
1854
1855 return transfer_image_common(bus, m);
1856 }
1857
1858 static int import_raw(int argc, char *argv[], void *userdata) {
1859 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1860 _cleanup_free_ char *ll = NULL;
1861 _cleanup_close_ int fd = -1;
1862 const char *local = NULL, *path = NULL;
1863 sd_bus *bus = userdata;
1864 int r;
1865
1866 assert(bus);
1867
1868 if (argc >= 2)
1869 path = argv[1];
1870 if (isempty(path) || streq(path, "-"))
1871 path = NULL;
1872
1873 if (argc >= 3)
1874 local = argv[2];
1875 else if (path)
1876 local = basename(path);
1877 if (isempty(local) || streq(local, "-"))
1878 local = NULL;
1879
1880 if (!local) {
1881 log_error("Need either path or local name.");
1882 return -EINVAL;
1883 }
1884
1885 r = raw_strip_suffixes(local, &ll);
1886 if (r < 0)
1887 return log_oom();
1888
1889 local = ll;
1890
1891 if (!machine_name_is_valid(local)) {
1892 log_error("Local name %s is not a suitable machine name.", local);
1893 return -EINVAL;
1894 }
1895
1896 if (path) {
1897 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1898 if (fd < 0)
1899 return log_error_errno(errno, "Failed to open %s: %m", path);
1900 }
1901
1902 r = sd_bus_message_new_method_call(
1903 bus,
1904 &m,
1905 "org.freedesktop.import1",
1906 "/org/freedesktop/import1",
1907 "org.freedesktop.import1.Manager",
1908 "ImportRaw");
1909 if (r < 0)
1910 return bus_log_create_error(r);
1911
1912 r = sd_bus_message_append(
1913 m,
1914 "hsbb",
1915 fd >= 0 ? fd : STDIN_FILENO,
1916 local,
1917 arg_force,
1918 arg_read_only);
1919 if (r < 0)
1920 return bus_log_create_error(r);
1921
1922 return transfer_image_common(bus, m);
1923 }
1924
1925 static void determine_compression_from_filename(const char *p) {
1926 if (arg_format)
1927 return;
1928
1929 if (!p)
1930 return;
1931
1932 if (endswith(p, ".xz"))
1933 arg_format = "xz";
1934 else if (endswith(p, ".gz"))
1935 arg_format = "gzip";
1936 else if (endswith(p, ".bz2"))
1937 arg_format = "bzip2";
1938 }
1939
1940 static int export_tar(int argc, char *argv[], void *userdata) {
1941 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1942 _cleanup_close_ int fd = -1;
1943 const char *local = NULL, *path = NULL;
1944 sd_bus *bus = userdata;
1945 int r;
1946
1947 assert(bus);
1948
1949 local = argv[1];
1950 if (!machine_name_is_valid(local)) {
1951 log_error("Machine name %s is not valid.", local);
1952 return -EINVAL;
1953 }
1954
1955 if (argc >= 3)
1956 path = argv[2];
1957 if (isempty(path) || streq(path, "-"))
1958 path = NULL;
1959
1960 if (path) {
1961 determine_compression_from_filename(path);
1962
1963 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
1964 if (fd < 0)
1965 return log_error_errno(errno, "Failed to open %s: %m", path);
1966 }
1967
1968 r = sd_bus_message_new_method_call(
1969 bus,
1970 &m,
1971 "org.freedesktop.import1",
1972 "/org/freedesktop/import1",
1973 "org.freedesktop.import1.Manager",
1974 "ExportTar");
1975 if (r < 0)
1976 return bus_log_create_error(r);
1977
1978 r = sd_bus_message_append(
1979 m,
1980 "shs",
1981 local,
1982 fd >= 0 ? fd : STDOUT_FILENO,
1983 arg_format);
1984 if (r < 0)
1985 return bus_log_create_error(r);
1986
1987 return transfer_image_common(bus, m);
1988 }
1989
1990 static int export_raw(int argc, char *argv[], void *userdata) {
1991 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1992 _cleanup_close_ int fd = -1;
1993 const char *local = NULL, *path = NULL;
1994 sd_bus *bus = userdata;
1995 int r;
1996
1997 assert(bus);
1998
1999 local = argv[1];
2000 if (!machine_name_is_valid(local)) {
2001 log_error("Machine name %s is not valid.", local);
2002 return -EINVAL;
2003 }
2004
2005 if (argc >= 3)
2006 path = argv[2];
2007 if (isempty(path) || streq(path, "-"))
2008 path = NULL;
2009
2010 if (path) {
2011 determine_compression_from_filename(path);
2012
2013 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
2014 if (fd < 0)
2015 return log_error_errno(errno, "Failed to open %s: %m", path);
2016 }
2017
2018 r = sd_bus_message_new_method_call(
2019 bus,
2020 &m,
2021 "org.freedesktop.import1",
2022 "/org/freedesktop/import1",
2023 "org.freedesktop.import1.Manager",
2024 "ExportRaw");
2025 if (r < 0)
2026 return bus_log_create_error(r);
2027
2028 r = sd_bus_message_append(
2029 m,
2030 "shs",
2031 local,
2032 fd >= 0 ? fd : STDOUT_FILENO,
2033 arg_format);
2034 if (r < 0)
2035 return bus_log_create_error(r);
2036
2037 return transfer_image_common(bus, m);
2038 }
2039
2040 static int pull_tar(int argc, char *argv[], void *userdata) {
2041 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2042 _cleanup_free_ char *l = NULL, *ll = NULL;
2043 const char *local, *remote;
2044 sd_bus *bus = userdata;
2045 int r;
2046
2047 assert(bus);
2048
2049 remote = argv[1];
2050 if (!http_url_is_valid(remote)) {
2051 log_error("URL '%s' is not valid.", remote);
2052 return -EINVAL;
2053 }
2054
2055 if (argc >= 3)
2056 local = argv[2];
2057 else {
2058 r = import_url_last_component(remote, &l);
2059 if (r < 0)
2060 return log_error_errno(r, "Failed to get final component of URL: %m");
2061
2062 local = l;
2063 }
2064
2065 if (isempty(local) || streq(local, "-"))
2066 local = NULL;
2067
2068 if (local) {
2069 r = tar_strip_suffixes(local, &ll);
2070 if (r < 0)
2071 return log_oom();
2072
2073 local = ll;
2074
2075 if (!machine_name_is_valid(local)) {
2076 log_error("Local name %s is not a suitable machine name.", local);
2077 return -EINVAL;
2078 }
2079 }
2080
2081 r = sd_bus_message_new_method_call(
2082 bus,
2083 &m,
2084 "org.freedesktop.import1",
2085 "/org/freedesktop/import1",
2086 "org.freedesktop.import1.Manager",
2087 "PullTar");
2088 if (r < 0)
2089 return bus_log_create_error(r);
2090
2091 r = sd_bus_message_append(
2092 m,
2093 "sssb",
2094 remote,
2095 local,
2096 import_verify_to_string(arg_verify),
2097 arg_force);
2098 if (r < 0)
2099 return bus_log_create_error(r);
2100
2101 return transfer_image_common(bus, m);
2102 }
2103
2104 static int pull_raw(int argc, char *argv[], void *userdata) {
2105 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2106 _cleanup_free_ char *l = NULL, *ll = NULL;
2107 const char *local, *remote;
2108 sd_bus *bus = userdata;
2109 int r;
2110
2111 assert(bus);
2112
2113 remote = argv[1];
2114 if (!http_url_is_valid(remote)) {
2115 log_error("URL '%s' is not valid.", remote);
2116 return -EINVAL;
2117 }
2118
2119 if (argc >= 3)
2120 local = argv[2];
2121 else {
2122 r = import_url_last_component(remote, &l);
2123 if (r < 0)
2124 return log_error_errno(r, "Failed to get final component of URL: %m");
2125
2126 local = l;
2127 }
2128
2129 if (isempty(local) || streq(local, "-"))
2130 local = NULL;
2131
2132 if (local) {
2133 r = raw_strip_suffixes(local, &ll);
2134 if (r < 0)
2135 return log_oom();
2136
2137 local = ll;
2138
2139 if (!machine_name_is_valid(local)) {
2140 log_error("Local name %s is not a suitable machine name.", local);
2141 return -EINVAL;
2142 }
2143 }
2144
2145 r = sd_bus_message_new_method_call(
2146 bus,
2147 &m,
2148 "org.freedesktop.import1",
2149 "/org/freedesktop/import1",
2150 "org.freedesktop.import1.Manager",
2151 "PullRaw");
2152 if (r < 0)
2153 return bus_log_create_error(r);
2154
2155 r = sd_bus_message_append(
2156 m,
2157 "sssb",
2158 remote,
2159 local,
2160 import_verify_to_string(arg_verify),
2161 arg_force);
2162 if (r < 0)
2163 return bus_log_create_error(r);
2164
2165 return transfer_image_common(bus, m);
2166 }
2167
2168 static int pull_dkr(int argc, char *argv[], void *userdata) {
2169 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2170 const char *local, *remote, *tag;
2171 sd_bus *bus = userdata;
2172 int r;
2173
2174 if (arg_verify != IMPORT_VERIFY_NO) {
2175 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2176 return -EINVAL;
2177 }
2178
2179 remote = argv[1];
2180 tag = strchr(remote, ':');
2181 if (tag) {
2182 remote = strndupa(remote, tag - remote);
2183 tag++;
2184 }
2185
2186 if (!dkr_name_is_valid(remote)) {
2187 log_error("DKR name '%s' is invalid.", remote);
2188 return -EINVAL;
2189 }
2190 if (tag && !dkr_tag_is_valid(tag)) {
2191 log_error("DKR tag '%s' is invalid.", remote);
2192 return -EINVAL;
2193 }
2194
2195 if (argc >= 3)
2196 local = argv[2];
2197 else {
2198 local = strchr(remote, '/');
2199 if (local)
2200 local++;
2201 else
2202 local = remote;
2203 }
2204
2205 if (isempty(local) || streq(local, "-"))
2206 local = NULL;
2207
2208 if (local) {
2209 if (!machine_name_is_valid(local)) {
2210 log_error("Local name %s is not a suitable machine name.", local);
2211 return -EINVAL;
2212 }
2213 }
2214
2215 r = sd_bus_message_new_method_call(
2216 bus,
2217 &m,
2218 "org.freedesktop.import1",
2219 "/org/freedesktop/import1",
2220 "org.freedesktop.import1.Manager",
2221 "PullDkr");
2222 if (r < 0)
2223 return bus_log_create_error(r);
2224
2225 r = sd_bus_message_append(
2226 m,
2227 "sssssb",
2228 arg_dkr_index_url,
2229 remote,
2230 tag,
2231 local,
2232 import_verify_to_string(arg_verify),
2233 arg_force);
2234 if (r < 0)
2235 return bus_log_create_error(r);
2236
2237 return transfer_image_common(bus, m);
2238 }
2239
2240 typedef struct TransferInfo {
2241 uint32_t id;
2242 const char *type;
2243 const char *remote;
2244 const char *local;
2245 double progress;
2246 } TransferInfo;
2247
2248 static int compare_transfer_info(const void *a, const void *b) {
2249 const TransferInfo *x = a, *y = b;
2250
2251 return strcmp(x->local, y->local);
2252 }
2253
2254 static int list_transfers(int argc, char *argv[], void *userdata) {
2255 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
2256 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
2257 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2258 _cleanup_free_ TransferInfo *transfers = NULL;
2259 size_t n_transfers = 0, n_allocated = 0, j;
2260 const char *type, *remote, *local, *object;
2261 sd_bus *bus = userdata;
2262 uint32_t id, max_id = 0;
2263 double progress;
2264 int r;
2265
2266 pager_open_if_enabled();
2267
2268 r = sd_bus_call_method(
2269 bus,
2270 "org.freedesktop.import1",
2271 "/org/freedesktop/import1",
2272 "org.freedesktop.import1.Manager",
2273 "ListTransfers",
2274 &error,
2275 &reply,
2276 NULL);
2277 if (r < 0) {
2278 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2279 return r;
2280 }
2281
2282 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
2283 if (r < 0)
2284 return bus_log_parse_error(r);
2285
2286 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
2287 size_t l;
2288
2289 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2290 return log_oom();
2291
2292 transfers[n_transfers].id = id;
2293 transfers[n_transfers].type = type;
2294 transfers[n_transfers].remote = remote;
2295 transfers[n_transfers].local = local;
2296 transfers[n_transfers].progress = progress;
2297
2298 l = strlen(type);
2299 if (l > max_type)
2300 max_type = l;
2301
2302 l = strlen(remote);
2303 if (l > max_remote)
2304 max_remote = l;
2305
2306 l = strlen(local);
2307 if (l > max_local)
2308 max_local = l;
2309
2310 if (id > max_id)
2311 max_id = id;
2312
2313 n_transfers ++;
2314 }
2315 if (r < 0)
2316 return bus_log_parse_error(r);
2317
2318 r = sd_bus_message_exit_container(reply);
2319 if (r < 0)
2320 return bus_log_parse_error(r);
2321
2322 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2323
2324 if (arg_legend)
2325 printf("%-*s %-*s %-*s %-*s %-*s\n",
2326 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
2327 (int) 7, "PERCENT",
2328 (int) max_type, "TYPE",
2329 (int) max_local, "LOCAL",
2330 (int) max_remote, "REMOTE");
2331
2332 for (j = 0; j < n_transfers; j++)
2333 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
2334 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
2335 (int) 6, (unsigned) (transfers[j].progress * 100),
2336 (int) max_type, transfers[j].type,
2337 (int) max_local, transfers[j].local,
2338 (int) max_remote, transfers[j].remote);
2339
2340 if (arg_legend)
2341 printf("\n%zu transfers listed.\n", n_transfers);
2342
2343 return 0;
2344 }
2345
2346 static int cancel_transfer(int argc, char *argv[], void *userdata) {
2347 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2348 sd_bus *bus = userdata;
2349 int r, i;
2350
2351 assert(bus);
2352
2353 polkit_agent_open_if_enabled();
2354
2355 for (i = 1; i < argc; i++) {
2356 uint32_t id;
2357
2358 r = safe_atou32(argv[i], &id);
2359 if (r < 0)
2360 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2361
2362 r = sd_bus_call_method(
2363 bus,
2364 "org.freedesktop.import1",
2365 "/org/freedesktop/import1",
2366 "org.freedesktop.import1.Manager",
2367 "CancelTransfer",
2368 &error,
2369 NULL,
2370 "u", id);
2371 if (r < 0) {
2372 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2373 return r;
2374 }
2375 }
2376
2377 return 0;
2378 }
2379
2380 static int set_limit(int argc, char *argv[], void *userdata) {
2381 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2382 sd_bus *bus = userdata;
2383 uint64_t limit;
2384 int r;
2385
2386 if (streq(argv[argc-1], "-"))
2387 limit = (uint64_t) -1;
2388 else {
2389 off_t off;
2390
2391 r = parse_size(argv[argc-1], 1024, &off);
2392 if (r < 0)
2393 return log_error("Failed to parse size: %s", argv[argc-1]);
2394
2395 limit = (uint64_t) off;
2396 }
2397
2398 if (argc > 2)
2399 /* With two arguments changes the quota limit of the
2400 * specified image */
2401 r = sd_bus_call_method(
2402 bus,
2403 "org.freedesktop.machine1",
2404 "/org/freedesktop/machine1",
2405 "org.freedesktop.machine1.Manager",
2406 "SetImageLimit",
2407 &error,
2408 NULL,
2409 "st", argv[1], limit);
2410 else
2411 /* With one argument changes the pool quota limit */
2412 r = sd_bus_call_method(
2413 bus,
2414 "org.freedesktop.machine1",
2415 "/org/freedesktop/machine1",
2416 "org.freedesktop.machine1.Manager",
2417 "SetPoolLimit",
2418 &error,
2419 NULL,
2420 "t", limit);
2421
2422 if (r < 0) {
2423 log_error("Could not set limit: %s", bus_error_message(&error, -r));
2424 return r;
2425 }
2426
2427 return 0;
2428 }
2429
2430 static int help(int argc, char *argv[], void *userdata) {
2431
2432 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2433 "Send control commands to or query the virtual machine and container\n"
2434 "registration manager.\n\n"
2435 " -h --help Show this help\n"
2436 " --version Show package version\n"
2437 " --no-pager Do not pipe output into a pager\n"
2438 " --no-legend Do not show the headers and footers\n"
2439 " --no-ask-password Do not ask for system passwords\n"
2440 " -H --host=[USER@]HOST Operate on remote host\n"
2441 " -M --machine=CONTAINER Operate on local container\n"
2442 " -p --property=NAME Show only properties by this name\n"
2443 " -q --quiet Suppress output\n"
2444 " -a --all Show all properties, including empty ones\n"
2445 " -l --full Do not ellipsize output\n"
2446 " --kill-who=WHO Who to send signal to\n"
2447 " -s --signal=SIGNAL Which signal to send\n"
2448 " --uid=USER Specify user ID to invoke shell as\n"
2449 " --setenv=VAR=VALUE Add an environment variable for shell\n"
2450 " --read-only Create read-only bind mount\n"
2451 " --mkdir Create directory before bind mounting, if missing\n"
2452 " -n --lines=INTEGER Number of journal entries to show\n"
2453 " -o --output=STRING Change journal output mode (short,\n"
2454 " short-monotonic, verbose, export, json,\n"
2455 " json-pretty, json-sse, cat)\n"
2456 " --verify=MODE Verification mode for downloaded images (no,\n"
2457 " checksum, signature)\n"
2458 " --force Download image even if already exists\n"
2459 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2460 " downloads\n\n"
2461 "Machine Commands:\n"
2462 " list List running VMs and containers\n"
2463 " status NAME... Show VM/container details\n"
2464 " show [NAME...] Show properties of one or more VMs/containers\n"
2465 " start NAME... Start container as a service\n"
2466 " login [NAME] Get a login prompt in a container or on the\n"
2467 " local host\n"
2468 " shell [[USER@]NAME [COMMAND...]]\n"
2469 " Invoke a shell (or other command) in a container\n"
2470 " or on the local host\n"
2471 " enable NAME... Enable automatic container start at boot\n"
2472 " disable NAME... Disable automatic container start at boot\n"
2473 " poweroff NAME... Power off one or more containers\n"
2474 " reboot NAME... Reboot one or more containers\n"
2475 " terminate NAME... Terminate one or more VMs/containers\n"
2476 " kill NAME... Send signal to processes of a VM/container\n"
2477 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2478 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2479 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2480 "Image Commands:\n"
2481 " list-images Show available container and VM images\n"
2482 " image-status [NAME...] Show image details\n"
2483 " show-image [NAME...] Show properties of image\n"
2484 " clone NAME NAME Clone an image\n"
2485 " rename NAME NAME Rename an image\n"
2486 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2487 " remove NAME... Remove an image\n"
2488 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2489 "Image Transfer Commands:\n"
2490 " pull-tar URL [NAME] Download a TAR container image\n"
2491 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2492 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2493 " import-tar FILE [NAME] Import a local TAR container image\n"
2494 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2495 " export-tar NAME [FILE] Export a TAR container image locally\n"
2496 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2497 " list-transfers Show list of downloads in progress\n"
2498 " cancel-transfer Cancel a download\n"
2499 , program_invocation_short_name);
2500
2501 return 0;
2502 }
2503
2504 static int parse_argv(int argc, char *argv[]) {
2505
2506 enum {
2507 ARG_VERSION = 0x100,
2508 ARG_NO_PAGER,
2509 ARG_NO_LEGEND,
2510 ARG_KILL_WHO,
2511 ARG_READ_ONLY,
2512 ARG_MKDIR,
2513 ARG_NO_ASK_PASSWORD,
2514 ARG_VERIFY,
2515 ARG_FORCE,
2516 ARG_DKR_INDEX_URL,
2517 ARG_FORMAT,
2518 ARG_UID,
2519 ARG_SETENV,
2520 };
2521
2522 static const struct option options[] = {
2523 { "help", no_argument, NULL, 'h' },
2524 { "version", no_argument, NULL, ARG_VERSION },
2525 { "property", required_argument, NULL, 'p' },
2526 { "all", no_argument, NULL, 'a' },
2527 { "full", no_argument, NULL, 'l' },
2528 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2529 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2530 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2531 { "signal", required_argument, NULL, 's' },
2532 { "host", required_argument, NULL, 'H' },
2533 { "machine", required_argument, NULL, 'M' },
2534 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2535 { "mkdir", no_argument, NULL, ARG_MKDIR },
2536 { "quiet", no_argument, NULL, 'q' },
2537 { "lines", required_argument, NULL, 'n' },
2538 { "output", required_argument, NULL, 'o' },
2539 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2540 { "verify", required_argument, NULL, ARG_VERIFY },
2541 { "force", no_argument, NULL, ARG_FORCE },
2542 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
2543 { "format", required_argument, NULL, ARG_FORMAT },
2544 { "uid", required_argument, NULL, ARG_UID },
2545 { "setenv", required_argument, NULL, ARG_SETENV },
2546 {}
2547 };
2548
2549 int c, r;
2550
2551 assert(argc >= 0);
2552 assert(argv);
2553
2554 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2555
2556 switch (c) {
2557
2558 case 'h':
2559 return help(0, NULL, NULL);
2560
2561 case ARG_VERSION:
2562 puts(PACKAGE_STRING);
2563 puts(SYSTEMD_FEATURES);
2564 return 0;
2565
2566 case 'p':
2567 r = strv_extend(&arg_property, optarg);
2568 if (r < 0)
2569 return log_oom();
2570
2571 /* If the user asked for a particular
2572 * property, show it to him, even if it is
2573 * empty. */
2574 arg_all = true;
2575 break;
2576
2577 case 'a':
2578 arg_all = true;
2579 break;
2580
2581 case 'l':
2582 arg_full = true;
2583 break;
2584
2585 case 'n':
2586 if (safe_atou(optarg, &arg_lines) < 0) {
2587 log_error("Failed to parse lines '%s'", optarg);
2588 return -EINVAL;
2589 }
2590 break;
2591
2592 case 'o':
2593 arg_output = output_mode_from_string(optarg);
2594 if (arg_output < 0) {
2595 log_error("Unknown output '%s'.", optarg);
2596 return -EINVAL;
2597 }
2598 break;
2599
2600 case ARG_NO_PAGER:
2601 arg_no_pager = true;
2602 break;
2603
2604 case ARG_NO_LEGEND:
2605 arg_legend = false;
2606 break;
2607
2608 case ARG_KILL_WHO:
2609 arg_kill_who = optarg;
2610 break;
2611
2612 case 's':
2613 arg_signal = signal_from_string_try_harder(optarg);
2614 if (arg_signal < 0) {
2615 log_error("Failed to parse signal string %s.", optarg);
2616 return -EINVAL;
2617 }
2618 break;
2619
2620 case ARG_NO_ASK_PASSWORD:
2621 arg_ask_password = false;
2622 break;
2623
2624 case 'H':
2625 arg_transport = BUS_TRANSPORT_REMOTE;
2626 arg_host = optarg;
2627 break;
2628
2629 case 'M':
2630 arg_transport = BUS_TRANSPORT_MACHINE;
2631 arg_host = optarg;
2632 break;
2633
2634 case ARG_READ_ONLY:
2635 arg_read_only = true;
2636 break;
2637
2638 case ARG_MKDIR:
2639 arg_mkdir = true;
2640 break;
2641
2642 case 'q':
2643 arg_quiet = true;
2644 break;
2645
2646 case ARG_VERIFY:
2647 arg_verify = import_verify_from_string(optarg);
2648 if (arg_verify < 0) {
2649 log_error("Failed to parse --verify= setting: %s", optarg);
2650 return -EINVAL;
2651 }
2652 break;
2653
2654 case ARG_FORCE:
2655 arg_force = true;
2656 break;
2657
2658 case ARG_DKR_INDEX_URL:
2659 if (!http_url_is_valid(optarg)) {
2660 log_error("Index URL is invalid: %s", optarg);
2661 return -EINVAL;
2662 }
2663
2664 arg_dkr_index_url = optarg;
2665 break;
2666
2667 case ARG_FORMAT:
2668 if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
2669 log_error("Unknown format: %s", optarg);
2670 return -EINVAL;
2671 }
2672
2673 arg_format = optarg;
2674 break;
2675
2676 case ARG_UID:
2677 arg_uid = optarg;
2678 break;
2679
2680 case ARG_SETENV:
2681 if (!env_assignment_is_valid(optarg)) {
2682 log_error("Environment assignment invalid: %s", optarg);
2683 return -EINVAL;
2684 }
2685
2686 r = strv_extend(&arg_setenv, optarg);
2687 if (r < 0)
2688 return log_oom();
2689 break;
2690
2691 case '?':
2692 return -EINVAL;
2693
2694 default:
2695 assert_not_reached("Unhandled option");
2696 }
2697
2698 return 1;
2699 }
2700
2701 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2702
2703 static const Verb verbs[] = {
2704 { "help", VERB_ANY, VERB_ANY, 0, help },
2705 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2706 { "list-images", VERB_ANY, 1, 0, list_images },
2707 { "status", 2, VERB_ANY, 0, show_machine },
2708 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
2709 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2710 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2711 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2712 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2713 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2714 { "kill", 2, VERB_ANY, 0, kill_machine },
2715 { "login", VERB_ANY, 2, 0, login_machine },
2716 { "shell", VERB_ANY, VERB_ANY, 0, shell_machine },
2717 { "bind", 3, 4, 0, bind_mount },
2718 { "copy-to", 3, 4, 0, copy_files },
2719 { "copy-from", 3, 4, 0, copy_files },
2720 { "remove", 2, VERB_ANY, 0, remove_image },
2721 { "rename", 3, 3, 0, rename_image },
2722 { "clone", 3, 3, 0, clone_image },
2723 { "read-only", 2, 3, 0, read_only_image },
2724 { "start", 2, VERB_ANY, 0, start_machine },
2725 { "enable", 2, VERB_ANY, 0, enable_machine },
2726 { "disable", 2, VERB_ANY, 0, enable_machine },
2727 { "import-tar", 2, 3, 0, import_tar },
2728 { "import-raw", 2, 3, 0, import_raw },
2729 { "export-tar", 2, 3, 0, export_tar },
2730 { "export-raw", 2, 3, 0, export_raw },
2731 { "pull-tar", 2, 3, 0, pull_tar },
2732 { "pull-raw", 2, 3, 0, pull_raw },
2733 { "pull-dkr", 2, 3, 0, pull_dkr },
2734 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2735 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
2736 { "set-limit", 2, 3, 0, set_limit },
2737 {}
2738 };
2739
2740 return dispatch_verb(argc, argv, verbs, bus);
2741 }
2742
2743 int main(int argc, char*argv[]) {
2744 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
2745 int r;
2746
2747 setlocale(LC_ALL, "");
2748 log_parse_environment();
2749 log_open();
2750
2751 r = parse_argv(argc, argv);
2752 if (r <= 0)
2753 goto finish;
2754
2755 r = bus_open_transport(arg_transport, arg_host, false, &bus);
2756 if (r < 0) {
2757 log_error_errno(r, "Failed to create bus connection: %m");
2758 goto finish;
2759 }
2760
2761 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2762
2763 r = machinectl_main(argc, argv, bus);
2764
2765 finish:
2766 pager_close();
2767 polkit_agent_close();
2768
2769 strv_free(arg_property);
2770 strv_free(arg_setenv);
2771
2772 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2773 }