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