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