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