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