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