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