]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machinectl.c
util-lib: split string parsing related calls from util.[ch] into parse-util.[ch]
[thirdparty/systemd.git] / src / machine / machinectl.c
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
63 static char **arg_property = NULL;
64 static bool arg_all = false;
65 static bool arg_full = false;
66 static bool arg_no_pager = false;
67 static bool arg_legend = true;
68 static const char *arg_kill_who = NULL;
69 static int arg_signal = SIGTERM;
70 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
71 static char *arg_host = NULL;
72 static bool arg_read_only = false;
73 static bool arg_mkdir = false;
74 static bool arg_quiet = false;
75 static bool arg_ask_password = true;
76 static unsigned arg_lines = 10;
77 static OutputMode arg_output = OUTPUT_SHORT;
78 static bool arg_force = false;
79 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
80 static const char* arg_dkr_index_url = NULL;
81 static const char* arg_format = NULL;
82 static const char *arg_uid = NULL;
83 static char **arg_setenv = NULL;
84
85 static void pager_open_if_enabled(void) {
86
87 if (arg_no_pager)
88 return;
89
90 pager_open(false);
91 }
92
93 static void polkit_agent_open_if_enabled(void) {
94
95 /* Open the polkit agent as a child process if necessary */
96
97 if (!arg_ask_password)
98 return;
99
100 if (arg_transport != BUS_TRANSPORT_LOCAL)
101 return;
102
103 polkit_agent_open();
104 }
105
106 static OutputFlags get_output_flags(void) {
107 return
108 arg_all * OUTPUT_SHOW_ALL |
109 arg_full * OUTPUT_FULL_WIDTH |
110 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
111 on_tty() * OUTPUT_COLOR |
112 !arg_quiet * OUTPUT_WARN_CUTOFF;
113 }
114
115 typedef struct MachineInfo {
116 const char *name;
117 const char *class;
118 const char *service;
119 } MachineInfo;
120
121 static int compare_machine_info(const void *a, const void *b) {
122 const MachineInfo *x = a, *y = b;
123
124 return strcmp(x->name, y->name);
125 }
126
127 static int list_machines(int argc, char *argv[], void *userdata) {
128
129 size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
130 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
131 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
132 _cleanup_free_ MachineInfo *machines = NULL;
133 const char *name, *class, *service, *object;
134 size_t n_machines = 0, n_allocated = 0, j;
135 sd_bus *bus = userdata;
136 int r;
137
138 assert(bus);
139
140 pager_open_if_enabled();
141
142 r = sd_bus_call_method(
143 bus,
144 "org.freedesktop.machine1",
145 "/org/freedesktop/machine1",
146 "org.freedesktop.machine1.Manager",
147 "ListMachines",
148 &error,
149 &reply,
150 NULL);
151 if (r < 0) {
152 log_error("Could not get machines: %s", bus_error_message(&error, -r));
153 return r;
154 }
155
156 r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
157 if (r < 0)
158 return bus_log_parse_error(r);
159
160 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
161 size_t l;
162
163 if (name[0] == '.' && !arg_all)
164 continue;
165
166 if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
167 return log_oom();
168
169 machines[n_machines].name = name;
170 machines[n_machines].class = class;
171 machines[n_machines].service = service;
172
173 l = strlen(name);
174 if (l > max_name)
175 max_name = l;
176
177 l = strlen(class);
178 if (l > max_class)
179 max_class = l;
180
181 l = strlen(service);
182 if (l > max_service)
183 max_service = l;
184
185 n_machines ++;
186 }
187 if (r < 0)
188 return bus_log_parse_error(r);
189
190 r = sd_bus_message_exit_container(reply);
191 if (r < 0)
192 return bus_log_parse_error(r);
193
194 qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
195
196 if (arg_legend)
197 printf("%-*s %-*s %-*s\n",
198 (int) max_name, "MACHINE",
199 (int) max_class, "CLASS",
200 (int) max_service, "SERVICE");
201
202 for (j = 0; j < n_machines; j++)
203 printf("%-*s %-*s %-*s\n",
204 (int) max_name, machines[j].name,
205 (int) max_class, machines[j].class,
206 (int) max_service, machines[j].service);
207
208 if (arg_legend)
209 printf("\n%zu machines listed.\n", n_machines);
210
211 return 0;
212 }
213
214 typedef struct ImageInfo {
215 const char *name;
216 const char *type;
217 bool read_only;
218 usec_t crtime;
219 usec_t mtime;
220 uint64_t size;
221 } ImageInfo;
222
223 static int compare_image_info(const void *a, const void *b) {
224 const ImageInfo *x = a, *y = b;
225
226 return strcmp(x->name, y->name);
227 }
228
229 static int list_images(int argc, char *argv[], void *userdata) {
230
231 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
232 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("USAGE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
233 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
234 _cleanup_free_ ImageInfo *images = NULL;
235 size_t n_images = 0, n_allocated = 0, j;
236 const char *name, *type, *object;
237 sd_bus *bus = userdata;
238 uint64_t crtime, mtime, size;
239 int read_only, r;
240
241 assert(bus);
242
243 pager_open_if_enabled();
244
245 r = sd_bus_call_method(
246 bus,
247 "org.freedesktop.machine1",
248 "/org/freedesktop/machine1",
249 "org.freedesktop.machine1.Manager",
250 "ListImages",
251 &error,
252 &reply,
253 "");
254 if (r < 0) {
255 log_error("Could not get images: %s", bus_error_message(&error, -r));
256 return r;
257 }
258
259 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
260 if (r < 0)
261 return bus_log_parse_error(r);
262
263 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
264 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
265 size_t l;
266
267 if (name[0] == '.' && !arg_all)
268 continue;
269
270 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
271 return log_oom();
272
273 images[n_images].name = name;
274 images[n_images].type = type;
275 images[n_images].read_only = read_only;
276 images[n_images].crtime = crtime;
277 images[n_images].mtime = mtime;
278 images[n_images].size = size;
279
280 l = strlen(name);
281 if (l > max_name)
282 max_name = l;
283
284 l = strlen(type);
285 if (l > max_type)
286 max_type = l;
287
288 if (crtime != 0) {
289 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
290 if (l > max_crtime)
291 max_crtime = l;
292 }
293
294 if (mtime != 0) {
295 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
296 if (l > max_mtime)
297 max_mtime = l;
298 }
299
300 if (size != (uint64_t) -1) {
301 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
302 if (l > max_size)
303 max_size = l;
304 }
305
306 n_images++;
307 }
308 if (r < 0)
309 return bus_log_parse_error(r);
310
311 r = sd_bus_message_exit_container(reply);
312 if (r < 0)
313 return bus_log_parse_error(r);
314
315 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
316
317 if (arg_legend)
318 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
319 (int) max_name, "NAME",
320 (int) max_type, "TYPE",
321 "RO",
322 (int) max_size, "USAGE",
323 (int) max_crtime, "CREATED",
324 (int) max_mtime, "MODIFIED");
325
326 for (j = 0; j < n_images; j++) {
327 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
328
329 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
330 (int) max_name, images[j].name,
331 (int) max_type, images[j].type,
332 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_normal() : "",
333 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
334 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
335 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
336 }
337
338 if (arg_legend)
339 printf("\n%zu images listed.\n", n_images);
340
341 return 0;
342 }
343
344 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
345 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
346 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
347 _cleanup_free_ char *path = NULL;
348 const char *cgroup;
349 int r;
350 unsigned c;
351
352 assert(bus);
353 assert(unit);
354
355 if (arg_transport == BUS_TRANSPORT_REMOTE)
356 return 0;
357
358 path = unit_dbus_path_from_name(unit);
359 if (!path)
360 return log_oom();
361
362 r = sd_bus_get_property(
363 bus,
364 "org.freedesktop.systemd1",
365 path,
366 unit_dbus_interface_from_name(unit),
367 "ControlGroup",
368 &error,
369 &reply,
370 "s");
371 if (r < 0) {
372 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
373 return r;
374 }
375
376 r = sd_bus_message_read(reply, "s", &cgroup);
377 if (r < 0)
378 return bus_log_parse_error(r);
379
380 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
381 return 0;
382
383 c = columns();
384 if (c > 18)
385 c -= 18;
386 else
387 c = 0;
388
389 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
390 return 0;
391 }
392
393 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
394 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
395 int r;
396
397 assert(bus);
398 assert(name);
399 assert(prefix);
400 assert(prefix2);
401
402 r = sd_bus_call_method(bus,
403 "org.freedesktop.machine1",
404 "/org/freedesktop/machine1",
405 "org.freedesktop.machine1.Manager",
406 "GetMachineAddresses",
407 NULL,
408 &reply,
409 "s", name);
410 if (r < 0)
411 return r;
412
413 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
414 if (r < 0)
415 return bus_log_parse_error(r);
416
417 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
418 int family;
419 const void *a;
420 size_t sz;
421 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
422
423 r = sd_bus_message_read(reply, "i", &family);
424 if (r < 0)
425 return bus_log_parse_error(r);
426
427 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
428 if (r < 0)
429 return bus_log_parse_error(r);
430
431 fputs(prefix, stdout);
432 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
433 if (family == AF_INET6 && ifi > 0)
434 printf("%%%i", ifi);
435 fputc('\n', stdout);
436
437 r = sd_bus_message_exit_container(reply);
438 if (r < 0)
439 return bus_log_parse_error(r);
440
441 if (prefix != prefix2)
442 prefix = prefix2;
443 }
444 if (r < 0)
445 return bus_log_parse_error(r);
446
447 r = sd_bus_message_exit_container(reply);
448 if (r < 0)
449 return bus_log_parse_error(r);
450
451 return 0;
452 }
453
454 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
455 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
456 const char *k, *v, *pretty = NULL;
457 int r;
458
459 assert(bus);
460 assert(name);
461 assert(prefix);
462
463 r = sd_bus_call_method(bus,
464 "org.freedesktop.machine1",
465 "/org/freedesktop/machine1",
466 "org.freedesktop.machine1.Manager",
467 "GetMachineOSRelease",
468 NULL,
469 &reply,
470 "s", name);
471 if (r < 0)
472 return r;
473
474 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
475 if (r < 0)
476 return bus_log_parse_error(r);
477
478 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
479 if (streq(k, "PRETTY_NAME"))
480 pretty = v;
481
482 }
483 if (r < 0)
484 return bus_log_parse_error(r);
485
486 r = sd_bus_message_exit_container(reply);
487 if (r < 0)
488 return bus_log_parse_error(r);
489
490 if (pretty)
491 printf("%s%s\n", prefix, pretty);
492
493 return 0;
494 }
495
496 typedef struct MachineStatusInfo {
497 char *name;
498 sd_id128_t id;
499 char *class;
500 char *service;
501 char *unit;
502 char *root_directory;
503 pid_t leader;
504 struct dual_timestamp timestamp;
505 int *netif;
506 unsigned n_netif;
507 } MachineStatusInfo;
508
509 static void machine_status_info_clear(MachineStatusInfo *info) {
510 if (info) {
511 free(info->name);
512 free(info->class);
513 free(info->service);
514 free(info->unit);
515 free(info->root_directory);
516 free(info->netif);
517 zero(*info);
518 }
519 }
520
521 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
522 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
523 char since2[FORMAT_TIMESTAMP_MAX], *s2;
524 int ifi = -1;
525
526 assert(bus);
527 assert(i);
528
529 fputs(strna(i->name), stdout);
530
531 if (!sd_id128_equal(i->id, SD_ID128_NULL))
532 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
533 else
534 putchar('\n');
535
536 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
537 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
538
539 if (s1)
540 printf("\t Since: %s; %s\n", s2, s1);
541 else if (s2)
542 printf("\t Since: %s\n", s2);
543
544 if (i->leader > 0) {
545 _cleanup_free_ char *t = NULL;
546
547 printf("\t Leader: %u", (unsigned) i->leader);
548
549 get_process_comm(i->leader, &t);
550 if (t)
551 printf(" (%s)", t);
552
553 putchar('\n');
554 }
555
556 if (i->service) {
557 printf("\t Service: %s", i->service);
558
559 if (i->class)
560 printf("; class %s", i->class);
561
562 putchar('\n');
563 } else if (i->class)
564 printf("\t Class: %s\n", i->class);
565
566 if (i->root_directory)
567 printf("\t Root: %s\n", i->root_directory);
568
569 if (i->n_netif > 0) {
570 unsigned c;
571
572 fputs("\t Iface:", stdout);
573
574 for (c = 0; c < i->n_netif; c++) {
575 char name[IF_NAMESIZE+1] = "";
576
577 if (if_indextoname(i->netif[c], name)) {
578 fputc(' ', stdout);
579 fputs(name, stdout);
580
581 if (ifi < 0)
582 ifi = i->netif[c];
583 else
584 ifi = 0;
585 } else
586 printf(" %i", i->netif[c]);
587 }
588
589 fputc('\n', stdout);
590 }
591
592 print_addresses(bus, i->name, ifi,
593 "\t Address: ",
594 "\t ");
595
596 print_os_release(bus, i->name, "\t OS: ");
597
598 if (i->unit) {
599 printf("\t Unit: %s\n", i->unit);
600 show_unit_cgroup(bus, i->unit, i->leader);
601
602 if (arg_transport == BUS_TRANSPORT_LOCAL)
603
604 show_journal_by_unit(
605 stdout,
606 i->unit,
607 arg_output,
608 0,
609 i->timestamp.monotonic,
610 arg_lines,
611 0,
612 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
613 SD_JOURNAL_LOCAL_ONLY,
614 true,
615 NULL);
616 }
617 }
618
619 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
620 MachineStatusInfo *i = userdata;
621 size_t l;
622 const void *v;
623 int r;
624
625 assert_cc(sizeof(int32_t) == sizeof(int));
626 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
627 if (r < 0)
628 return r;
629 if (r == 0)
630 return -EBADMSG;
631
632 i->n_netif = l / sizeof(int32_t);
633 i->netif = memdup(v, l);
634 if (!i->netif)
635 return -ENOMEM;
636
637 return 0;
638 }
639
640 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
641
642 static const struct bus_properties_map map[] = {
643 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
644 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
645 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
646 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
647 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
648 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
649 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
650 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
651 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
652 { "NetworkInterfaces", "ai", map_netif, 0 },
653 {}
654 };
655
656 _cleanup_(machine_status_info_clear) MachineStatusInfo info = {};
657 int r;
658
659 assert(verb);
660 assert(bus);
661 assert(path);
662 assert(new_line);
663
664 r = bus_map_all_properties(bus,
665 "org.freedesktop.machine1",
666 path,
667 map,
668 &info);
669 if (r < 0)
670 return log_error_errno(r, "Could not get properties: %m");
671
672 if (*new_line)
673 printf("\n");
674 *new_line = true;
675
676 print_machine_status_info(bus, &info);
677
678 return r;
679 }
680
681 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
682 int r;
683
684 assert(bus);
685 assert(path);
686 assert(new_line);
687
688 if (*new_line)
689 printf("\n");
690
691 *new_line = true;
692
693 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
694 if (r < 0)
695 log_error_errno(r, "Could not get properties: %m");
696
697 return r;
698 }
699
700 static int show_machine(int argc, char *argv[], void *userdata) {
701
702 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
703 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
704 bool properties, new_line = false;
705 sd_bus *bus = userdata;
706 int r = 0, i;
707
708 assert(bus);
709
710 properties = !strstr(argv[0], "status");
711
712 pager_open_if_enabled();
713
714 if (properties && argc <= 1) {
715
716 /* If no argument is specified, inspect the manager
717 * itself */
718 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
719 if (r < 0)
720 return r;
721 }
722
723 for (i = 1; i < argc; i++) {
724 const char *path = NULL;
725
726 r = sd_bus_call_method(
727 bus,
728 "org.freedesktop.machine1",
729 "/org/freedesktop/machine1",
730 "org.freedesktop.machine1.Manager",
731 "GetMachine",
732 &error,
733 &reply,
734 "s", argv[i]);
735 if (r < 0) {
736 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
737 return r;
738 }
739
740 r = sd_bus_message_read(reply, "o", &path);
741 if (r < 0)
742 return bus_log_parse_error(r);
743
744 if (properties)
745 r = show_machine_properties(bus, path, &new_line);
746 else
747 r = show_machine_info(argv[0], bus, path, &new_line);
748 }
749
750 return r;
751 }
752
753 typedef struct ImageStatusInfo {
754 char *name;
755 char *path;
756 char *type;
757 int read_only;
758 usec_t crtime;
759 usec_t mtime;
760 uint64_t usage;
761 uint64_t limit;
762 uint64_t usage_exclusive;
763 uint64_t limit_exclusive;
764 } ImageStatusInfo;
765
766 static void image_status_info_clear(ImageStatusInfo *info) {
767 if (info) {
768 free(info->name);
769 free(info->path);
770 free(info->type);
771 zero(*info);
772 }
773 }
774
775 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
776 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
777 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
778 char bs[FORMAT_BYTES_MAX], *s3;
779 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
780
781 assert(bus);
782 assert(i);
783
784 if (i->name) {
785 fputs(i->name, stdout);
786 putchar('\n');
787 }
788
789 if (i->type)
790 printf("\t Type: %s\n", i->type);
791
792 if (i->path)
793 printf("\t Path: %s\n", i->path);
794
795 printf("\t RO: %s%s%s\n",
796 i->read_only ? ansi_highlight_red() : "",
797 i->read_only ? "read-only" : "writable",
798 i->read_only ? ansi_normal() : "");
799
800 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
801 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
802 if (s1 && s2)
803 printf("\t Created: %s; %s\n", s2, s1);
804 else if (s2)
805 printf("\t Created: %s\n", s2);
806
807 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
808 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
809 if (s1 && s2)
810 printf("\tModified: %s; %s\n", s2, s1);
811 else if (s2)
812 printf("\tModified: %s\n", s2);
813
814 s3 = format_bytes(bs, sizeof(bs), i->usage);
815 s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
816 if (s3 && s4)
817 printf("\t Usage: %s (exclusive: %s)\n", s3, s4);
818 else if (s3)
819 printf("\t Usage: %s\n", s3);
820
821 s3 = format_bytes(bs, sizeof(bs), i->limit);
822 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
823 if (s3 && s4)
824 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
825 else if (s3)
826 printf("\t Limit: %s\n", s3);
827 }
828
829 static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
830
831 static const struct bus_properties_map map[] = {
832 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
833 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
834 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
835 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
836 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
837 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
838 { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
839 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
840 { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
841 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
842 {}
843 };
844
845 _cleanup_(image_status_info_clear) ImageStatusInfo info = {};
846 int r;
847
848 assert(bus);
849 assert(path);
850 assert(new_line);
851
852 r = bus_map_all_properties(bus,
853 "org.freedesktop.machine1",
854 path,
855 map,
856 &info);
857 if (r < 0)
858 return log_error_errno(r, "Could not get properties: %m");
859
860 if (*new_line)
861 printf("\n");
862 *new_line = true;
863
864 print_image_status_info(bus, &info);
865
866 return r;
867 }
868
869 typedef struct PoolStatusInfo {
870 char *path;
871 uint64_t usage;
872 uint64_t limit;
873 } PoolStatusInfo;
874
875 static void pool_status_info_clear(PoolStatusInfo *info) {
876 if (info) {
877 free(info->path);
878 zero(*info);
879 info->usage = -1;
880 info->limit = -1;
881 }
882 }
883
884 static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
885 char bs[FORMAT_BYTES_MAX], *s;
886
887 if (i->path)
888 printf("\t Path: %s\n", i->path);
889
890 s = format_bytes(bs, sizeof(bs), i->usage);
891 if (s)
892 printf("\t Usage: %s\n", s);
893
894 s = format_bytes(bs, sizeof(bs), i->limit);
895 if (s)
896 printf("\t Limit: %s\n", s);
897 }
898
899 static int show_pool_info(sd_bus *bus) {
900
901 static const struct bus_properties_map map[] = {
902 { "PoolPath", "s", NULL, offsetof(PoolStatusInfo, path) },
903 { "PoolUsage", "t", NULL, offsetof(PoolStatusInfo, usage) },
904 { "PoolLimit", "t", NULL, offsetof(PoolStatusInfo, limit) },
905 {}
906 };
907
908 _cleanup_(pool_status_info_clear) PoolStatusInfo info = {
909 .usage = (uint64_t) -1,
910 .limit = (uint64_t) -1,
911 };
912 int r;
913
914 assert(bus);
915
916 r = bus_map_all_properties(bus,
917 "org.freedesktop.machine1",
918 "/org/freedesktop/machine1",
919 map,
920 &info);
921 if (r < 0)
922 return log_error_errno(r, "Could not get properties: %m");
923
924 print_pool_status_info(bus, &info);
925
926 return 0;
927 }
928
929
930 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
931 int r;
932
933 assert(bus);
934 assert(path);
935 assert(new_line);
936
937 if (*new_line)
938 printf("\n");
939
940 *new_line = true;
941
942 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
943 if (r < 0)
944 log_error_errno(r, "Could not get properties: %m");
945
946 return r;
947 }
948
949 static int show_image(int argc, char *argv[], void *userdata) {
950
951 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
952 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
953 bool properties, new_line = false;
954 sd_bus *bus = userdata;
955 int r = 0, i;
956
957 assert(bus);
958
959 properties = !strstr(argv[0], "status");
960
961 pager_open_if_enabled();
962
963 if (argc <= 1) {
964
965 /* If no argument is specified, inspect the manager
966 * itself */
967
968 if (properties)
969 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
970 else
971 r = show_pool_info(bus);
972 if (r < 0)
973 return r;
974 }
975
976 for (i = 1; i < argc; i++) {
977 const char *path = NULL;
978
979 r = sd_bus_call_method(
980 bus,
981 "org.freedesktop.machine1",
982 "/org/freedesktop/machine1",
983 "org.freedesktop.machine1.Manager",
984 "GetImage",
985 &error,
986 &reply,
987 "s", argv[i]);
988 if (r < 0) {
989 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
990 return r;
991 }
992
993 r = sd_bus_message_read(reply, "o", &path);
994 if (r < 0)
995 return bus_log_parse_error(r);
996
997 if (properties)
998 r = show_image_properties(bus, path, &new_line);
999 else
1000 r = show_image_info(bus, path, &new_line);
1001 }
1002
1003 return r;
1004 }
1005
1006 static int kill_machine(int argc, char *argv[], void *userdata) {
1007 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1008 sd_bus *bus = userdata;
1009 int r, i;
1010
1011 assert(bus);
1012
1013 polkit_agent_open_if_enabled();
1014
1015 if (!arg_kill_who)
1016 arg_kill_who = "all";
1017
1018 for (i = 1; i < argc; i++) {
1019 r = sd_bus_call_method(
1020 bus,
1021 "org.freedesktop.machine1",
1022 "/org/freedesktop/machine1",
1023 "org.freedesktop.machine1.Manager",
1024 "KillMachine",
1025 &error,
1026 NULL,
1027 "ssi", argv[i], arg_kill_who, arg_signal);
1028 if (r < 0) {
1029 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
1030 return r;
1031 }
1032 }
1033
1034 return 0;
1035 }
1036
1037 static int reboot_machine(int argc, char *argv[], void *userdata) {
1038 arg_kill_who = "leader";
1039 arg_signal = SIGINT; /* sysvinit + systemd */
1040
1041 return kill_machine(argc, argv, userdata);
1042 }
1043
1044 static int poweroff_machine(int argc, char *argv[], void *userdata) {
1045 arg_kill_who = "leader";
1046 arg_signal = SIGRTMIN+4; /* only systemd */
1047
1048 return kill_machine(argc, argv, userdata);
1049 }
1050
1051 static int terminate_machine(int argc, char *argv[], void *userdata) {
1052 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1053 sd_bus *bus = userdata;
1054 int r, i;
1055
1056 assert(bus);
1057
1058 polkit_agent_open_if_enabled();
1059
1060 for (i = 1; i < argc; i++) {
1061 r = sd_bus_call_method(
1062 bus,
1063 "org.freedesktop.machine1",
1064 "/org/freedesktop/machine1",
1065 "org.freedesktop.machine1.Manager",
1066 "TerminateMachine",
1067 &error,
1068 NULL,
1069 "s", argv[i]);
1070 if (r < 0) {
1071 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
1072 return r;
1073 }
1074 }
1075
1076 return 0;
1077 }
1078
1079 static int copy_files(int argc, char *argv[], void *userdata) {
1080 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1081 _cleanup_free_ char *abs_host_path = NULL;
1082 char *dest, *host_path, *container_path;
1083 sd_bus *bus = userdata;
1084 bool copy_from;
1085 int r;
1086
1087 assert(bus);
1088
1089 polkit_agent_open_if_enabled();
1090
1091 copy_from = streq(argv[0], "copy-from");
1092 dest = argv[3] ?: argv[2];
1093 host_path = copy_from ? dest : argv[2];
1094 container_path = copy_from ? argv[2] : dest;
1095
1096 if (!path_is_absolute(host_path)) {
1097 r = path_make_absolute_cwd(host_path, &abs_host_path);
1098 if (r < 0)
1099 return log_error_errno(r, "Failed to make path absolute: %m");
1100
1101 host_path = abs_host_path;
1102 }
1103
1104 r = sd_bus_call_method(
1105 bus,
1106 "org.freedesktop.machine1",
1107 "/org/freedesktop/machine1",
1108 "org.freedesktop.machine1.Manager",
1109 copy_from ? "CopyFromMachine" : "CopyToMachine",
1110 &error,
1111 NULL,
1112 "sss",
1113 argv[1],
1114 copy_from ? container_path : host_path,
1115 copy_from ? host_path : container_path);
1116 if (r < 0)
1117 return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r));
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, PTYForwardFlags flags, 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, flags, 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 (flags & PTY_FORWARD_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, PTY_FORWARD_IGNORE_VHANGUP, 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, 0, 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 (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
2387 limit = (uint64_t) -1;
2388 else {
2389 r = parse_size(argv[argc-1], 1024, &limit);
2390 if (r < 0)
2391 return log_error("Failed to parse size: %s", argv[argc-1]);
2392 }
2393
2394 if (argc > 2)
2395 /* With two arguments changes the quota limit of the
2396 * specified image */
2397 r = sd_bus_call_method(
2398 bus,
2399 "org.freedesktop.machine1",
2400 "/org/freedesktop/machine1",
2401 "org.freedesktop.machine1.Manager",
2402 "SetImageLimit",
2403 &error,
2404 NULL,
2405 "st", argv[1], limit);
2406 else
2407 /* With one argument changes the pool quota limit */
2408 r = sd_bus_call_method(
2409 bus,
2410 "org.freedesktop.machine1",
2411 "/org/freedesktop/machine1",
2412 "org.freedesktop.machine1.Manager",
2413 "SetPoolLimit",
2414 &error,
2415 NULL,
2416 "t", limit);
2417
2418 if (r < 0) {
2419 log_error("Could not set limit: %s", bus_error_message(&error, -r));
2420 return r;
2421 }
2422
2423 return 0;
2424 }
2425
2426 static int help(int argc, char *argv[], void *userdata) {
2427
2428 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2429 "Send control commands to or query the virtual machine and container\n"
2430 "registration manager.\n\n"
2431 " -h --help Show this help\n"
2432 " --version Show package version\n"
2433 " --no-pager Do not pipe output into a pager\n"
2434 " --no-legend Do not show the headers and footers\n"
2435 " --no-ask-password Do not ask for system passwords\n"
2436 " -H --host=[USER@]HOST Operate on remote host\n"
2437 " -M --machine=CONTAINER Operate on local container\n"
2438 " -p --property=NAME Show only properties by this name\n"
2439 " -q --quiet Suppress output\n"
2440 " -a --all Show all properties, including empty ones\n"
2441 " -l --full Do not ellipsize output\n"
2442 " --kill-who=WHO Who to send signal to\n"
2443 " -s --signal=SIGNAL Which signal to send\n"
2444 " --uid=USER Specify user ID to invoke shell as\n"
2445 " --setenv=VAR=VALUE Add an environment variable for shell\n"
2446 " --read-only Create read-only bind mount\n"
2447 " --mkdir Create directory before bind mounting, if missing\n"
2448 " -n --lines=INTEGER Number of journal entries to show\n"
2449 " -o --output=STRING Change journal output mode (short,\n"
2450 " short-monotonic, verbose, export, json,\n"
2451 " json-pretty, json-sse, cat)\n"
2452 " --verify=MODE Verification mode for downloaded images (no,\n"
2453 " checksum, signature)\n"
2454 " --force Download image even if already exists\n"
2455 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2456 " downloads\n\n"
2457 "Machine Commands:\n"
2458 " list List running VMs and containers\n"
2459 " status NAME... Show VM/container details\n"
2460 " show [NAME...] Show properties of one or more VMs/containers\n"
2461 " start NAME... Start container as a service\n"
2462 " login [NAME] Get a login prompt in a container or on the\n"
2463 " local host\n"
2464 " shell [[USER@]NAME [COMMAND...]]\n"
2465 " Invoke a shell (or other command) in a container\n"
2466 " or on the local host\n"
2467 " enable NAME... Enable automatic container start at boot\n"
2468 " disable NAME... Disable automatic container start at boot\n"
2469 " poweroff NAME... Power off one or more containers\n"
2470 " reboot NAME... Reboot one or more containers\n"
2471 " terminate NAME... Terminate one or more VMs/containers\n"
2472 " kill NAME... Send signal to processes of a VM/container\n"
2473 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2474 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2475 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2476 "Image Commands:\n"
2477 " list-images Show available container and VM images\n"
2478 " image-status [NAME...] Show image details\n"
2479 " show-image [NAME...] Show properties of image\n"
2480 " clone NAME NAME Clone an image\n"
2481 " rename NAME NAME Rename an image\n"
2482 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2483 " remove NAME... Remove an image\n"
2484 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2485 "Image Transfer Commands:\n"
2486 " pull-tar URL [NAME] Download a TAR container image\n"
2487 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2488 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2489 " import-tar FILE [NAME] Import a local TAR container image\n"
2490 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2491 " export-tar NAME [FILE] Export a TAR container image locally\n"
2492 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2493 " list-transfers Show list of downloads in progress\n"
2494 " cancel-transfer Cancel a download\n"
2495 , program_invocation_short_name);
2496
2497 return 0;
2498 }
2499
2500 static int parse_argv(int argc, char *argv[]) {
2501
2502 enum {
2503 ARG_VERSION = 0x100,
2504 ARG_NO_PAGER,
2505 ARG_NO_LEGEND,
2506 ARG_KILL_WHO,
2507 ARG_READ_ONLY,
2508 ARG_MKDIR,
2509 ARG_NO_ASK_PASSWORD,
2510 ARG_VERIFY,
2511 ARG_FORCE,
2512 ARG_DKR_INDEX_URL,
2513 ARG_FORMAT,
2514 ARG_UID,
2515 ARG_SETENV,
2516 };
2517
2518 static const struct option options[] = {
2519 { "help", no_argument, NULL, 'h' },
2520 { "version", no_argument, NULL, ARG_VERSION },
2521 { "property", required_argument, NULL, 'p' },
2522 { "all", no_argument, NULL, 'a' },
2523 { "full", no_argument, NULL, 'l' },
2524 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2525 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2526 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2527 { "signal", required_argument, NULL, 's' },
2528 { "host", required_argument, NULL, 'H' },
2529 { "machine", required_argument, NULL, 'M' },
2530 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2531 { "mkdir", no_argument, NULL, ARG_MKDIR },
2532 { "quiet", no_argument, NULL, 'q' },
2533 { "lines", required_argument, NULL, 'n' },
2534 { "output", required_argument, NULL, 'o' },
2535 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2536 { "verify", required_argument, NULL, ARG_VERIFY },
2537 { "force", no_argument, NULL, ARG_FORCE },
2538 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
2539 { "format", required_argument, NULL, ARG_FORMAT },
2540 { "uid", required_argument, NULL, ARG_UID },
2541 { "setenv", required_argument, NULL, ARG_SETENV },
2542 {}
2543 };
2544
2545 int c, r;
2546
2547 assert(argc >= 0);
2548 assert(argv);
2549
2550 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2551
2552 switch (c) {
2553
2554 case 'h':
2555 return help(0, NULL, NULL);
2556
2557 case ARG_VERSION:
2558 return version();
2559
2560 case 'p':
2561 r = strv_extend(&arg_property, optarg);
2562 if (r < 0)
2563 return log_oom();
2564
2565 /* If the user asked for a particular
2566 * property, show it to him, even if it is
2567 * empty. */
2568 arg_all = true;
2569 break;
2570
2571 case 'a':
2572 arg_all = true;
2573 break;
2574
2575 case 'l':
2576 arg_full = true;
2577 break;
2578
2579 case 'n':
2580 if (safe_atou(optarg, &arg_lines) < 0) {
2581 log_error("Failed to parse lines '%s'", optarg);
2582 return -EINVAL;
2583 }
2584 break;
2585
2586 case 'o':
2587 arg_output = output_mode_from_string(optarg);
2588 if (arg_output < 0) {
2589 log_error("Unknown output '%s'.", optarg);
2590 return -EINVAL;
2591 }
2592 break;
2593
2594 case ARG_NO_PAGER:
2595 arg_no_pager = true;
2596 break;
2597
2598 case ARG_NO_LEGEND:
2599 arg_legend = false;
2600 break;
2601
2602 case ARG_KILL_WHO:
2603 arg_kill_who = optarg;
2604 break;
2605
2606 case 's':
2607 arg_signal = signal_from_string_try_harder(optarg);
2608 if (arg_signal < 0) {
2609 log_error("Failed to parse signal string %s.", optarg);
2610 return -EINVAL;
2611 }
2612 break;
2613
2614 case ARG_NO_ASK_PASSWORD:
2615 arg_ask_password = false;
2616 break;
2617
2618 case 'H':
2619 arg_transport = BUS_TRANSPORT_REMOTE;
2620 arg_host = optarg;
2621 break;
2622
2623 case 'M':
2624 arg_transport = BUS_TRANSPORT_MACHINE;
2625 arg_host = optarg;
2626 break;
2627
2628 case ARG_READ_ONLY:
2629 arg_read_only = true;
2630 break;
2631
2632 case ARG_MKDIR:
2633 arg_mkdir = true;
2634 break;
2635
2636 case 'q':
2637 arg_quiet = true;
2638 break;
2639
2640 case ARG_VERIFY:
2641 arg_verify = import_verify_from_string(optarg);
2642 if (arg_verify < 0) {
2643 log_error("Failed to parse --verify= setting: %s", optarg);
2644 return -EINVAL;
2645 }
2646 break;
2647
2648 case ARG_FORCE:
2649 arg_force = true;
2650 break;
2651
2652 case ARG_DKR_INDEX_URL:
2653 if (!http_url_is_valid(optarg)) {
2654 log_error("Index URL is invalid: %s", optarg);
2655 return -EINVAL;
2656 }
2657
2658 arg_dkr_index_url = optarg;
2659 break;
2660
2661 case ARG_FORMAT:
2662 if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
2663 log_error("Unknown format: %s", optarg);
2664 return -EINVAL;
2665 }
2666
2667 arg_format = optarg;
2668 break;
2669
2670 case ARG_UID:
2671 arg_uid = optarg;
2672 break;
2673
2674 case ARG_SETENV:
2675 if (!env_assignment_is_valid(optarg)) {
2676 log_error("Environment assignment invalid: %s", optarg);
2677 return -EINVAL;
2678 }
2679
2680 r = strv_extend(&arg_setenv, optarg);
2681 if (r < 0)
2682 return log_oom();
2683 break;
2684
2685 case '?':
2686 return -EINVAL;
2687
2688 default:
2689 assert_not_reached("Unhandled option");
2690 }
2691
2692 return 1;
2693 }
2694
2695 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2696
2697 static const Verb verbs[] = {
2698 { "help", VERB_ANY, VERB_ANY, 0, help },
2699 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2700 { "list-images", VERB_ANY, 1, 0, list_images },
2701 { "status", 2, VERB_ANY, 0, show_machine },
2702 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
2703 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2704 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2705 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2706 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2707 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2708 { "kill", 2, VERB_ANY, 0, kill_machine },
2709 { "login", VERB_ANY, 2, 0, login_machine },
2710 { "shell", VERB_ANY, VERB_ANY, 0, shell_machine },
2711 { "bind", 3, 4, 0, bind_mount },
2712 { "copy-to", 3, 4, 0, copy_files },
2713 { "copy-from", 3, 4, 0, copy_files },
2714 { "remove", 2, VERB_ANY, 0, remove_image },
2715 { "rename", 3, 3, 0, rename_image },
2716 { "clone", 3, 3, 0, clone_image },
2717 { "read-only", 2, 3, 0, read_only_image },
2718 { "start", 2, VERB_ANY, 0, start_machine },
2719 { "enable", 2, VERB_ANY, 0, enable_machine },
2720 { "disable", 2, VERB_ANY, 0, enable_machine },
2721 { "import-tar", 2, 3, 0, import_tar },
2722 { "import-raw", 2, 3, 0, import_raw },
2723 { "export-tar", 2, 3, 0, export_tar },
2724 { "export-raw", 2, 3, 0, export_raw },
2725 { "pull-tar", 2, 3, 0, pull_tar },
2726 { "pull-raw", 2, 3, 0, pull_raw },
2727 { "pull-dkr", 2, 3, 0, pull_dkr },
2728 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2729 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
2730 { "set-limit", 2, 3, 0, set_limit },
2731 {}
2732 };
2733
2734 return dispatch_verb(argc, argv, verbs, bus);
2735 }
2736
2737 int main(int argc, char*argv[]) {
2738 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
2739 int r;
2740
2741 setlocale(LC_ALL, "");
2742 log_parse_environment();
2743 log_open();
2744
2745 r = parse_argv(argc, argv);
2746 if (r <= 0)
2747 goto finish;
2748
2749 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
2750 if (r < 0) {
2751 log_error_errno(r, "Failed to create bus connection: %m");
2752 goto finish;
2753 }
2754
2755 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2756
2757 r = machinectl_main(argc, argv, bus);
2758
2759 finish:
2760 pager_close();
2761 polkit_agent_close();
2762
2763 strv_free(arg_property);
2764 strv_free(arg_setenv);
2765
2766 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2767 }