]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machinectl.c
treewide: fix multiple typos
[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 ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
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 char last_char = 0;
1359 bool machine_died;
1360
1361 assert(bus);
1362
1363 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1364 arg_transport != BUS_TRANSPORT_MACHINE) {
1365 log_error("Login only supported on local machines.");
1366 return -ENOTSUP;
1367 }
1368
1369 polkit_agent_open_if_enabled();
1370
1371 r = sd_event_default(&event);
1372 if (r < 0)
1373 return log_error_errno(r, "Failed to get event loop: %m");
1374
1375 r = sd_bus_attach_event(bus, event, 0);
1376 if (r < 0)
1377 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1378
1379 match = strappenda("type='signal',"
1380 "sender='org.freedesktop.machine1',"
1381 "path='/org/freedesktop/machine1',",
1382 "interface='org.freedesktop.machine1.Manager',"
1383 "member='MachineRemoved',"
1384 "arg0='",
1385 argv[1],
1386 "'");
1387
1388 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1389 if (r < 0)
1390 return log_error_errno(r, "Failed to add machine removal match: %m");
1391
1392 r = sd_bus_message_new_method_call(bus,
1393 &m,
1394 "org.freedesktop.machine1",
1395 "/org/freedesktop/machine1",
1396 "org.freedesktop.machine1.Manager",
1397 "OpenMachineLogin");
1398 if (r < 0)
1399 return bus_log_create_error(r);
1400
1401 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1402 if (r < 0)
1403 return bus_log_create_error(r);
1404
1405 r = sd_bus_message_append(m, "s", argv[1]);
1406 if (r < 0)
1407 return bus_log_create_error(r);
1408
1409 r = sd_bus_call(bus, m, 0, &error, &reply);
1410 if (r < 0) {
1411 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1412 return r;
1413 }
1414
1415 r = sd_bus_message_read(reply, "hs", &master, &pty);
1416 if (r < 0)
1417 return bus_log_parse_error(r);
1418
1419 sigprocmask_many(SIG_BLOCK, SIGWINCH, SIGTERM, SIGINT, -1);
1420
1421 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1422
1423 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1424 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1425
1426 r = pty_forward_new(event, master, true, &forward);
1427 if (r < 0)
1428 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1429
1430 r = sd_event_loop(event);
1431 if (r < 0)
1432 return log_error_errno(r, "Failed to run event loop: %m");
1433
1434 pty_forward_get_last_char(forward, &last_char);
1435 machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
1436
1437 forward = pty_forward_free(forward);
1438
1439 if (last_char != '\n')
1440 fputc('\n', stdout);
1441
1442 if (machine_died)
1443 log_info("Machine %s terminated.", argv[1]);
1444 else
1445 log_info("Connection to machine %s terminated.", argv[1]);
1446
1447 sd_event_get_exit_code(event, &ret);
1448 return ret;
1449 }
1450
1451 static int remove_image(int argc, char *argv[], void *userdata) {
1452 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1453 sd_bus *bus = userdata;
1454 int r, i;
1455
1456 assert(bus);
1457
1458 polkit_agent_open_if_enabled();
1459
1460 for (i = 1; i < argc; i++) {
1461 r = sd_bus_call_method(
1462 bus,
1463 "org.freedesktop.machine1",
1464 "/org/freedesktop/machine1",
1465 "org.freedesktop.machine1.Manager",
1466 "RemoveImage",
1467 &error,
1468 NULL,
1469 "s", argv[i]);
1470 if (r < 0) {
1471 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1472 return r;
1473 }
1474 }
1475
1476 return 0;
1477 }
1478
1479 static int rename_image(int argc, char *argv[], void *userdata) {
1480 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1481 sd_bus *bus = userdata;
1482 int r;
1483
1484 polkit_agent_open_if_enabled();
1485
1486 r = sd_bus_call_method(
1487 bus,
1488 "org.freedesktop.machine1",
1489 "/org/freedesktop/machine1",
1490 "org.freedesktop.machine1.Manager",
1491 "RenameImage",
1492 &error,
1493 NULL,
1494 "ss", argv[1], argv[2]);
1495 if (r < 0) {
1496 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1497 return r;
1498 }
1499
1500 return 0;
1501 }
1502
1503 static int clone_image(int argc, char *argv[], void *userdata) {
1504 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1505 sd_bus *bus = userdata;
1506 int r;
1507
1508 polkit_agent_open_if_enabled();
1509
1510 r = sd_bus_call_method(
1511 bus,
1512 "org.freedesktop.machine1",
1513 "/org/freedesktop/machine1",
1514 "org.freedesktop.machine1.Manager",
1515 "CloneImage",
1516 &error,
1517 NULL,
1518 "ssb", argv[1], argv[2], arg_read_only);
1519 if (r < 0) {
1520 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1521 return r;
1522 }
1523
1524 return 0;
1525 }
1526
1527 static int read_only_image(int argc, char *argv[], void *userdata) {
1528 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1529 sd_bus *bus = userdata;
1530 int b = true, r;
1531
1532 if (argc > 2) {
1533 b = parse_boolean(argv[2]);
1534 if (b < 0) {
1535 log_error("Failed to parse boolean argument: %s", argv[2]);
1536 return -EINVAL;
1537 }
1538 }
1539
1540 polkit_agent_open_if_enabled();
1541
1542 r = sd_bus_call_method(
1543 bus,
1544 "org.freedesktop.machine1",
1545 "/org/freedesktop/machine1",
1546 "org.freedesktop.machine1.Manager",
1547 "MarkImageReadOnly",
1548 &error,
1549 NULL,
1550 "sb", argv[1], b);
1551 if (r < 0) {
1552 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1553 return r;
1554 }
1555
1556 return 0;
1557 }
1558
1559 static int start_machine(int argc, char *argv[], void *userdata) {
1560 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1561 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1562 sd_bus *bus = userdata;
1563 int r, i;
1564
1565 assert(bus);
1566
1567 polkit_agent_open_if_enabled();
1568
1569 r = bus_wait_for_jobs_new(bus, &w);
1570 if (r < 0)
1571 return log_oom();
1572
1573 for (i = 1; i < argc; i++) {
1574 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1575 _cleanup_free_ char *e = NULL, *unit = NULL;
1576 const char *object;
1577
1578 if (!machine_name_is_valid(argv[i])) {
1579 log_error("Invalid machine name %s.", argv[i]);
1580 return -EINVAL;
1581 }
1582
1583 e = unit_name_escape(argv[i]);
1584 if (!e)
1585 return log_oom();
1586
1587 unit = unit_name_build("systemd-nspawn", e, ".service");
1588 if (!unit)
1589 return log_oom();
1590
1591 r = sd_bus_message_new_method_call(
1592 bus,
1593 &m,
1594 "org.freedesktop.systemd1",
1595 "/org/freedesktop/systemd1",
1596 "org.freedesktop.systemd1.Manager",
1597 "StartUnit");
1598 if (r < 0)
1599 return bus_log_create_error(r);
1600
1601 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1602 if (r < 0)
1603 return bus_log_create_error(r);
1604
1605 r = sd_bus_message_append(m, "ss", unit, "fail");
1606 if (r < 0)
1607 return bus_log_create_error(r);
1608
1609 r = sd_bus_call(bus, m, 0, &error, &reply);
1610 if (r < 0) {
1611 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1612 return r;
1613 }
1614
1615 r = sd_bus_message_read(reply, "o", &object);
1616 if (r < 0)
1617 return bus_log_parse_error(r);
1618
1619 r = bus_wait_for_jobs_add(w, object);
1620 if (r < 0)
1621 return log_oom();
1622 }
1623
1624 r = bus_wait_for_jobs(w, arg_quiet);
1625 if (r < 0)
1626 return r;
1627
1628 return 0;
1629 }
1630
1631 static int enable_machine(int argc, char *argv[], void *userdata) {
1632 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1633 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1634 int carries_install_info = 0;
1635 const char *method = NULL;
1636 sd_bus *bus = userdata;
1637 int r, i;
1638
1639 assert(bus);
1640
1641 polkit_agent_open_if_enabled();
1642
1643 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1644
1645 r = sd_bus_message_new_method_call(
1646 bus,
1647 &m,
1648 "org.freedesktop.systemd1",
1649 "/org/freedesktop/systemd1",
1650 "org.freedesktop.systemd1.Manager",
1651 method);
1652 if (r < 0)
1653 return bus_log_create_error(r);
1654
1655 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1656 if (r < 0)
1657 return bus_log_create_error(r);
1658
1659 r = sd_bus_message_open_container(m, 'a', "s");
1660 if (r < 0)
1661 return bus_log_create_error(r);
1662
1663 for (i = 1; i < argc; i++) {
1664 _cleanup_free_ char *e = NULL, *unit = NULL;
1665
1666 if (!machine_name_is_valid(argv[i])) {
1667 log_error("Invalid machine name %s.", argv[i]);
1668 return -EINVAL;
1669 }
1670
1671 e = unit_name_escape(argv[i]);
1672 if (!e)
1673 return log_oom();
1674
1675 unit = unit_name_build("systemd-nspawn", e, ".service");
1676 if (!unit)
1677 return log_oom();
1678
1679 r = sd_bus_message_append(m, "s", unit);
1680 if (r < 0)
1681 return bus_log_create_error(r);
1682 }
1683
1684 r = sd_bus_message_close_container(m);
1685 if (r < 0)
1686 return bus_log_create_error(r);
1687
1688 if (streq(argv[0], "enable"))
1689 r = sd_bus_message_append(m, "bb", false, false);
1690 else
1691 r = sd_bus_message_append(m, "b", false);
1692 if (r < 0)
1693 return bus_log_create_error(r);
1694
1695 r = sd_bus_call(bus, m, 0, &error, &reply);
1696 if (r < 0) {
1697 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1698 return r;
1699 }
1700
1701 if (streq(argv[0], "enable")) {
1702 r = sd_bus_message_read(reply, "b", carries_install_info);
1703 if (r < 0)
1704 return bus_log_parse_error(r);
1705 }
1706
1707 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1708 if (r < 0)
1709 return r;
1710
1711 m = sd_bus_message_unref(m);
1712
1713 r = sd_bus_message_new_method_call(
1714 bus,
1715 &m,
1716 "org.freedesktop.systemd1",
1717 "/org/freedesktop/systemd1",
1718 "org.freedesktop.systemd1.Manager",
1719 "Reload");
1720 if (r < 0)
1721 return bus_log_create_error(r);
1722
1723 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1724 if (r < 0)
1725 return bus_log_create_error(r);
1726
1727 r = sd_bus_call(bus, m, 0, &error, NULL);
1728 if (r < 0) {
1729 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1730 return r;
1731 }
1732
1733 return 0;
1734 }
1735
1736 static int match_log_message(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1737 const char **our_path = userdata, *line;
1738 unsigned priority;
1739 int r;
1740
1741 assert(bus);
1742 assert(m);
1743 assert(our_path);
1744
1745 r = sd_bus_message_read(m, "us", &priority, &line);
1746 if (r < 0) {
1747 bus_log_parse_error(r);
1748 return 0;
1749 }
1750
1751 if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
1752 return 0;
1753
1754 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1755 return 0;
1756
1757 log_full(priority, "%s", line);
1758 return 0;
1759 }
1760
1761 static int match_transfer_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1762 const char **our_path = userdata, *path, *result;
1763 uint32_t id;
1764 int r;
1765
1766 assert(bus);
1767 assert(m);
1768 assert(our_path);
1769
1770 r = sd_bus_message_read(m, "uos", &id, &path, &result);
1771 if (r < 0) {
1772 bus_log_parse_error(r);
1773 return 0;
1774 }
1775
1776 if (!streq_ptr(*our_path, path))
1777 return 0;
1778
1779 sd_event_exit(sd_bus_get_event(bus), !streq_ptr(result, "done"));
1780 return 0;
1781 }
1782
1783 static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1784 assert(s);
1785 assert(si);
1786
1787 if (!arg_quiet)
1788 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
1789
1790 sd_event_exit(sd_event_source_get_event(s), EINTR);
1791 return 0;
1792 }
1793
1794 static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
1795 _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1796 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1797 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1798 _cleanup_event_unref_ sd_event* event = NULL;
1799 const char *path = NULL;
1800 uint32_t id;
1801 int r;
1802
1803 assert(bus);
1804 assert(m);
1805
1806 polkit_agent_open_if_enabled();
1807
1808 r = sd_event_default(&event);
1809 if (r < 0)
1810 return log_error_errno(r, "Failed to get event loop: %m");
1811
1812 r = sd_bus_attach_event(bus, event, 0);
1813 if (r < 0)
1814 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1815
1816 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1817 if (r < 0)
1818 return bus_log_create_error(r);
1819
1820 r = sd_bus_add_match(
1821 bus,
1822 &slot_job_removed,
1823 "type='signal',"
1824 "sender='org.freedesktop.import1',"
1825 "interface='org.freedesktop.import1.Manager',"
1826 "member='TransferRemoved',"
1827 "path='/org/freedesktop/import1'",
1828 match_transfer_removed, &path);
1829 if (r < 0)
1830 return log_error_errno(r, "Failed to install match: %m");
1831
1832 r = sd_bus_add_match(
1833 bus,
1834 &slot_log_message,
1835 "type='signal',"
1836 "sender='org.freedesktop.import1',"
1837 "interface='org.freedesktop.import1.Transfer',"
1838 "member='LogMessage'",
1839 match_log_message, &path);
1840 if (r < 0)
1841 return log_error_errno(r, "Failed to install match: %m");
1842
1843 r = sd_bus_call(bus, m, 0, &error, &reply);
1844 if (r < 0) {
1845 log_error("Failed pull image: %s", bus_error_message(&error, -r));
1846 return r;
1847 }
1848
1849 r = sd_bus_message_read(reply, "uo", &id, &path);
1850 if (r < 0)
1851 return bus_log_parse_error(r);
1852
1853 sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
1854
1855 if (!arg_quiet)
1856 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
1857
1858 sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
1859 sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
1860
1861 r = sd_event_loop(event);
1862 if (r < 0)
1863 return log_error_errno(r, "Failed to run event loop: %m");
1864
1865 return -r;
1866 }
1867
1868 static int pull_tar(int argc, char *argv[], void *userdata) {
1869 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1870 _cleanup_free_ char *l = NULL, *ll = NULL;
1871 const char *local, *remote;
1872 sd_bus *bus = userdata;
1873 int r;
1874
1875 assert(bus);
1876
1877 remote = argv[1];
1878 if (!http_url_is_valid(remote)) {
1879 log_error("URL '%s' is not valid.", remote);
1880 return -EINVAL;
1881 }
1882
1883 if (argc >= 3)
1884 local = argv[2];
1885 else {
1886 r = import_url_last_component(remote, &l);
1887 if (r < 0)
1888 return log_error_errno(r, "Failed to get final component of URL: %m");
1889
1890 local = l;
1891 }
1892
1893 if (isempty(local) || streq(local, "-"))
1894 local = NULL;
1895
1896 if (local) {
1897 r = tar_strip_suffixes(local, &ll);
1898 if (r < 0)
1899 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1900
1901 local = ll;
1902
1903 if (!machine_name_is_valid(local)) {
1904 log_error("Local name %s is not a suitable machine name.", local);
1905 return -EINVAL;
1906 }
1907 }
1908
1909 r = sd_bus_message_new_method_call(
1910 bus,
1911 &m,
1912 "org.freedesktop.import1",
1913 "/org/freedesktop/import1",
1914 "org.freedesktop.import1.Manager",
1915 "PullTar");
1916 if (r < 0)
1917 return bus_log_create_error(r);
1918
1919 r = sd_bus_message_append(
1920 m,
1921 "sssb",
1922 remote,
1923 local,
1924 import_verify_to_string(arg_verify),
1925 arg_force);
1926 if (r < 0)
1927 return bus_log_create_error(r);
1928
1929 return pull_image_common(bus, m);
1930 }
1931
1932 static int pull_raw(int argc, char *argv[], void *userdata) {
1933 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1934 _cleanup_free_ char *l = NULL, *ll = NULL;
1935 const char *local, *remote;
1936 sd_bus *bus = userdata;
1937 int r;
1938
1939 assert(bus);
1940
1941 remote = argv[1];
1942 if (!http_url_is_valid(remote)) {
1943 log_error("URL '%s' is not valid.", remote);
1944 return -EINVAL;
1945 }
1946
1947 if (argc >= 3)
1948 local = argv[2];
1949 else {
1950 r = import_url_last_component(remote, &l);
1951 if (r < 0)
1952 return log_error_errno(r, "Failed to get final component of URL: %m");
1953
1954 local = l;
1955 }
1956
1957 if (isempty(local) || streq(local, "-"))
1958 local = NULL;
1959
1960 if (local) {
1961 r = raw_strip_suffixes(local, &ll);
1962 if (r < 0)
1963 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1964
1965 local = ll;
1966
1967 if (!machine_name_is_valid(local)) {
1968 log_error("Local name %s is not a suitable machine name.", local);
1969 return -EINVAL;
1970 }
1971 }
1972
1973 r = sd_bus_message_new_method_call(
1974 bus,
1975 &m,
1976 "org.freedesktop.import1",
1977 "/org/freedesktop/import1",
1978 "org.freedesktop.import1.Manager",
1979 "PullRaw");
1980 if (r < 0)
1981 return bus_log_create_error(r);
1982
1983 r = sd_bus_message_append(
1984 m,
1985 "sssb",
1986 remote,
1987 local,
1988 import_verify_to_string(arg_verify),
1989 arg_force);
1990 if (r < 0)
1991 return bus_log_create_error(r);
1992
1993 return pull_image_common(bus, m);
1994 }
1995
1996 static int pull_dkr(int argc, char *argv[], void *userdata) {
1997 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1998 _cleanup_free_ char *l = NULL, *ll = NULL;
1999 const char *local, *remote, *tag;
2000 sd_bus *bus = userdata;
2001 int r;
2002
2003 if (arg_verify != IMPORT_VERIFY_NO) {
2004 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2005 return -EINVAL;
2006 }
2007
2008 remote = argv[1];
2009 tag = strchr(remote, ':');
2010 if (tag) {
2011 remote = strndupa(remote, tag - remote);
2012 tag++;
2013 }
2014
2015 if (!dkr_name_is_valid(remote)) {
2016 log_error("DKR name '%s' is invalid.", remote);
2017 return -EINVAL;
2018 }
2019 if (tag && !dkr_tag_is_valid(tag)) {
2020 log_error("DKR tag '%s' is invalid.", remote);
2021 return -EINVAL;
2022 }
2023
2024 if (argc >= 3)
2025 local = argv[2];
2026 else {
2027 local = strchr(remote, '/');
2028 if (local)
2029 local++;
2030 else
2031 local = remote;
2032 }
2033
2034 if (isempty(local) || streq(local, "-"))
2035 local = NULL;
2036
2037 if (local) {
2038 if (!machine_name_is_valid(local)) {
2039 log_error("Local name %s is not a suitable machine name.", local);
2040 return -EINVAL;
2041 }
2042 }
2043
2044 r = sd_bus_message_new_method_call(
2045 bus,
2046 &m,
2047 "org.freedesktop.import1",
2048 "/org/freedesktop/import1",
2049 "org.freedesktop.import1.Manager",
2050 "PullDkr");
2051 if (r < 0)
2052 return bus_log_create_error(r);
2053
2054 r = sd_bus_message_append(
2055 m,
2056 "sssssb",
2057 arg_dkr_index_url,
2058 remote,
2059 tag,
2060 local,
2061 import_verify_to_string(arg_verify),
2062 arg_force);
2063 if (r < 0)
2064 return bus_log_create_error(r);
2065
2066 return pull_image_common(bus, m);
2067 }
2068
2069 typedef struct TransferInfo {
2070 uint32_t id;
2071 const char *type;
2072 const char *remote;
2073 const char *local;
2074 double progress;
2075 } TransferInfo;
2076
2077 static int compare_transfer_info(const void *a, const void *b) {
2078 const TransferInfo *x = a, *y = b;
2079
2080 return strcmp(x->local, y->local);
2081 }
2082
2083 static int list_transfers(int argc, char *argv[], void *userdata) {
2084 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
2085 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
2086 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2087 _cleanup_free_ TransferInfo *transfers = NULL;
2088 size_t n_transfers = 0, n_allocated = 0, j;
2089 const char *type, *remote, *local, *object;
2090 sd_bus *bus = userdata;
2091 uint32_t id, max_id = 0;
2092 double progress;
2093 int r;
2094
2095 pager_open_if_enabled();
2096
2097 r = sd_bus_call_method(
2098 bus,
2099 "org.freedesktop.import1",
2100 "/org/freedesktop/import1",
2101 "org.freedesktop.import1.Manager",
2102 "ListTransfers",
2103 &error,
2104 &reply,
2105 NULL);
2106 if (r < 0) {
2107 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2108 return r;
2109 }
2110
2111 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
2112 if (r < 0)
2113 return bus_log_parse_error(r);
2114
2115 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
2116 size_t l;
2117
2118 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2119 return log_oom();
2120
2121 transfers[n_transfers].id = id;
2122 transfers[n_transfers].type = type;
2123 transfers[n_transfers].remote = remote;
2124 transfers[n_transfers].local = local;
2125 transfers[n_transfers].progress = progress;
2126
2127 l = strlen(type);
2128 if (l > max_type)
2129 max_type = l;
2130
2131 l = strlen(remote);
2132 if (l > max_remote)
2133 max_remote = l;
2134
2135 l = strlen(local);
2136 if (l > max_local)
2137 max_local = l;
2138
2139 if (id > max_id)
2140 max_id = id;
2141
2142 n_transfers ++;
2143 }
2144 if (r < 0)
2145 return bus_log_parse_error(r);
2146
2147 r = sd_bus_message_exit_container(reply);
2148 if (r < 0)
2149 return bus_log_parse_error(r);
2150
2151 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2152
2153 if (arg_legend)
2154 printf("%-*s %-*s %-*s %-*s %-*s\n",
2155 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
2156 (int) 7, "PERCENT",
2157 (int) max_type, "TYPE",
2158 (int) max_local, "LOCAL",
2159 (int) max_remote, "REMOTE");
2160
2161 for (j = 0; j < n_transfers; j++)
2162 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
2163 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
2164 (int) 6, (unsigned) (transfers[j].progress * 100),
2165 (int) max_type, transfers[j].type,
2166 (int) max_local, transfers[j].local,
2167 (int) max_remote, transfers[j].remote);
2168
2169 if (arg_legend)
2170 printf("\n%zu transfers listed.\n", n_transfers);
2171
2172 return 0;
2173 }
2174
2175 static int cancel_transfer(int argc, char *argv[], void *userdata) {
2176 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2177 sd_bus *bus = userdata;
2178 int r, i;
2179
2180 assert(bus);
2181
2182 polkit_agent_open_if_enabled();
2183
2184 for (i = 1; i < argc; i++) {
2185 uint32_t id;
2186
2187 r = safe_atou32(argv[i], &id);
2188 if (r < 0)
2189 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2190
2191 r = sd_bus_call_method(
2192 bus,
2193 "org.freedesktop.import1",
2194 "/org/freedesktop/import1",
2195 "org.freedesktop.import1.Manager",
2196 "CancelTransfer",
2197 &error,
2198 NULL,
2199 "u", id);
2200 if (r < 0) {
2201 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2202 return r;
2203 }
2204 }
2205
2206 return 0;
2207 }
2208
2209 static int help(int argc, char *argv[], void *userdata) {
2210
2211 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2212 "Send control commands to or query the virtual machine and container\n"
2213 "registration manager.\n\n"
2214 " -h --help Show this help\n"
2215 " --version Show package version\n"
2216 " --no-pager Do not pipe output into a pager\n"
2217 " --no-legend Do not show the headers and footers\n"
2218 " --no-ask-password Do not ask for system passwords\n"
2219 " -H --host=[USER@]HOST Operate on remote host\n"
2220 " -M --machine=CONTAINER Operate on local container\n"
2221 " -p --property=NAME Show only properties by this name\n"
2222 " -q --quiet Suppress output\n"
2223 " -a --all Show all properties, including empty ones\n"
2224 " -l --full Do not ellipsize output\n"
2225 " --kill-who=WHO Who to send signal to\n"
2226 " -s --signal=SIGNAL Which signal to send\n"
2227 " --read-only Create read-only bind mount\n"
2228 " --mkdir Create directory before bind mounting, if missing\n"
2229 " -n --lines=INTEGER Number of journal entries to show\n"
2230 " -o --output=STRING Change journal output mode (short,\n"
2231 " short-monotonic, verbose, export, json,\n"
2232 " json-pretty, json-sse, cat)\n"
2233 " --verify=MODE Verification mode for downloaded images (no,\n"
2234 " checksum, signature)\n"
2235 " --force Download image even if already exists\n"
2236 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2237 " downloads\n\n"
2238 "Machine Commands:\n"
2239 " list List running VMs and containers\n"
2240 " status NAME... Show VM/container details\n"
2241 " show NAME... Show properties of one or more VMs/containers\n"
2242 " start NAME... Start container as a service\n"
2243 " login NAME Get a login prompt on a container\n"
2244 " enable NAME... Enable automatic container start at boot\n"
2245 " disable NAME... Disable automatic container start at boot\n"
2246 " poweroff NAME... Power off one or more containers\n"
2247 " reboot NAME... Reboot one or more containers\n"
2248 " terminate NAME... Terminate one or more VMs/containers\n"
2249 " kill NAME... Send signal to processes of a VM/container\n"
2250 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2251 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2252 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2253 "Image Commands:\n"
2254 " list-images Show available container and VM images\n"
2255 " image-status NAME... Show image details\n"
2256 " show-image NAME... Show properties of image\n"
2257 " clone NAME NAME Clone an image\n"
2258 " rename NAME NAME Rename an image\n"
2259 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2260 " remove NAME... Remove an image\n\n"
2261 "Image Transfer Commands:\n"
2262 " pull-tar URL [NAME] Download a TAR container image\n"
2263 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2264 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2265 " list-transfers Show list of downloads in progress\n"
2266 " cancel-transfer Cancel a download\n"
2267 , program_invocation_short_name);
2268
2269 return 0;
2270 }
2271
2272 static int parse_argv(int argc, char *argv[]) {
2273
2274 enum {
2275 ARG_VERSION = 0x100,
2276 ARG_NO_PAGER,
2277 ARG_NO_LEGEND,
2278 ARG_KILL_WHO,
2279 ARG_READ_ONLY,
2280 ARG_MKDIR,
2281 ARG_NO_ASK_PASSWORD,
2282 ARG_VERIFY,
2283 ARG_FORCE,
2284 ARG_DKR_INDEX_URL,
2285 };
2286
2287 static const struct option options[] = {
2288 { "help", no_argument, NULL, 'h' },
2289 { "version", no_argument, NULL, ARG_VERSION },
2290 { "property", required_argument, NULL, 'p' },
2291 { "all", no_argument, NULL, 'a' },
2292 { "full", no_argument, NULL, 'l' },
2293 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2294 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2295 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2296 { "signal", required_argument, NULL, 's' },
2297 { "host", required_argument, NULL, 'H' },
2298 { "machine", required_argument, NULL, 'M' },
2299 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2300 { "mkdir", no_argument, NULL, ARG_MKDIR },
2301 { "quiet", no_argument, NULL, 'q' },
2302 { "lines", required_argument, NULL, 'n' },
2303 { "output", required_argument, NULL, 'o' },
2304 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2305 { "verify", required_argument, NULL, ARG_VERIFY },
2306 { "force", no_argument, NULL, ARG_FORCE },
2307 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
2308 {}
2309 };
2310
2311 int c, r;
2312
2313 assert(argc >= 0);
2314 assert(argv);
2315
2316 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2317
2318 switch (c) {
2319
2320 case 'h':
2321 return help(0, NULL, NULL);
2322
2323 case ARG_VERSION:
2324 puts(PACKAGE_STRING);
2325 puts(SYSTEMD_FEATURES);
2326 return 0;
2327
2328 case 'p':
2329 r = strv_extend(&arg_property, optarg);
2330 if (r < 0)
2331 return log_oom();
2332
2333 /* If the user asked for a particular
2334 * property, show it to him, even if it is
2335 * empty. */
2336 arg_all = true;
2337 break;
2338
2339 case 'a':
2340 arg_all = true;
2341 break;
2342
2343 case 'l':
2344 arg_full = true;
2345 break;
2346
2347 case 'n':
2348 if (safe_atou(optarg, &arg_lines) < 0) {
2349 log_error("Failed to parse lines '%s'", optarg);
2350 return -EINVAL;
2351 }
2352 break;
2353
2354 case 'o':
2355 arg_output = output_mode_from_string(optarg);
2356 if (arg_output < 0) {
2357 log_error("Unknown output '%s'.", optarg);
2358 return -EINVAL;
2359 }
2360 break;
2361
2362 case ARG_NO_PAGER:
2363 arg_no_pager = true;
2364 break;
2365
2366 case ARG_NO_LEGEND:
2367 arg_legend = false;
2368 break;
2369
2370 case ARG_KILL_WHO:
2371 arg_kill_who = optarg;
2372 break;
2373
2374 case 's':
2375 arg_signal = signal_from_string_try_harder(optarg);
2376 if (arg_signal < 0) {
2377 log_error("Failed to parse signal string %s.", optarg);
2378 return -EINVAL;
2379 }
2380 break;
2381
2382 case ARG_NO_ASK_PASSWORD:
2383 arg_ask_password = false;
2384 break;
2385
2386 case 'H':
2387 arg_transport = BUS_TRANSPORT_REMOTE;
2388 arg_host = optarg;
2389 break;
2390
2391 case 'M':
2392 arg_transport = BUS_TRANSPORT_MACHINE;
2393 arg_host = optarg;
2394 break;
2395
2396 case ARG_READ_ONLY:
2397 arg_read_only = true;
2398 break;
2399
2400 case ARG_MKDIR:
2401 arg_mkdir = true;
2402 break;
2403
2404 case 'q':
2405 arg_quiet = true;
2406 break;
2407
2408 case ARG_VERIFY:
2409 arg_verify = import_verify_from_string(optarg);
2410 if (arg_verify < 0) {
2411 log_error("Failed to parse --verify= setting: %s", optarg);
2412 return -EINVAL;
2413 }
2414 break;
2415
2416 case ARG_FORCE:
2417 arg_force = true;
2418 break;
2419
2420 case ARG_DKR_INDEX_URL:
2421 if (!http_url_is_valid(optarg)) {
2422 log_error("Index URL is invalid: %s", optarg);
2423 return -EINVAL;
2424 }
2425
2426 arg_dkr_index_url = optarg;
2427 break;
2428
2429 case '?':
2430 return -EINVAL;
2431
2432 default:
2433 assert_not_reached("Unhandled option");
2434 }
2435
2436 return 1;
2437 }
2438
2439 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2440
2441 static const Verb verbs[] = {
2442 { "help", VERB_ANY, VERB_ANY, 0, help },
2443 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2444 { "list-images", VERB_ANY, 1, 0, list_images },
2445 { "status", 2, VERB_ANY, 0, show_machine },
2446 { "image-status", 2, VERB_ANY, 0, show_image },
2447 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2448 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2449 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2450 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2451 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2452 { "kill", 2, VERB_ANY, 0, kill_machine },
2453 { "login", 2, 2, 0, login_machine },
2454 { "bind", 3, 4, 0, bind_mount },
2455 { "copy-to", 3, 4, 0, copy_files },
2456 { "copy-from", 3, 4, 0, copy_files },
2457 { "remove", 2, VERB_ANY, 0, remove_image },
2458 { "rename", 3, 3, 0, rename_image },
2459 { "clone", 3, 3, 0, clone_image },
2460 { "read-only", 2, 3, 0, read_only_image },
2461 { "start", 2, VERB_ANY, 0, start_machine },
2462 { "enable", 2, VERB_ANY, 0, enable_machine },
2463 { "disable", 2, VERB_ANY, 0, enable_machine },
2464 { "pull-tar", 2, 3, 0, pull_tar },
2465 { "pull-raw", 2, 3, 0, pull_raw },
2466 { "pull-dkr", 2, 3, 0, pull_dkr },
2467 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2468 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
2469 {}
2470 };
2471
2472 return dispatch_verb(argc, argv, verbs, bus);
2473 }
2474
2475 int main(int argc, char*argv[]) {
2476 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
2477 int r;
2478
2479 setlocale(LC_ALL, "");
2480 log_parse_environment();
2481 log_open();
2482
2483 r = parse_argv(argc, argv);
2484 if (r <= 0)
2485 goto finish;
2486
2487 r = bus_open_transport(arg_transport, arg_host, false, &bus);
2488 if (r < 0) {
2489 log_error_errno(r, "Failed to create bus connection: %m");
2490 goto finish;
2491 }
2492
2493 r = machinectl_main(argc, argv, bus);
2494
2495 finish:
2496 pager_close();
2497 polkit_agent_close();
2498
2499 strv_free(arg_property);
2500
2501 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2502 }