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