chiark / gitweb /
0f69734dc731be6c3a22629418c4f8ef4e900efb
[elogind.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 help(int argc, char *argv[], void *userdata) {
1287
1288         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1289                "Send control commands to or query the virtual machine and container\n"
1290                "registration manager.\n\n"
1291                "  -h --help                   Show this help\n"
1292                "     --version                Show package version\n"
1293                "     --no-pager               Do not pipe output into a pager\n"
1294                "     --no-legend              Do not show the headers and footers\n"
1295                "  -H --host=[USER@]HOST       Operate on remote host\n"
1296                "  -M --machine=CONTAINER      Operate on local container\n"
1297                "  -p --property=NAME          Show only properties by this name\n"
1298                "  -a --all                    Show all properties, including empty ones\n"
1299                "  -l --full                   Do not ellipsize output\n"
1300                "     --kill-who=WHO           Who to send signal to\n"
1301                "  -s --signal=SIGNAL          Which signal to send\n"
1302                "     --read-only              Create read-only bind mount\n"
1303                "     --mkdir                  Create directory before bind mounting, if missing\n\n"
1304                "Machine Commands:\n"
1305                "  list                        List running VMs and containers\n"
1306                "  status NAME...              Show VM/container details\n"
1307                "  show NAME...                Show properties of one or more VMs/containers\n"
1308                "  login NAME                  Get a login prompt on a container\n"
1309                "  poweroff NAME...            Power off one or more containers\n"
1310                "  reboot NAME...              Reboot one or more containers\n"
1311                "  kill NAME...                Send signal to processes of a VM/container\n"
1312                "  terminate NAME...           Terminate one or more VMs/containers\n"
1313                "  bind NAME PATH [PATH]       Bind mount a path from the host into a container\n"
1314                "  copy-to NAME PATH [PATH]    Copy files from the host to a container\n"
1315                "  copy-from NAME PATH [PATH]  Copy files from a container to the host\n\n"
1316                "Image Commands:\n"
1317                "  list-images                 Show available images\n"
1318                "  image-status NAME...        Show image details\n"
1319                "  show-image NAME...          Show properties of image\n",
1320                program_invocation_short_name);
1321
1322         return 0;
1323 }
1324
1325 static int parse_argv(int argc, char *argv[]) {
1326
1327         enum {
1328                 ARG_VERSION = 0x100,
1329                 ARG_NO_PAGER,
1330                 ARG_NO_LEGEND,
1331                 ARG_KILL_WHO,
1332                 ARG_READ_ONLY,
1333                 ARG_MKDIR,
1334         };
1335
1336         static const struct option options[] = {
1337                 { "help",            no_argument,       NULL, 'h'                 },
1338                 { "version",         no_argument,       NULL, ARG_VERSION         },
1339                 { "property",        required_argument, NULL, 'p'                 },
1340                 { "all",             no_argument,       NULL, 'a'                 },
1341                 { "full",            no_argument,       NULL, 'l'                 },
1342                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
1343                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
1344                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
1345                 { "signal",          required_argument, NULL, 's'                 },
1346                 { "host",            required_argument, NULL, 'H'                 },
1347                 { "machine",         required_argument, NULL, 'M'                 },
1348                 { "read-only",       no_argument,       NULL, ARG_READ_ONLY       },
1349                 { "mkdir",           no_argument,       NULL, ARG_MKDIR           },
1350                 {}
1351         };
1352
1353         int c, r;
1354
1355         assert(argc >= 0);
1356         assert(argv);
1357
1358         while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1359
1360                 switch (c) {
1361
1362                 case 'h':
1363                         return help(0, NULL, NULL);
1364
1365                 case ARG_VERSION:
1366                         puts(PACKAGE_STRING);
1367                         puts(SYSTEMD_FEATURES);
1368                         return 0;
1369
1370                 case 'p':
1371                         r = strv_extend(&arg_property, optarg);
1372                         if (r < 0)
1373                                 return log_oom();
1374
1375                         /* If the user asked for a particular
1376                          * property, show it to him, even if it is
1377                          * empty. */
1378                         arg_all = true;
1379                         break;
1380
1381                 case 'a':
1382                         arg_all = true;
1383                         break;
1384
1385                 case 'l':
1386                         arg_full = true;
1387                         break;
1388
1389                 case ARG_NO_PAGER:
1390                         arg_no_pager = true;
1391                         break;
1392
1393                 case ARG_NO_LEGEND:
1394                         arg_legend = false;
1395                         break;
1396
1397                 case ARG_KILL_WHO:
1398                         arg_kill_who = optarg;
1399                         break;
1400
1401                 case 's':
1402                         arg_signal = signal_from_string_try_harder(optarg);
1403                         if (arg_signal < 0) {
1404                                 log_error("Failed to parse signal string %s.", optarg);
1405                                 return -EINVAL;
1406                         }
1407                         break;
1408
1409                 case 'H':
1410                         arg_transport = BUS_TRANSPORT_REMOTE;
1411                         arg_host = optarg;
1412                         break;
1413
1414                 case 'M':
1415                         arg_transport = BUS_TRANSPORT_MACHINE;
1416                         arg_host = optarg;
1417                         break;
1418
1419                 case ARG_READ_ONLY:
1420                         arg_read_only = true;
1421                         break;
1422
1423                 case ARG_MKDIR:
1424                         arg_mkdir = true;
1425                         break;
1426
1427                 case '?':
1428                         return -EINVAL;
1429
1430                 default:
1431                         assert_not_reached("Unhandled option");
1432                 }
1433
1434         return 1;
1435 }
1436
1437 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1438
1439         static const Verb verbs[] = {
1440                 { "help",        VERB_ANY, VERB_ANY, 0,            help              },
1441                 { "list",        VERB_ANY, 1,        VERB_DEFAULT, list_machines     },
1442                 { "list-images", VERB_ANY, 1,        0,            list_images       },
1443                 { "status",      2,        VERB_ANY, 0,            show_machine      },
1444                 { "image-status",2,        VERB_ANY, 0,            show_image        },
1445                 { "show",        VERB_ANY, VERB_ANY, 0,            show_machine      },
1446                 { "show-image",  VERB_ANY, VERB_ANY, 0,            show_image        },
1447                 { "terminate",   2,        VERB_ANY, 0,            terminate_machine },
1448                 { "reboot",      2,        VERB_ANY, 0,            reboot_machine    },
1449                 { "poweroff",    2,        VERB_ANY, 0,            poweroff_machine  },
1450                 { "kill",        2,        VERB_ANY, 0,            kill_machine      },
1451                 { "login",       2,        2,        0,            login_machine     },
1452                 { "bind",        3,        4,        0,            bind_mount        },
1453                 { "copy-to",     3,        4,        0,            copy_files        },
1454                 { "copy-from",   3,        4,        0,            copy_files        },
1455                 {}
1456         };
1457
1458         return dispatch_verb(argc, argv, verbs, bus);
1459 }
1460
1461 int main(int argc, char*argv[]) {
1462         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1463         int r;
1464
1465         setlocale(LC_ALL, "");
1466         log_parse_environment();
1467         log_open();
1468
1469         r = parse_argv(argc, argv);
1470         if (r <= 0)
1471                 goto finish;
1472
1473         r = bus_open_transport(arg_transport, arg_host, false, &bus);
1474         if (r < 0) {
1475                 log_error_errno(r, "Failed to create bus connection: %m");
1476                 goto finish;
1477         }
1478
1479         r = machinectl_main(argc, argv, bus);
1480
1481 finish:
1482         pager_close();
1483
1484         strv_free(arg_property);
1485
1486         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1487 }