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