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