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