chiark / gitweb /
7e995cab68d22e6d092d703c7fd7d0fbd33cc602
[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 hangup */
1244
1245                 r = pty_forward_set_repeat(*forward, false);
1246                 if (r >= 0)
1247                         return 0;
1248
1249                 log_error_errno(r, "Failed to set repeat flag: %m");
1250         }
1251
1252         /* On error, or when the forwarder is not initialized yet, quit immediately */
1253         sd_event_exit(sd_bus_get_event(bus), EXIT_FAILURE);
1254         return 0;
1255 }
1256
1257 static int login_machine(int argc, char *argv[], void *userdata) {
1258         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1259         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1260         _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1261         _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1262         _cleanup_event_unref_ sd_event *event = NULL;
1263         int master = -1, r, ret = 0;
1264         sd_bus *bus = userdata;
1265         const char *pty, *match;
1266         sigset_t mask;
1267         char last_char = 0;
1268         bool machine_died;
1269
1270         assert(bus);
1271
1272         if (arg_transport != BUS_TRANSPORT_LOCAL &&
1273             arg_transport != BUS_TRANSPORT_MACHINE) {
1274                 log_error("Login only supported on local machines.");
1275                 return -ENOTSUP;
1276         }
1277
1278         r = sd_event_default(&event);
1279         if (r < 0)
1280                 return log_error_errno(r, "Failed to get event loop: %m");
1281
1282         r = sd_bus_attach_event(bus, event, 0);
1283         if (r < 0)
1284                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1285
1286         match = strappenda("type='signal',"
1287                            "sender='org.freedesktop.machine1',"
1288                            "path='/org/freedesktop/machine1',",
1289                            "interface='org.freedesktop.machine1.Manager',"
1290                            "member='MachineRemoved',"
1291                            "arg0='",
1292                            argv[1],
1293                            "'");
1294
1295         r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1296         if (r < 0)
1297                 return log_error_errno(r, "Failed to add machine removal match: %m");
1298
1299         r = sd_bus_message_new_method_call(bus,
1300                                            &m,
1301                                            "org.freedesktop.machine1",
1302                                            "/org/freedesktop/machine1",
1303                                            "org.freedesktop.machine1.Manager",
1304                                            "OpenMachineLogin");
1305         if (r < 0)
1306                 return bus_log_create_error(r);
1307
1308         r = sd_bus_message_set_allow_interactive_authorization(m, true);
1309         if (r < 0)
1310                 return bus_log_create_error(r);
1311
1312         r = sd_bus_message_append(m, "s", argv[1]);
1313         if (r < 0)
1314                 return bus_log_create_error(r);
1315
1316         r = sd_bus_call(bus, m, 0, &error, &reply);
1317         if (r < 0) {
1318                 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1319                 return r;
1320         }
1321
1322         r = sd_bus_message_read(reply, "hs", &master, &pty);
1323         if (r < 0)
1324                 return bus_log_parse_error(r);
1325
1326         assert_se(sigemptyset(&mask) == 0);
1327         sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1328         assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1329
1330         log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1331
1332         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1333         sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1334
1335         r = pty_forward_new(event, master, true, &forward);
1336         if (r < 0)
1337                 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1338
1339         r = sd_event_loop(event);
1340         if (r < 0)
1341                 return log_error_errno(r, "Failed to run event loop: %m");
1342
1343         pty_forward_get_last_char(forward, &last_char);
1344         machine_died = pty_forward_get_repeat(forward) == 0;
1345
1346         forward = pty_forward_free(forward);
1347
1348         if (last_char != '\n')
1349                 fputc('\n', stdout);
1350
1351         if (machine_died)
1352                 log_info("Machine %s terminated.", argv[1]);
1353         else
1354                 log_info("Connection to machine %s terminated.", argv[1]);
1355
1356         sd_event_get_exit_code(event, &ret);
1357         return ret;
1358 }
1359
1360 static int remove_image(int argc, char *argv[], void *userdata) {
1361         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1362         sd_bus *bus = userdata;
1363         int r, i;
1364
1365         assert(bus);
1366
1367         for (i = 1; i < argc; i++) {
1368                 r = sd_bus_call_method(
1369                                 bus,
1370                                 "org.freedesktop.machine1",
1371                                 "/org/freedesktop/machine1",
1372                                 "org.freedesktop.machine1.Manager",
1373                                 "RemoveImage",
1374                                 &error,
1375                                 NULL,
1376                                 "s", argv[i]);
1377                 if (r < 0) {
1378                         log_error("Could not remove image: %s", bus_error_message(&error, -r));
1379                         return r;
1380                 }
1381         }
1382
1383         return 0;
1384 }
1385
1386 static int rename_image(int argc, char *argv[], void *userdata) {
1387         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1388         sd_bus *bus = userdata;
1389         int r;
1390
1391         r = sd_bus_call_method(
1392                         bus,
1393                         "org.freedesktop.machine1",
1394                         "/org/freedesktop/machine1",
1395                         "org.freedesktop.machine1.Manager",
1396                         "RenameImage",
1397                         &error,
1398                         NULL,
1399                         "ss", argv[1], argv[2]);
1400         if (r < 0) {
1401                 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1402                 return r;
1403         }
1404
1405         return 0;
1406 }
1407
1408 static int clone_image(int argc, char *argv[], void *userdata) {
1409         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1410         sd_bus *bus = userdata;
1411         int r;
1412
1413         r = sd_bus_call_method(
1414                         bus,
1415                         "org.freedesktop.machine1",
1416                         "/org/freedesktop/machine1",
1417                         "org.freedesktop.machine1.Manager",
1418                         "CloneImage",
1419                         &error,
1420                         NULL,
1421                         "ssb", argv[1], argv[2], arg_read_only);
1422         if (r < 0) {
1423                 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1424                 return r;
1425         }
1426
1427         return 0;
1428 }
1429
1430 static int read_only_image(int argc, char *argv[], void *userdata) {
1431         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1432         sd_bus *bus = userdata;
1433         int b = true, r;
1434
1435         if (argc > 2) {
1436                 b = parse_boolean(argv[2]);
1437                 if (b < 0) {
1438                         log_error("Failed to parse boolean argument: %s", argv[2]);
1439                         return -EINVAL;
1440                 }
1441         }
1442
1443         r = sd_bus_call_method(
1444                         bus,
1445                         "org.freedesktop.machine1",
1446                         "/org/freedesktop/machine1",
1447                         "org.freedesktop.machine1.Manager",
1448                         "MarkImageReadOnly",
1449                         &error,
1450                         NULL,
1451                         "sb", argv[1], b);
1452         if (r < 0) {
1453                 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1454                 return r;
1455         }
1456
1457         return 0;
1458 }
1459
1460 static int start_machine(int argc, char *argv[], void *userdata) {
1461         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1462         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1463         sd_bus *bus = userdata;
1464         int r, i;
1465
1466         assert(bus);
1467
1468         r = bus_wait_for_jobs_new(bus, &w);
1469         if (r < 0)
1470                 return log_oom();
1471
1472         for (i = 1; i < argc; i++) {
1473                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1474                 _cleanup_free_ char *e = NULL, *unit = NULL;
1475                 const char *object;
1476
1477                 if (!machine_name_is_valid(argv[i])) {
1478                         log_error("Invalid machine name %s.", argv[i]);
1479                         return -EINVAL;
1480                 }
1481
1482                 e = unit_name_escape(argv[i]);
1483                 if (!e)
1484                         return log_oom();
1485
1486                 unit = unit_name_build("systemd-nspawn", e, ".service");
1487                 if (!unit)
1488                         return log_oom();
1489
1490                 r = sd_bus_message_new_method_call(
1491                                 bus,
1492                                 &m,
1493                                 "org.freedesktop.systemd1",
1494                                 "/org/freedesktop/systemd1",
1495                                 "org.freedesktop.systemd1.Manager",
1496                                 "StartUnit");
1497                 if (r < 0)
1498                         return bus_log_create_error(r);
1499
1500                 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1501                 if (r < 0)
1502                         return bus_log_create_error(r);
1503
1504                 r = sd_bus_message_append(m, "ss", unit, "fail");
1505                 if (r < 0)
1506                         return bus_log_create_error(r);
1507
1508                 r = sd_bus_call(bus, m, 0, &error, &reply);
1509                 if (r < 0) {
1510                         log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1511                         return r;
1512                 }
1513
1514                 r = sd_bus_message_read(reply, "o", &object);
1515                 if (r < 0)
1516                         return bus_log_parse_error(r);
1517
1518                 r = bus_wait_for_jobs_add(w, object);
1519                 if (r < 0)
1520                         return log_oom();
1521         }
1522
1523         r = bus_wait_for_jobs(w, arg_quiet);
1524         if (r < 0)
1525                 return r;
1526
1527         return 0;
1528 }
1529
1530 static int enable_machine(int argc, char *argv[], void *userdata) {
1531         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1532         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1533         int carries_install_info = 0;
1534         const char *method = NULL;
1535         sd_bus *bus = userdata;
1536         int r, i;
1537
1538         assert(bus);
1539
1540         method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1541
1542         r = sd_bus_message_new_method_call(
1543                         bus,
1544                         &m,
1545                         "org.freedesktop.systemd1",
1546                         "/org/freedesktop/systemd1",
1547                         "org.freedesktop.systemd1.Manager",
1548                         method);
1549         if (r < 0)
1550                 return bus_log_create_error(r);
1551
1552         r = sd_bus_message_set_allow_interactive_authorization(m, true);
1553         if (r < 0)
1554                 return bus_log_create_error(r);
1555
1556         r = sd_bus_message_open_container(m, 'a', "s");
1557         if (r < 0)
1558                 return bus_log_create_error(r);
1559
1560         for (i = 1; i < argc; i++) {
1561                 _cleanup_free_ char *e = NULL, *unit = NULL;
1562
1563                 if (!machine_name_is_valid(argv[i])) {
1564                         log_error("Invalid machine name %s.", argv[i]);
1565                         return -EINVAL;
1566                 }
1567
1568                 e = unit_name_escape(argv[i]);
1569                 if (!e)
1570                         return log_oom();
1571
1572                 unit = unit_name_build("systemd-nspawn", e, ".service");
1573                 if (!unit)
1574                         return log_oom();
1575
1576                 r = sd_bus_message_append(m, "s", unit);
1577                 if (r < 0)
1578                         return bus_log_create_error(r);
1579         }
1580
1581         r = sd_bus_message_close_container(m);
1582         if (r < 0)
1583                 return bus_log_create_error(r);
1584
1585         if (streq(argv[0], "enable"))
1586                 r = sd_bus_message_append(m, "bb", false, false);
1587         else
1588                 r = sd_bus_message_append(m, "b", false);
1589         if (r < 0)
1590                 return bus_log_create_error(r);
1591
1592         r = sd_bus_call(bus, m, 0, &error, &reply);
1593         if (r < 0) {
1594                 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1595                 return r;
1596         }
1597
1598         if (streq(argv[0], "enable")) {
1599                 r = sd_bus_message_read(reply, "b", carries_install_info);
1600                 if (r < 0)
1601                         return bus_log_parse_error(r);
1602         }
1603
1604         r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1605         if (r < 0)
1606                 return r;
1607
1608         m = sd_bus_message_unref(m);
1609
1610         r = sd_bus_message_new_method_call(
1611                         bus,
1612                         &m,
1613                         "org.freedesktop.systemd1",
1614                         "/org/freedesktop/systemd1",
1615                         "org.freedesktop.systemd1.Manager",
1616                         "Reload");
1617         if (r < 0)
1618                 return bus_log_create_error(r);
1619
1620         r = sd_bus_message_set_allow_interactive_authorization(m, true);
1621         if (r < 0)
1622                 return bus_log_create_error(r);
1623
1624         r = sd_bus_call(bus, m, 0, &error, NULL);
1625         if (r < 0) {
1626                 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1627                 return r;
1628         }
1629
1630         return 0;
1631 }
1632
1633 static int help(int argc, char *argv[], void *userdata) {
1634
1635         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1636                "Send control commands to or query the virtual machine and container\n"
1637                "registration manager.\n\n"
1638                "  -h --help                   Show this help\n"
1639                "     --version                Show package version\n"
1640                "     --no-pager               Do not pipe output into a pager\n"
1641                "     --no-legend              Do not show the headers and footers\n"
1642                "  -H --host=[USER@]HOST       Operate on remote host\n"
1643                "  -M --machine=CONTAINER      Operate on local container\n"
1644                "  -p --property=NAME          Show only properties by this name\n"
1645                "  -q --quiet                  Suppress output\n"
1646                "  -a --all                    Show all properties, including empty ones\n"
1647                "  -l --full                   Do not ellipsize output\n"
1648                "     --kill-who=WHO           Who to send signal to\n"
1649                "  -s --signal=SIGNAL          Which signal to send\n"
1650                "     --read-only              Create read-only bind mount\n"
1651                "     --mkdir                  Create directory before bind mounting, if missing\n\n"
1652                "Machine Commands:\n"
1653                "  list                        List running VMs and containers\n"
1654                "  status NAME...              Show VM/container details\n"
1655                "  show NAME...                Show properties of one or more VMs/containers\n"
1656                "  login NAME                  Get a login prompt on a container\n"
1657                "  start NAME...               Start container as a service\n"
1658                "  enable NAME...              Enable automatic container start at boot\n"
1659                "  disable NAME...             Disable automatic container start at boot\n"
1660                "  poweroff NAME...            Power off one or more containers\n"
1661                "  reboot NAME...              Reboot one or more containers\n"
1662                "  terminate NAME...           Terminate one or more VMs/containers\n"
1663                "  kill NAME...                Send signal to processes of a VM/container\n"
1664                "  copy-to NAME PATH [PATH]    Copy files from the host to a container\n"
1665                "  copy-from NAME PATH [PATH]  Copy files from a container to the host\n"
1666                "  bind NAME PATH [PATH]       Bind mount a path from the host into a container\n\n"
1667                "Image Commands:\n"
1668                "  list-images                 Show available images\n"
1669                "  image-status NAME...        Show image details\n"
1670                "  show-image NAME...          Show properties of image\n"
1671                "  clone NAME NAME             Clone an image\n"
1672                "  rename NAME NAME            Rename an image\n"
1673                "  read-only NAME [BOOL]       Mark or unmark image read-only\n"
1674                "  remove NAME...              Remove an image\n",
1675                program_invocation_short_name);
1676
1677         return 0;
1678 }
1679
1680 static int parse_argv(int argc, char *argv[]) {
1681
1682         enum {
1683                 ARG_VERSION = 0x100,
1684                 ARG_NO_PAGER,
1685                 ARG_NO_LEGEND,
1686                 ARG_KILL_WHO,
1687                 ARG_READ_ONLY,
1688                 ARG_MKDIR,
1689         };
1690
1691         static const struct option options[] = {
1692                 { "help",            no_argument,       NULL, 'h'                 },
1693                 { "version",         no_argument,       NULL, ARG_VERSION         },
1694                 { "property",        required_argument, NULL, 'p'                 },
1695                 { "all",             no_argument,       NULL, 'a'                 },
1696                 { "full",            no_argument,       NULL, 'l'                 },
1697                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
1698                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
1699                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
1700                 { "signal",          required_argument, NULL, 's'                 },
1701                 { "host",            required_argument, NULL, 'H'                 },
1702                 { "machine",         required_argument, NULL, 'M'                 },
1703                 { "read-only",       no_argument,       NULL, ARG_READ_ONLY       },
1704                 { "mkdir",           no_argument,       NULL, ARG_MKDIR           },
1705                 { "quiet",           no_argument,       NULL, 'q'                 },
1706                 {}
1707         };
1708
1709         int c, r;
1710
1711         assert(argc >= 0);
1712         assert(argv);
1713
1714         while ((c = getopt_long(argc, argv, "hp:als:H:M:q", options, NULL)) >= 0)
1715
1716                 switch (c) {
1717
1718                 case 'h':
1719                         return help(0, NULL, NULL);
1720
1721                 case ARG_VERSION:
1722                         puts(PACKAGE_STRING);
1723                         puts(SYSTEMD_FEATURES);
1724                         return 0;
1725
1726                 case 'p':
1727                         r = strv_extend(&arg_property, optarg);
1728                         if (r < 0)
1729                                 return log_oom();
1730
1731                         /* If the user asked for a particular
1732                          * property, show it to him, even if it is
1733                          * empty. */
1734                         arg_all = true;
1735                         break;
1736
1737                 case 'a':
1738                         arg_all = true;
1739                         break;
1740
1741                 case 'l':
1742                         arg_full = true;
1743                         break;
1744
1745                 case ARG_NO_PAGER:
1746                         arg_no_pager = true;
1747                         break;
1748
1749                 case ARG_NO_LEGEND:
1750                         arg_legend = false;
1751                         break;
1752
1753                 case ARG_KILL_WHO:
1754                         arg_kill_who = optarg;
1755                         break;
1756
1757                 case 's':
1758                         arg_signal = signal_from_string_try_harder(optarg);
1759                         if (arg_signal < 0) {
1760                                 log_error("Failed to parse signal string %s.", optarg);
1761                                 return -EINVAL;
1762                         }
1763                         break;
1764
1765                 case 'H':
1766                         arg_transport = BUS_TRANSPORT_REMOTE;
1767                         arg_host = optarg;
1768                         break;
1769
1770                 case 'M':
1771                         arg_transport = BUS_TRANSPORT_MACHINE;
1772                         arg_host = optarg;
1773                         break;
1774
1775                 case ARG_READ_ONLY:
1776                         arg_read_only = true;
1777                         break;
1778
1779                 case ARG_MKDIR:
1780                         arg_mkdir = true;
1781                         break;
1782
1783                 case 'q':
1784                         arg_quiet = true;
1785                         break;
1786
1787                 case '?':
1788                         return -EINVAL;
1789
1790                 default:
1791                         assert_not_reached("Unhandled option");
1792                 }
1793
1794         return 1;
1795 }
1796
1797 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1798
1799         static const Verb verbs[] = {
1800                 { "help",        VERB_ANY, VERB_ANY, 0,            help              },
1801                 { "list",        VERB_ANY, 1,        VERB_DEFAULT, list_machines     },
1802                 { "list-images", VERB_ANY, 1,        0,            list_images       },
1803                 { "status",      2,        VERB_ANY, 0,            show_machine      },
1804                 { "image-status",2,        VERB_ANY, 0,            show_image        },
1805                 { "show",        VERB_ANY, VERB_ANY, 0,            show_machine      },
1806                 { "show-image",  VERB_ANY, VERB_ANY, 0,            show_image        },
1807                 { "terminate",   2,        VERB_ANY, 0,            terminate_machine },
1808                 { "reboot",      2,        VERB_ANY, 0,            reboot_machine    },
1809                 { "poweroff",    2,        VERB_ANY, 0,            poweroff_machine  },
1810                 { "kill",        2,        VERB_ANY, 0,            kill_machine      },
1811                 { "login",       2,        2,        0,            login_machine     },
1812                 { "bind",        3,        4,        0,            bind_mount        },
1813                 { "copy-to",     3,        4,        0,            copy_files        },
1814                 { "copy-from",   3,        4,        0,            copy_files        },
1815                 { "remove",      2,        VERB_ANY, 0,            remove_image      },
1816                 { "rename",      3,        3,        0,            rename_image      },
1817                 { "clone",       3,        3,        0,            clone_image       },
1818                 { "read-only",   2,        3,        0,            read_only_image   },
1819                 { "start",       2,        VERB_ANY, 0,            start_machine     },
1820                 { "enable",      2,        VERB_ANY, 0,            enable_machine    },
1821                 { "disable",     2,        VERB_ANY, 0,            enable_machine    },
1822                 {}
1823         };
1824
1825         return dispatch_verb(argc, argv, verbs, bus);
1826 }
1827
1828 int main(int argc, char*argv[]) {
1829         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1830         int r;
1831
1832         setlocale(LC_ALL, "");
1833         log_parse_environment();
1834         log_open();
1835
1836         r = parse_argv(argc, argv);
1837         if (r <= 0)
1838                 goto finish;
1839
1840         r = bus_open_transport(arg_transport, arg_host, false, &bus);
1841         if (r < 0) {
1842                 log_error_errno(r, "Failed to create bus connection: %m");
1843                 goto finish;
1844         }
1845
1846         r = machinectl_main(argc, argv, bus);
1847
1848 finish:
1849         pager_close();
1850
1851         strv_free(arg_property);
1852
1853         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1854 }