]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machinectl.c
machinectl/machined: implement "rename", "clone", "read-only" verbs for machine images
[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
36 #include "sd-bus.h"
37 #include "log.h"
38 #include "util.h"
39 #include "macro.h"
40 #include "pager.h"
41 #include "bus-util.h"
42 #include "bus-error.h"
43 #include "build.h"
44 #include "strv.h"
45 #include "unit-name.h"
46 #include "cgroup-show.h"
47 #include "cgroup-util.h"
48 #include "ptyfwd.h"
49 #include "event-util.h"
50 #include "path-util.h"
51 #include "mkdir.h"
52 #include "copy.h"
53 #include "verbs.h"
54
55 static char **arg_property = NULL;
56 static bool arg_all = false;
57 static bool arg_full = false;
58 static bool arg_no_pager = false;
59 static bool arg_legend = true;
60 static const char *arg_kill_who = NULL;
61 static int arg_signal = SIGTERM;
62 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
63 static char *arg_host = NULL;
64 static bool arg_read_only = false;
65 static bool arg_mkdir = false;
66
67 static void pager_open_if_enabled(void) {
68
69 /* Cache result before we open the pager */
70 if (arg_no_pager)
71 return;
72
73 pager_open(false);
74 }
75
76 static int list_machines(int argc, char *argv[], void *userdata) {
77
78 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
79 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
80 const char *name, *class, *service, *object;
81 sd_bus *bus = userdata;
82 unsigned k = 0;
83 int r;
84
85 assert(bus);
86
87 pager_open_if_enabled();
88
89 r = sd_bus_call_method(
90 bus,
91 "org.freedesktop.machine1",
92 "/org/freedesktop/machine1",
93 "org.freedesktop.machine1.Manager",
94 "ListMachines",
95 &error,
96 &reply,
97 "");
98 if (r < 0) {
99 log_error("Could not get machines: %s", bus_error_message(&error, -r));
100 return r;
101 }
102
103 if (arg_legend)
104 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
105
106 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
107 if (r < 0)
108 return bus_log_parse_error(r);
109
110 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
111 printf("%-32s %-9s %-16s\n", name, class, service);
112
113 k++;
114 }
115 if (r < 0)
116 return bus_log_parse_error(r);
117
118 r = sd_bus_message_exit_container(reply);
119 if (r < 0)
120 return bus_log_parse_error(r);
121
122 if (arg_legend)
123 printf("\n%u machines listed.\n", k);
124
125 return 0;
126 }
127
128 typedef struct ImageInfo {
129 const char *name;
130 const char *type;
131 bool read_only;
132 usec_t crtime;
133 usec_t mtime;
134 } ImageInfo;
135
136 static int compare_image_info(const void *a, const void *b) {
137 const ImageInfo *x = a, *y = b;
138
139 return strcmp(x->name, y->name);
140 }
141
142 static int list_images(int argc, char *argv[], void *userdata) {
143
144 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
145 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
146 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
147 _cleanup_free_ ImageInfo *images = NULL;
148 size_t n_images = 0, n_allocated = 0, j;
149 const char *name, *type, *object;
150 sd_bus *bus = userdata;
151 uint64_t crtime, mtime;
152 int read_only, r;
153
154 assert(bus);
155
156 pager_open_if_enabled();
157
158 r = sd_bus_call_method(
159 bus,
160 "org.freedesktop.machine1",
161 "/org/freedesktop/machine1",
162 "org.freedesktop.machine1.Manager",
163 "ListImages",
164 &error,
165 &reply,
166 "");
167 if (r < 0) {
168 log_error("Could not get images: %s", bus_error_message(&error, -r));
169 return r;
170 }
171
172 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbtto)");
173 if (r < 0)
174 return bus_log_parse_error(r);
175
176 while ((r = sd_bus_message_read(reply, "(ssbtto)", &name, &type, &read_only, &crtime, &mtime, &object)) > 0) {
177 char buf[FORMAT_TIMESTAMP_MAX];
178 size_t l;
179
180 if (name[0] == '.' && !arg_all)
181 continue;
182
183 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
184 return log_oom();
185
186 images[n_images].name = name;
187 images[n_images].type = type;
188 images[n_images].read_only = read_only;
189 images[n_images].crtime = crtime;
190 images[n_images].mtime = mtime;
191
192 l = strlen(name);
193 if (l > max_name)
194 max_name = l;
195
196 l = strlen(type);
197 if (l > max_type)
198 max_type = l;
199
200 if (crtime != 0) {
201 l = strlen(format_timestamp(buf, sizeof(buf), crtime));
202 if (l > max_crtime)
203 max_crtime = l;
204 }
205
206 if (mtime != 0) {
207 l = strlen(format_timestamp(buf, sizeof(buf), mtime));
208 if (l > max_mtime)
209 max_mtime = l;
210 }
211
212 n_images++;
213 }
214 if (r < 0)
215 return bus_log_parse_error(r);
216
217 r = sd_bus_message_exit_container(reply);
218 if (r < 0)
219 return bus_log_parse_error(r);
220
221 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
222
223 if (arg_legend)
224 printf("%-*s %-*s %-3s %-*s %-*s\n",
225 (int) max_name, "NAME",
226 (int) max_type, "TYPE",
227 "RO",
228 (int) max_crtime, "CREATED",
229 (int) max_mtime, "MODIFIED");
230
231 for (j = 0; j < n_images; j++) {
232 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX];
233
234 printf("%-*s %-*s %s%-3s%s %-*s %-*s\n",
235 (int) max_name, images[j].name,
236 (int) max_type, images[j].type,
237 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
238 (int) max_crtime, images[j].crtime != 0 ? format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime) : "-",
239 (int) max_mtime, images[j].mtime != 0 ? format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime) : "-");
240 }
241
242 if (r < 0)
243 return bus_log_parse_error(r);
244
245
246 if (arg_legend)
247 printf("\n%zu images listed.\n", n_images);
248
249 return 0;
250 }
251
252 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
253 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
254 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
255 _cleanup_free_ char *path = NULL;
256 const char *cgroup;
257 int r, output_flags;
258 unsigned c;
259
260 assert(bus);
261 assert(unit);
262
263 if (arg_transport == BUS_TRANSPORT_REMOTE)
264 return 0;
265
266 path = unit_dbus_path_from_name(unit);
267 if (!path)
268 return log_oom();
269
270 r = sd_bus_get_property(
271 bus,
272 "org.freedesktop.systemd1",
273 path,
274 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
275 "ControlGroup",
276 &error,
277 &reply,
278 "s");
279 if (r < 0) {
280 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
281 return r;
282 }
283
284 r = sd_bus_message_read(reply, "s", &cgroup);
285 if (r < 0)
286 return bus_log_parse_error(r);
287
288 if (isempty(cgroup))
289 return 0;
290
291 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
292 return 0;
293
294 output_flags =
295 arg_all * OUTPUT_SHOW_ALL |
296 arg_full * OUTPUT_FULL_WIDTH;
297
298 c = columns();
299 if (c > 18)
300 c -= 18;
301 else
302 c = 0;
303
304 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
305 return 0;
306 }
307
308 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
309 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
310 int r;
311
312 assert(bus);
313 assert(name);
314 assert(prefix);
315 assert(prefix2);
316
317 r = sd_bus_call_method(bus,
318 "org.freedesktop.machine1",
319 "/org/freedesktop/machine1",
320 "org.freedesktop.machine1.Manager",
321 "GetMachineAddresses",
322 NULL,
323 &reply,
324 "s", name);
325 if (r < 0)
326 return r;
327
328 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
329 if (r < 0)
330 return bus_log_parse_error(r);
331
332 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
333 int family;
334 const void *a;
335 size_t sz;
336 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
337
338 r = sd_bus_message_read(reply, "i", &family);
339 if (r < 0)
340 return bus_log_parse_error(r);
341
342 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
343 if (r < 0)
344 return bus_log_parse_error(r);
345
346 fputs(prefix, stdout);
347 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
348 if (family == AF_INET6 && ifi > 0)
349 printf("%%%i", ifi);
350 fputc('\n', stdout);
351
352 r = sd_bus_message_exit_container(reply);
353 if (r < 0)
354 return bus_log_parse_error(r);
355
356 if (prefix != prefix2)
357 prefix = prefix2;
358 }
359 if (r < 0)
360 return bus_log_parse_error(r);
361
362 r = sd_bus_message_exit_container(reply);
363 if (r < 0)
364 return bus_log_parse_error(r);
365
366 return 0;
367 }
368
369 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
370 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
371 const char *k, *v, *pretty = NULL;
372 int r;
373
374 assert(bus);
375 assert(name);
376 assert(prefix);
377
378 r = sd_bus_call_method(bus,
379 "org.freedesktop.machine1",
380 "/org/freedesktop/machine1",
381 "org.freedesktop.machine1.Manager",
382 "GetMachineOSRelease",
383 NULL,
384 &reply,
385 "s", name);
386 if (r < 0)
387 return r;
388
389 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
390 if (r < 0)
391 return bus_log_parse_error(r);
392
393 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
394 if (streq(k, "PRETTY_NAME"))
395 pretty = v;
396
397 }
398 if (r < 0)
399 return bus_log_parse_error(r);
400
401 r = sd_bus_message_exit_container(reply);
402 if (r < 0)
403 return bus_log_parse_error(r);
404
405 if (pretty)
406 printf("%s%s\n", prefix, pretty);
407
408 return 0;
409 }
410
411 typedef struct MachineStatusInfo {
412 char *name;
413 sd_id128_t id;
414 char *class;
415 char *service;
416 char *unit;
417 char *root_directory;
418 pid_t leader;
419 usec_t timestamp;
420 int *netif;
421 unsigned n_netif;
422 } MachineStatusInfo;
423
424 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
425 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
426 char since2[FORMAT_TIMESTAMP_MAX], *s2;
427 int ifi = -1;
428
429 assert(bus);
430 assert(i);
431
432 fputs(strna(i->name), stdout);
433
434 if (!sd_id128_equal(i->id, SD_ID128_NULL))
435 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
436 else
437 putchar('\n');
438
439 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
440 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
441
442 if (s1)
443 printf("\t Since: %s; %s\n", s2, s1);
444 else if (s2)
445 printf("\t Since: %s\n", s2);
446
447 if (i->leader > 0) {
448 _cleanup_free_ char *t = NULL;
449
450 printf("\t Leader: %u", (unsigned) i->leader);
451
452 get_process_comm(i->leader, &t);
453 if (t)
454 printf(" (%s)", t);
455
456 putchar('\n');
457 }
458
459 if (i->service) {
460 printf("\t Service: %s", i->service);
461
462 if (i->class)
463 printf("; class %s", i->class);
464
465 putchar('\n');
466 } else if (i->class)
467 printf("\t Class: %s\n", i->class);
468
469 if (i->root_directory)
470 printf("\t Root: %s\n", i->root_directory);
471
472 if (i->n_netif > 0) {
473 unsigned c;
474
475 fputs("\t Iface:", stdout);
476
477 for (c = 0; c < i->n_netif; c++) {
478 char name[IF_NAMESIZE+1] = "";
479
480 if (if_indextoname(i->netif[c], name)) {
481 fputc(' ', stdout);
482 fputs(name, stdout);
483
484 if (ifi < 0)
485 ifi = i->netif[c];
486 else
487 ifi = 0;
488 } else
489 printf(" %i", i->netif[c]);
490 }
491
492 fputc('\n', stdout);
493 }
494
495 print_addresses(bus, i->name, ifi,
496 "\t Address: ",
497 "\t ");
498
499 print_os_release(bus, i->name, "\t OS: ");
500
501 if (i->unit) {
502 printf("\t Unit: %s\n", i->unit);
503 show_unit_cgroup(bus, i->unit, i->leader);
504 }
505 }
506
507 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
508 MachineStatusInfo *i = userdata;
509 size_t l;
510 const void *v;
511 int r;
512
513 assert_cc(sizeof(int32_t) == sizeof(int));
514 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
515 if (r < 0)
516 return r;
517 if (r == 0)
518 return -EBADMSG;
519
520 i->n_netif = l / sizeof(int32_t);
521 i->netif = memdup(v, l);
522 if (!i->netif)
523 return -ENOMEM;
524
525 return 0;
526 }
527
528 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
529
530 static const struct bus_properties_map map[] = {
531 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
532 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
533 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
534 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
535 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
536 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
537 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
538 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
539 { "NetworkInterfaces", "ai", map_netif, 0 },
540 {}
541 };
542
543 MachineStatusInfo info = {};
544 int r;
545
546 assert(verb);
547 assert(bus);
548 assert(path);
549 assert(new_line);
550
551 r = bus_map_all_properties(bus,
552 "org.freedesktop.machine1",
553 path,
554 map,
555 &info);
556 if (r < 0)
557 return log_error_errno(r, "Could not get properties: %m");
558
559 if (*new_line)
560 printf("\n");
561 *new_line = true;
562
563 print_machine_status_info(bus, &info);
564
565 free(info.name);
566 free(info.class);
567 free(info.service);
568 free(info.unit);
569 free(info.root_directory);
570 free(info.netif);
571
572 return r;
573 }
574
575 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
576 int r;
577
578 assert(bus);
579 assert(path);
580 assert(new_line);
581
582 if (*new_line)
583 printf("\n");
584
585 *new_line = true;
586
587 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
588 if (r < 0)
589 log_error_errno(r, "Could not get properties: %m");
590
591 return r;
592 }
593
594 static int show_machine(int argc, char *argv[], void *userdata) {
595
596 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
597 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
598 bool properties, new_line = false;
599 sd_bus *bus = userdata;
600 int r = 0, i;
601
602 assert(bus);
603
604 properties = !strstr(argv[0], "status");
605
606 pager_open_if_enabled();
607
608 if (properties && argc <= 1) {
609
610 /* If no argument is specified, inspect the manager
611 * itself */
612 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
613 if (r < 0)
614 return r;
615 }
616
617 for (i = 1; i < argc; i++) {
618 const char *path = NULL;
619
620 r = sd_bus_call_method(
621 bus,
622 "org.freedesktop.machine1",
623 "/org/freedesktop/machine1",
624 "org.freedesktop.machine1.Manager",
625 "GetMachine",
626 &error,
627 &reply,
628 "s", argv[i]);
629 if (r < 0) {
630 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
631 return r;
632 }
633
634 r = sd_bus_message_read(reply, "o", &path);
635 if (r < 0)
636 return bus_log_parse_error(r);
637
638 if (properties)
639 r = show_machine_properties(bus, path, &new_line);
640 else
641 r = show_machine_info(argv[0], bus, path, &new_line);
642 }
643
644 return r;
645 }
646
647 typedef struct ImageStatusInfo {
648 char *name;
649 char *path;
650 char *type;
651 int read_only;
652 usec_t crtime;
653 usec_t mtime;
654 } ImageStatusInfo;
655
656 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
657 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
658 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
659
660 assert(bus);
661 assert(i);
662
663 if (i->name) {
664 fputs(i->name, stdout);
665 putchar('\n');
666 }
667
668 if (i->path)
669 printf("\t Type: %s\n", i->type);
670
671 if (i->path)
672 printf("\t Path: %s\n", i->path);
673
674 printf("\t RO: %s%s%s\n",
675 i->read_only ? ansi_highlight_red() : "",
676 i->read_only ? "read-only" : "writable",
677 i->read_only ? ansi_highlight_off() : "");
678
679 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
680 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
681 if (s1)
682 printf("\t Created: %s; %s\n", s2, s1);
683 else if (s2)
684 printf("\t Created: %s\n", s2);
685
686 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
687 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
688 if (s1)
689 printf("\tModified: %s; %s\n", s2, s1);
690 else if (s2)
691 printf("\tModified: %s\n", s2);
692 }
693
694 static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
695
696 static const struct bus_properties_map map[] = {
697 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
698 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
699 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
700 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
701 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
702 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
703 {}
704 };
705
706 ImageStatusInfo info = {};
707 int r;
708
709 assert(verb);
710 assert(bus);
711 assert(path);
712 assert(new_line);
713
714 r = bus_map_all_properties(bus,
715 "org.freedesktop.machine1",
716 path,
717 map,
718 &info);
719 if (r < 0)
720 return log_error_errno(r, "Could not get properties: %m");
721
722 if (*new_line)
723 printf("\n");
724 *new_line = true;
725
726 print_image_status_info(bus, &info);
727
728 free(info.name);
729 free(info.path);
730 free(info.type);
731
732 return r;
733 }
734
735 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
736 int r;
737
738 assert(bus);
739 assert(path);
740 assert(new_line);
741
742 if (*new_line)
743 printf("\n");
744
745 *new_line = true;
746
747 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
748 if (r < 0)
749 log_error_errno(r, "Could not get properties: %m");
750
751 return r;
752 }
753
754 static int show_image(int argc, char *argv[], void *userdata) {
755
756 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
757 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
758 bool properties, new_line = false;
759 sd_bus *bus = userdata;
760 int r = 0, i;
761
762 assert(bus);
763
764 properties = !strstr(argv[0], "status");
765
766 pager_open_if_enabled();
767
768 if (properties && argc <= 1) {
769
770 /* If no argument is specified, inspect the manager
771 * itself */
772 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
773 if (r < 0)
774 return r;
775 }
776
777 for (i = 1; i < argc; i++) {
778 const char *path = NULL;
779
780 r = sd_bus_call_method(
781 bus,
782 "org.freedesktop.machine1",
783 "/org/freedesktop/machine1",
784 "org.freedesktop.machine1.Manager",
785 "GetImage",
786 &error,
787 &reply,
788 "s", argv[i]);
789 if (r < 0) {
790 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
791 return r;
792 }
793
794 r = sd_bus_message_read(reply, "o", &path);
795 if (r < 0)
796 return bus_log_parse_error(r);
797
798 if (properties)
799 r = show_image_properties(bus, path, &new_line);
800 else
801 r = show_image_info(argv[0], bus, path, &new_line);
802 }
803
804 return r;
805 }
806
807 static int kill_machine(int argc, char *argv[], void *userdata) {
808 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
809 sd_bus *bus = userdata;
810 int i;
811
812 assert(bus);
813
814 if (!arg_kill_who)
815 arg_kill_who = "all";
816
817 for (i = 1; i < argc; i++) {
818 int r;
819
820 r = sd_bus_call_method(
821 bus,
822 "org.freedesktop.machine1",
823 "/org/freedesktop/machine1",
824 "org.freedesktop.machine1.Manager",
825 "KillMachine",
826 &error,
827 NULL,
828 "ssi", argv[i], arg_kill_who, arg_signal);
829 if (r < 0) {
830 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
831 return r;
832 }
833 }
834
835 return 0;
836 }
837
838 static int reboot_machine(int argc, char *argv[], void *userdata) {
839 arg_kill_who = "leader";
840 arg_signal = SIGINT; /* sysvinit + systemd */
841
842 return kill_machine(argc, argv, userdata);
843 }
844
845 static int poweroff_machine(int argc, char *argv[], void *userdata) {
846 arg_kill_who = "leader";
847 arg_signal = SIGRTMIN+4; /* only systemd */
848
849 return kill_machine(argc, argv, userdata);
850 }
851
852 static int terminate_machine(int argc, char *argv[], void *userdata) {
853 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
854 sd_bus *bus = userdata;
855 int i;
856
857 assert(bus);
858
859 for (i = 1; i < argc; i++) {
860 int r;
861
862 r = sd_bus_call_method(
863 bus,
864 "org.freedesktop.machine1",
865 "/org/freedesktop/machine1",
866 "org.freedesktop.machine1.Manager",
867 "TerminateMachine",
868 &error,
869 NULL,
870 "s", argv[i]);
871 if (r < 0) {
872 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
873 return r;
874 }
875 }
876
877 return 0;
878 }
879
880 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
881 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
882 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
883 const char *object;
884 uint32_t leader;
885 int r;
886
887 assert(bus);
888 assert(name);
889 assert(ret);
890
891 r = sd_bus_call_method(
892 bus,
893 "org.freedesktop.machine1",
894 "/org/freedesktop/machine1",
895 "org.freedesktop.machine1.Manager",
896 "GetMachine",
897 &error,
898 &reply,
899 "s", name);
900 if (r < 0) {
901 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
902 return r;
903 }
904
905 r = sd_bus_message_read(reply, "o", &object);
906 if (r < 0)
907 return bus_log_parse_error(r);
908
909 r = sd_bus_get_property(
910 bus,
911 "org.freedesktop.machine1",
912 object,
913 "org.freedesktop.machine1.Machine",
914 "Leader",
915 &error,
916 &reply2,
917 "u");
918 if (r < 0)
919 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
920
921 r = sd_bus_message_read(reply2, "u", &leader);
922 if (r < 0)
923 return bus_log_parse_error(r);
924
925 *ret = leader;
926 return 0;
927 }
928
929 static int copy_files(int argc, char *argv[], void *userdata) {
930 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
931 _cleanup_close_ int hostfd = -1;
932 sd_bus *bus = userdata;
933 pid_t child, leader;
934 bool copy_from;
935 siginfo_t si;
936 int r;
937
938 assert(bus);
939
940 copy_from = streq(argv[0], "copy-from");
941 dest = argv[3] ?: argv[2];
942 host_path = strdupa(copy_from ? dest : argv[2]);
943 container_path = strdupa(copy_from ? argv[2] : dest);
944
945 if (!path_is_absolute(container_path)) {
946 log_error("Container path not absolute.");
947 return -EINVAL;
948 }
949
950 t = strdup(host_path);
951 host_basename = basename(t);
952 host_dirname = dirname(host_path);
953
954 t = strdup(container_path);
955 container_basename = basename(t);
956 container_dirname = dirname(container_path);
957
958 r = machine_get_leader(bus, argv[1], &leader);
959 if (r < 0)
960 return r;
961
962 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
963 if (r < 0)
964 return log_error_errno(errno, "Failed to open source directory: %m");
965
966 child = fork();
967 if (child < 0)
968 return log_error_errno(errno, "Failed to fork(): %m");
969
970 if (child == 0) {
971 int containerfd;
972 const char *q;
973 int mntfd;
974
975 q = procfs_file_alloca(leader, "ns/mnt");
976 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
977 if (mntfd < 0) {
978 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
979 _exit(EXIT_FAILURE);
980 }
981
982 if (setns(mntfd, CLONE_NEWNS) < 0) {
983 log_error_errno(errno, "Failed to join namespace of leader: %m");
984 _exit(EXIT_FAILURE);
985 }
986
987 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
988 if (containerfd < 0) {
989 log_error_errno(errno, "Failed top open destination directory: %m");
990 _exit(EXIT_FAILURE);
991 }
992
993 if (copy_from)
994 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
995 else
996 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
997 if (r < 0) {
998 log_error_errno(errno, "Failed to copy tree: %m");
999 _exit(EXIT_FAILURE);
1000 }
1001
1002 _exit(EXIT_SUCCESS);
1003 }
1004
1005 r = wait_for_terminate(child, &si);
1006 if (r < 0)
1007 return log_error_errno(r, "Failed to wait for client: %m");
1008 if (si.si_code != CLD_EXITED) {
1009 log_error("Client died abnormally.");
1010 return -EIO;
1011 }
1012 if (si.si_status != EXIT_SUCCESS)
1013 return -EIO;
1014
1015 return 0;
1016 }
1017
1018 static int bind_mount(int argc, char *argv[], void *userdata) {
1019 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
1020 sd_bus *bus = userdata;
1021 pid_t child, leader;
1022 const char *dest;
1023 siginfo_t si;
1024 bool mount_slave_created = false, mount_slave_mounted = false,
1025 mount_tmp_created = false, mount_tmp_mounted = false,
1026 mount_outside_created = false, mount_outside_mounted = false;
1027 int r;
1028
1029 assert(bus);
1030
1031 /* One day, when bind mounting /proc/self/fd/n works across
1032 * namespace boundaries we should rework this logic to make
1033 * use of it... */
1034
1035 dest = argv[3] ?: argv[2];
1036 if (!path_is_absolute(dest)) {
1037 log_error("Destination path not absolute.");
1038 return -EINVAL;
1039 }
1040
1041 p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
1042 if (access(p, F_OK) < 0) {
1043 log_error("Container does not allow propagation of mount points.");
1044 return -ENOTSUP;
1045 }
1046
1047 r = machine_get_leader(bus, argv[1], &leader);
1048 if (r < 0)
1049 return r;
1050
1051 /* Our goal is to install a new bind mount into the container,
1052 possibly read-only. This is irritatingly complex
1053 unfortunately, currently.
1054
1055 First, we start by creating a private playground in /tmp,
1056 that we can mount MS_SLAVE. (Which is necessary, since
1057 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
1058 mounts.) */
1059
1060 if (!mkdtemp(mount_slave))
1061 return log_error_errno(errno, "Failed to create playground: %m");
1062
1063 mount_slave_created = true;
1064
1065 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
1066 r = log_error_errno(errno, "Failed to make bind mount: %m");
1067 goto finish;
1068 }
1069
1070 mount_slave_mounted = true;
1071
1072 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
1073 r = log_error_errno(errno, "Failed to remount slave: %m");
1074 goto finish;
1075 }
1076
1077 /* Second, we mount the source directory to a directory inside
1078 of our MS_SLAVE playground. */
1079 mount_tmp = strappenda(mount_slave, "/mount");
1080 if (mkdir(mount_tmp, 0700) < 0) {
1081 r = log_error_errno(errno, "Failed to create temporary mount: %m");
1082 goto finish;
1083 }
1084
1085 mount_tmp_created = true;
1086
1087 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
1088 r = log_error_errno(errno, "Failed to overmount: %m");
1089 goto finish;
1090 }
1091
1092 mount_tmp_mounted = true;
1093
1094 /* Third, we remount the new bind mount read-only if requested. */
1095 if (arg_read_only)
1096 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
1097 r = log_error_errno(errno, "Failed to mark read-only: %m");
1098 goto finish;
1099 }
1100
1101 /* Fourth, we move the new bind mount into the propagation
1102 * directory. This way it will appear there read-only
1103 * right-away. */
1104
1105 mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
1106 if (!mkdtemp(mount_outside)) {
1107 r = log_error_errno(errno, "Cannot create propagation directory: %m");
1108 goto finish;
1109 }
1110
1111 mount_outside_created = true;
1112
1113 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
1114 r = log_error_errno(errno, "Failed to move: %m");
1115 goto finish;
1116 }
1117
1118 mount_outside_mounted = true;
1119 mount_tmp_mounted = false;
1120
1121 (void) rmdir(mount_tmp);
1122 mount_tmp_created = false;
1123
1124 (void) umount(mount_slave);
1125 mount_slave_mounted = false;
1126
1127 (void) rmdir(mount_slave);
1128 mount_slave_created = false;
1129
1130 child = fork();
1131 if (child < 0) {
1132 r = log_error_errno(errno, "Failed to fork(): %m");
1133 goto finish;
1134 }
1135
1136 if (child == 0) {
1137 const char *mount_inside;
1138 int mntfd;
1139 const char *q;
1140
1141 q = procfs_file_alloca(leader, "ns/mnt");
1142 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1143 if (mntfd < 0) {
1144 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1145 _exit(EXIT_FAILURE);
1146 }
1147
1148 if (setns(mntfd, CLONE_NEWNS) < 0) {
1149 log_error_errno(errno, "Failed to join namespace of leader: %m");
1150 _exit(EXIT_FAILURE);
1151 }
1152
1153 if (arg_mkdir)
1154 mkdir_p(dest, 0755);
1155
1156 /* Fifth, move the mount to the right place inside */
1157 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
1158 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
1159 log_error_errno(errno, "Failed to mount: %m");
1160 _exit(EXIT_FAILURE);
1161 }
1162
1163 _exit(EXIT_SUCCESS);
1164 }
1165
1166 r = wait_for_terminate(child, &si);
1167 if (r < 0) {
1168 log_error_errno(r, "Failed to wait for client: %m");
1169 goto finish;
1170 }
1171 if (si.si_code != CLD_EXITED) {
1172 log_error("Client died abnormally.");
1173 r = -EIO;
1174 goto finish;
1175 }
1176 if (si.si_status != EXIT_SUCCESS) {
1177 r = -EIO;
1178 goto finish;
1179 }
1180
1181 r = 0;
1182
1183 finish:
1184 if (mount_outside_mounted)
1185 umount(mount_outside);
1186 if (mount_outside_created)
1187 rmdir(mount_outside);
1188
1189 if (mount_tmp_mounted)
1190 umount(mount_tmp);
1191 if (mount_tmp_created)
1192 umount(mount_tmp);
1193
1194 if (mount_slave_mounted)
1195 umount(mount_slave);
1196 if (mount_slave_created)
1197 umount(mount_slave);
1198
1199 return r;
1200 }
1201
1202 static int login_machine(int argc, char *argv[], void *userdata) {
1203 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1204 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1205 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1206 _cleanup_event_unref_ sd_event *event = NULL;
1207 int master = -1, r, ret = 0;
1208 sd_bus *bus = userdata;
1209 const char *pty;
1210 sigset_t mask;
1211 char last_char = 0;
1212
1213 assert(bus);
1214
1215 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1216 arg_transport != BUS_TRANSPORT_MACHINE) {
1217 log_error("Login only supported on local machines.");
1218 return -ENOTSUP;
1219 }
1220
1221 r = sd_event_default(&event);
1222 if (r < 0)
1223 return log_error_errno(r, "Failed to get event loop: %m");
1224
1225 r = sd_bus_attach_event(bus, event, 0);
1226 if (r < 0)
1227 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1228
1229 r = sd_bus_message_new_method_call(bus,
1230 &m,
1231 "org.freedesktop.machine1",
1232 "/org/freedesktop/machine1",
1233 "org.freedesktop.machine1.Manager",
1234 "OpenMachineLogin");
1235 if (r < 0)
1236 return bus_log_create_error(r);
1237
1238 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1239 if (r < 0)
1240 return bus_log_create_error(r);
1241
1242 r = sd_bus_message_append(m, "s", argv[1]);
1243 if (r < 0)
1244 return bus_log_create_error(r);
1245
1246 r = sd_bus_call(bus, m, 0, &error, &reply);
1247 if (r < 0) {
1248 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1249 return r;
1250 }
1251
1252 r = sd_bus_message_read(reply, "hs", &master, &pty);
1253 if (r < 0)
1254 return bus_log_parse_error(r);
1255
1256 assert_se(sigemptyset(&mask) == 0);
1257 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1258 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1259
1260 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", argv[1]);
1261
1262 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1263 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1264
1265 r = pty_forward_new(event, master, true, &forward);
1266 if (r < 0)
1267 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1268
1269 r = sd_event_loop(event);
1270 if (r < 0)
1271 return log_error_errno(r, "Failed to run event loop: %m");
1272
1273 pty_forward_last_char(forward, &last_char);
1274
1275 forward = pty_forward_free(forward);
1276
1277 if (last_char != '\n')
1278 fputc('\n', stdout);
1279
1280 log_info("Connection to container %s terminated.", argv[1]);
1281
1282 sd_event_get_exit_code(event, &ret);
1283 return ret;
1284 }
1285
1286 static int remove_image(int argc, char *argv[], void *userdata) {
1287 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1288 sd_bus *bus = userdata;
1289 int r, i;
1290
1291 assert(bus);
1292
1293 for (i = 1; i < argc; i++) {
1294 r = sd_bus_call_method(
1295 bus,
1296 "org.freedesktop.machine1",
1297 "/org/freedesktop/machine1",
1298 "org.freedesktop.machine1.Manager",
1299 "RemoveImage",
1300 &error,
1301 NULL,
1302 "s", argv[i]);
1303 if (r < 0) {
1304 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1305 return r;
1306 }
1307 }
1308
1309 return 0;
1310 }
1311
1312 static int rename_image(int argc, char *argv[], void *userdata) {
1313 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1314 sd_bus *bus = userdata;
1315 int r;
1316
1317 r = sd_bus_call_method(
1318 bus,
1319 "org.freedesktop.machine1",
1320 "/org/freedesktop/machine1",
1321 "org.freedesktop.machine1.Manager",
1322 "RenameImage",
1323 &error,
1324 NULL,
1325 "ss", argv[1], argv[2]);
1326 if (r < 0) {
1327 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1328 return r;
1329 }
1330
1331 return 0;
1332 }
1333
1334 static int clone_image(int argc, char *argv[], void *userdata) {
1335 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1336 sd_bus *bus = userdata;
1337 int r;
1338
1339 r = sd_bus_call_method(
1340 bus,
1341 "org.freedesktop.machine1",
1342 "/org/freedesktop/machine1",
1343 "org.freedesktop.machine1.Manager",
1344 "CloneImage",
1345 &error,
1346 NULL,
1347 "ssb", argv[1], argv[2], arg_read_only);
1348 if (r < 0) {
1349 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1350 return r;
1351 }
1352
1353 return 0;
1354 }
1355
1356 static int read_only_image(int argc, char *argv[], void *userdata) {
1357 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1358 sd_bus *bus = userdata;
1359 int b = true, r;
1360
1361 if (argc > 2) {
1362 b = parse_boolean(argv[2]);
1363 if (b < 0) {
1364 log_error("Failed to parse boolean argument: %s", argv[2]);
1365 return -EINVAL;
1366 }
1367 }
1368
1369 r = sd_bus_call_method(
1370 bus,
1371 "org.freedesktop.machine1",
1372 "/org/freedesktop/machine1",
1373 "org.freedesktop.machine1.Manager",
1374 "MarkImageReadOnly",
1375 &error,
1376 NULL,
1377 "sb", argv[1], b);
1378 if (r < 0) {
1379 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1380 return r;
1381 }
1382
1383 return 0;
1384 }
1385
1386 static int help(int argc, char *argv[], void *userdata) {
1387
1388 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1389 "Send control commands to or query the virtual machine and container\n"
1390 "registration manager.\n\n"
1391 " -h --help Show this help\n"
1392 " --version Show package version\n"
1393 " --no-pager Do not pipe output into a pager\n"
1394 " --no-legend Do not show the headers and footers\n"
1395 " -H --host=[USER@]HOST Operate on remote host\n"
1396 " -M --machine=CONTAINER Operate on local container\n"
1397 " -p --property=NAME Show only properties by this name\n"
1398 " -a --all Show all properties, including empty ones\n"
1399 " -l --full Do not ellipsize output\n"
1400 " --kill-who=WHO Who to send signal to\n"
1401 " -s --signal=SIGNAL Which signal to send\n"
1402 " --read-only Create read-only bind mount\n"
1403 " --mkdir Create directory before bind mounting, if missing\n\n"
1404 "Machine Commands:\n"
1405 " list List running VMs and containers\n"
1406 " status NAME... Show VM/container details\n"
1407 " show NAME... Show properties of one or more VMs/containers\n"
1408 " login NAME Get a login prompt on a container\n"
1409 " poweroff NAME... Power off one or more containers\n"
1410 " reboot NAME... Reboot one or more containers\n"
1411 " terminate NAME... Terminate one or more VMs/containers\n"
1412 " kill NAME... Send signal to processes of a VM/container\n"
1413 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1414 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
1415 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
1416 "Image Commands:\n"
1417 " list-images Show available images\n"
1418 " image-status NAME... Show image details\n"
1419 " show-image NAME... Show properties of image\n"
1420 " clone NAME NAME Clone an image\n"
1421 " rename NAME NAME Rename an image\n"
1422 " read-only NAME [BOOL] Mark or unmark image read-only\n"
1423 " remove NAME... Remove an image\n",
1424 program_invocation_short_name);
1425
1426 return 0;
1427 }
1428
1429 static int parse_argv(int argc, char *argv[]) {
1430
1431 enum {
1432 ARG_VERSION = 0x100,
1433 ARG_NO_PAGER,
1434 ARG_NO_LEGEND,
1435 ARG_KILL_WHO,
1436 ARG_READ_ONLY,
1437 ARG_MKDIR,
1438 };
1439
1440 static const struct option options[] = {
1441 { "help", no_argument, NULL, 'h' },
1442 { "version", no_argument, NULL, ARG_VERSION },
1443 { "property", required_argument, NULL, 'p' },
1444 { "all", no_argument, NULL, 'a' },
1445 { "full", no_argument, NULL, 'l' },
1446 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1447 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1448 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1449 { "signal", required_argument, NULL, 's' },
1450 { "host", required_argument, NULL, 'H' },
1451 { "machine", required_argument, NULL, 'M' },
1452 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1453 { "mkdir", no_argument, NULL, ARG_MKDIR },
1454 {}
1455 };
1456
1457 int c, r;
1458
1459 assert(argc >= 0);
1460 assert(argv);
1461
1462 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1463
1464 switch (c) {
1465
1466 case 'h':
1467 return help(0, NULL, NULL);
1468
1469 case ARG_VERSION:
1470 puts(PACKAGE_STRING);
1471 puts(SYSTEMD_FEATURES);
1472 return 0;
1473
1474 case 'p':
1475 r = strv_extend(&arg_property, optarg);
1476 if (r < 0)
1477 return log_oom();
1478
1479 /* If the user asked for a particular
1480 * property, show it to him, even if it is
1481 * empty. */
1482 arg_all = true;
1483 break;
1484
1485 case 'a':
1486 arg_all = true;
1487 break;
1488
1489 case 'l':
1490 arg_full = true;
1491 break;
1492
1493 case ARG_NO_PAGER:
1494 arg_no_pager = true;
1495 break;
1496
1497 case ARG_NO_LEGEND:
1498 arg_legend = false;
1499 break;
1500
1501 case ARG_KILL_WHO:
1502 arg_kill_who = optarg;
1503 break;
1504
1505 case 's':
1506 arg_signal = signal_from_string_try_harder(optarg);
1507 if (arg_signal < 0) {
1508 log_error("Failed to parse signal string %s.", optarg);
1509 return -EINVAL;
1510 }
1511 break;
1512
1513 case 'H':
1514 arg_transport = BUS_TRANSPORT_REMOTE;
1515 arg_host = optarg;
1516 break;
1517
1518 case 'M':
1519 arg_transport = BUS_TRANSPORT_MACHINE;
1520 arg_host = optarg;
1521 break;
1522
1523 case ARG_READ_ONLY:
1524 arg_read_only = true;
1525 break;
1526
1527 case ARG_MKDIR:
1528 arg_mkdir = true;
1529 break;
1530
1531 case '?':
1532 return -EINVAL;
1533
1534 default:
1535 assert_not_reached("Unhandled option");
1536 }
1537
1538 return 1;
1539 }
1540
1541 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1542
1543 static const Verb verbs[] = {
1544 { "help", VERB_ANY, VERB_ANY, 0, help },
1545 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
1546 { "list-images", VERB_ANY, 1, 0, list_images },
1547 { "status", 2, VERB_ANY, 0, show_machine },
1548 { "image-status",2, VERB_ANY, 0, show_image },
1549 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
1550 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
1551 { "terminate", 2, VERB_ANY, 0, terminate_machine },
1552 { "reboot", 2, VERB_ANY, 0, reboot_machine },
1553 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
1554 { "kill", 2, VERB_ANY, 0, kill_machine },
1555 { "login", 2, 2, 0, login_machine },
1556 { "bind", 3, 4, 0, bind_mount },
1557 { "copy-to", 3, 4, 0, copy_files },
1558 { "copy-from", 3, 4, 0, copy_files },
1559 { "remove", 2, VERB_ANY, 0, remove_image },
1560 { "rename", 3, 3, 0, rename_image },
1561 { "clone", 3, 3, 0, clone_image },
1562 { "read-only", 2, 3, 0, read_only_image },
1563 {}
1564 };
1565
1566 return dispatch_verb(argc, argv, verbs, bus);
1567 }
1568
1569 int main(int argc, char*argv[]) {
1570 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1571 int r;
1572
1573 setlocale(LC_ALL, "");
1574 log_parse_environment();
1575 log_open();
1576
1577 r = parse_argv(argc, argv);
1578 if (r <= 0)
1579 goto finish;
1580
1581 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1582 if (r < 0) {
1583 log_error_errno(r, "Failed to create bus connection: %m");
1584 goto finish;
1585 }
1586
1587 r = machinectl_main(argc, argv, bus);
1588
1589 finish:
1590 pager_close();
1591
1592 strv_free(arg_property);
1593
1594 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1595 }