chiark / gitweb /
machinectl: show network interface name for containers
[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
34 #include "sd-bus.h"
35 #include "log.h"
36 #include "util.h"
37 #include "macro.h"
38 #include "pager.h"
39 #include "bus-util.h"
40 #include "bus-error.h"
41 #include "build.h"
42 #include "strv.h"
43 #include "unit-name.h"
44 #include "cgroup-show.h"
45 #include "cgroup-util.h"
46 #include "ptyfwd.h"
47
48 static char **arg_property = NULL;
49 static bool arg_all = false;
50 static bool arg_full = false;
51 static bool arg_no_pager = false;
52 static bool arg_legend = true;
53 static const char *arg_kill_who = NULL;
54 static int arg_signal = SIGTERM;
55 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
56 static char *arg_host = NULL;
57
58 static void pager_open_if_enabled(void) {
59
60         /* Cache result before we open the pager */
61         if (arg_no_pager)
62                 return;
63
64         pager_open(false);
65 }
66
67 static int list_machines(sd_bus *bus, char **args, unsigned n) {
68         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
69         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
70         const char *name, *class, *service, *object;
71         unsigned k = 0;
72         int r;
73
74         pager_open_if_enabled();
75
76         r = sd_bus_call_method(
77                                 bus,
78                                 "org.freedesktop.machine1",
79                                 "/org/freedesktop/machine1",
80                                 "org.freedesktop.machine1.Manager",
81                                 "ListMachines",
82                                 &error,
83                                 &reply,
84                                 "");
85         if (r < 0) {
86                 log_error("Could not get machines: %s", bus_error_message(&error, -r));
87                 return r;
88         }
89
90         if (arg_legend)
91                 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
92
93         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
94         if (r < 0)
95                 return bus_log_parse_error(r);
96
97         while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
98                 printf("%-32s %-9s %-16s\n", name, class, service);
99
100                 k++;
101         }
102         if (r < 0)
103                 return bus_log_parse_error(r);
104
105         r = sd_bus_message_exit_container(reply);
106         if (r < 0)
107                 return bus_log_parse_error(r);
108
109         if (arg_legend)
110                 printf("\n%u machines listed.\n", k);
111
112         return 0;
113 }
114
115 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
116         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
117         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
118         _cleanup_free_ char *path = NULL;
119         const char *cgroup;
120         int r, output_flags;
121         unsigned c;
122
123         assert(bus);
124         assert(unit);
125
126         if (arg_transport == BUS_TRANSPORT_REMOTE)
127                 return 0;
128
129         path = unit_dbus_path_from_name(unit);
130         if (!path)
131                 return log_oom();
132
133         r = sd_bus_get_property(
134                         bus,
135                         "org.freedesktop.systemd1",
136                         path,
137                         endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
138                         "ControlGroup",
139                         &error,
140                         &reply,
141                         "s");
142         if (r < 0) {
143                 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
144                 return r;
145         }
146
147         r = sd_bus_message_read(reply, "s", &cgroup);
148         if (r < 0)
149                 return bus_log_parse_error(r);
150
151         if (isempty(cgroup))
152                 return 0;
153
154         if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
155                 return 0;
156
157         output_flags =
158                 arg_all * OUTPUT_SHOW_ALL |
159                 arg_full * OUTPUT_FULL_WIDTH;
160
161         c = columns();
162         if (c > 18)
163                 c -= 18;
164         else
165                 c = 0;
166
167         show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t  ", c, false, &leader, leader > 0, output_flags);
168         return 0;
169 }
170
171 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
172         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
173         int r;
174
175         assert(bus);
176         assert(name);
177         assert(prefix);
178         assert(prefix2);
179
180         r = sd_bus_call_method(bus,
181                                "org.freedesktop.machine1",
182                                "/org/freedesktop/machine1",
183                                "org.freedesktop.machine1.Manager",
184                                "GetMachineAddresses",
185                                NULL,
186                                &reply,
187                                "s", name);
188         if (r < 0)
189                 return r;
190
191         r = sd_bus_message_enter_container(reply, 'a', "(yay)");
192         if (r < 0)
193                 return bus_log_parse_error(r);
194
195         while ((r = sd_bus_message_enter_container(reply, 'r', "yay")) > 0) {
196                 unsigned char family;
197                 const void *a;
198                 size_t sz;
199                 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
200
201                 r = sd_bus_message_read(reply, "y", &family);
202                 if (r < 0)
203                         return bus_log_parse_error(r);
204
205                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
206                 if (r < 0)
207                         return bus_log_parse_error(r);
208
209                 fputs(prefix, stdout);
210                 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
211                 if (family == AF_INET6 && ifi > 0)
212                         printf("%%%i", ifi);
213                 fputc('\n', stdout);
214
215                 r = sd_bus_message_exit_container(reply);
216                 if (r < 0)
217                         return bus_log_parse_error(r);
218
219                 if (prefix != prefix2)
220                         prefix = prefix2;
221         }
222         if (r < 0)
223                 return bus_log_parse_error(r);
224
225         r = sd_bus_message_exit_container(reply);
226         if (r < 0)
227                 return bus_log_parse_error(r);
228
229         return 0;
230 }
231
232 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
233         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
234         const char *k, *v, *pretty = NULL;
235         int r;
236
237         assert(bus);
238         assert(name);
239         assert(prefix);
240
241         r = sd_bus_call_method(bus,
242                                "org.freedesktop.machine1",
243                                "/org/freedesktop/machine1",
244                                "org.freedesktop.machine1.Manager",
245                                "GetMachineOSRelease",
246                                NULL,
247                                &reply,
248                                "s", name);
249         if (r < 0)
250                 return r;
251
252         r = sd_bus_message_enter_container(reply, 'a', "{ss}");
253         if (r < 0)
254                 return bus_log_parse_error(r);
255
256         while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
257                 if (streq(k, "PRETTY_NAME"))
258                         pretty = v;
259
260         }
261         if (r < 0)
262                 return bus_log_parse_error(r);
263
264         r = sd_bus_message_exit_container(reply);
265         if (r < 0)
266                 return bus_log_parse_error(r);
267
268         if (pretty)
269                 printf("%s%s\n", prefix, pretty);
270
271         return 0;
272 }
273
274 typedef struct MachineStatusInfo {
275         char *name;
276         sd_id128_t id;
277         char *class;
278         char *service;
279         char *unit;
280         char *root_directory;
281         pid_t leader;
282         usec_t timestamp;
283         int *netif;
284         unsigned n_netif;
285 } MachineStatusInfo;
286
287 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
288         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
289         char since2[FORMAT_TIMESTAMP_MAX], *s2;
290         int ifi = -1;
291
292         assert(i);
293
294         fputs(strna(i->name), stdout);
295
296         if (!sd_id128_equal(i->id, SD_ID128_NULL))
297                 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
298         else
299                 putchar('\n');
300
301         s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
302         s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
303
304         if (s1)
305                 printf("\t   Since: %s; %s\n", s2, s1);
306         else if (s2)
307                 printf("\t   Since: %s\n", s2);
308
309         if (i->leader > 0) {
310                 _cleanup_free_ char *t = NULL;
311
312                 printf("\t  Leader: %u", (unsigned) i->leader);
313
314                 get_process_comm(i->leader, &t);
315                 if (t)
316                         printf(" (%s)", t);
317
318                 putchar('\n');
319         }
320
321         if (i->service) {
322                 printf("\t Service: %s", i->service);
323
324                 if (i->class)
325                         printf("; class %s", i->class);
326
327                 putchar('\n');
328         } else if (i->class)
329                 printf("\t   Class: %s\n", i->class);
330
331         if (i->root_directory)
332                 printf("\t    Root: %s\n", i->root_directory);
333
334         if (i->n_netif > 0) {
335                 unsigned c;
336
337                 fputs("\t   Iface:", stdout);
338
339                 for (c = 0; c < i->n_netif; c++) {
340                         char name[IF_NAMESIZE+1] = "";
341
342                         if (if_indextoname(i->netif[c], name)) {
343                                 fputc(' ', stdout);
344                                 fputs(name, stdout);
345
346                                 if (ifi < 0)
347                                         ifi = i->netif[c];
348                                 else
349                                         ifi = 0;
350                         } else
351                                 printf(" %i", i->netif[c]);
352                 }
353
354                 fputc('\n', stdout);
355         }
356
357         print_addresses(bus, i->name, ifi,
358                        "\t Address: ",
359                        "\t          ");
360
361         print_os_release(bus, i->name, "\t      OS: ");
362
363         if (i->unit) {
364                 printf("\t    Unit: %s\n", i->unit);
365                 show_unit_cgroup(bus, i->unit, i->leader);
366         }
367 }
368
369 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
370         MachineStatusInfo *i = userdata;
371         size_t l;
372         const void *v;
373         int r;
374
375         assert_cc(sizeof(int32_t) == sizeof(int));
376         r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
377         if (r < 0)
378                 return r;
379
380         i->n_netif = l / sizeof(int32_t);
381         i->netif = memdup(v, l);
382         if (!i->netif)
383                 return -ENOMEM;
384
385         return 0;
386 }
387
388 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
389
390         static const struct bus_properties_map map[]  = {
391                 { "Name",              "s",  NULL,          offsetof(MachineStatusInfo, name) },
392                 { "Class",             "s",  NULL,          offsetof(MachineStatusInfo, class) },
393                 { "Service",           "s",  NULL,          offsetof(MachineStatusInfo, service) },
394                 { "Unit",              "s",  NULL,          offsetof(MachineStatusInfo, unit) },
395                 { "RootDirectory",     "s",  NULL,          offsetof(MachineStatusInfo, root_directory) },
396                 { "Leader",            "u",  NULL,          offsetof(MachineStatusInfo, leader) },
397                 { "Timestamp",         "t",  NULL,          offsetof(MachineStatusInfo, timestamp) },
398                 { "Id",                "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
399                 { "NetworkInterfaces", "ai", map_netif,     0 },
400                 {}
401         };
402
403         MachineStatusInfo info = {};
404         int r;
405
406         assert(path);
407         assert(new_line);
408
409         r = bus_map_all_properties(bus,
410                                    "org.freedesktop.machine1",
411                                    path,
412                                    map,
413                                    &info);
414         if (r < 0) {
415                 log_error("Could not get properties: %s", strerror(-r));
416                 return r;
417         }
418
419         if (*new_line)
420                 printf("\n");
421         *new_line = true;
422
423         print_machine_status_info(bus, &info);
424
425         free(info.name);
426         free(info.class);
427         free(info.service);
428         free(info.unit);
429         free(info.root_directory);
430         free(info.netif);
431
432         return r;
433 }
434
435 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
436         int r;
437
438         if (*new_line)
439                 printf("\n");
440
441         *new_line = true;
442
443         r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
444         if (r < 0)
445                 log_error("Could not get properties: %s", strerror(-r));
446
447         return r;
448 }
449
450 static int show(sd_bus *bus, char **args, unsigned n) {
451         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
452         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
453         int r = 0;
454         unsigned i;
455         bool properties, new_line = false;
456
457         assert(bus);
458         assert(args);
459
460         properties = !strstr(args[0], "status");
461
462         pager_open_if_enabled();
463
464         if (properties && n <= 1) {
465
466                 /* If no argument is specified, inspect the manager
467                  * itself */
468                 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
469                 if (r < 0)
470                         return r;
471         }
472
473         for (i = 1; i < n; i++) {
474                 const char *path = NULL;
475
476                 r = sd_bus_call_method(
477                                         bus,
478                                         "org.freedesktop.machine1",
479                                         "/org/freedesktop/machine1",
480                                         "org.freedesktop.machine1.Manager",
481                                         "GetMachine",
482                                         &error,
483                                         &reply,
484                                         "s", args[i]);
485                 if (r < 0) {
486                         log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
487                         return r;
488                 }
489
490                 r = sd_bus_message_read(reply, "o", &path);
491                 if (r < 0)
492                         return bus_log_parse_error(r);
493
494                 if (properties)
495                         r = show_properties(bus, path, &new_line);
496                 else
497                         r = show_info(args[0], bus, path, &new_line);
498         }
499
500         return r;
501 }
502
503 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
504         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
505         unsigned i;
506
507         assert(args);
508
509         if (!arg_kill_who)
510                 arg_kill_who = "all";
511
512         for (i = 1; i < n; i++) {
513                 int r;
514
515                 r = sd_bus_call_method(
516                                         bus,
517                                         "org.freedesktop.machine1",
518                                         "/org/freedesktop/machine1",
519                                         "org.freedesktop.machine1.Manager",
520                                         "KillMachine",
521                                         &error,
522                                         NULL,
523                                         "ssi", args[i], arg_kill_who, arg_signal);
524                 if (r < 0) {
525                         log_error("Could not kill machine: %s", bus_error_message(&error, -r));
526                         return r;
527                 }
528         }
529
530         return 0;
531 }
532
533 static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
534         arg_kill_who = "leader";
535         arg_signal = SIGINT; /* sysvinit + systemd */
536
537         return kill_machine(bus, args, n);
538 }
539
540 static int poweroff_machine(sd_bus *bus, char **args, unsigned n) {
541         arg_kill_who = "leader";
542         arg_signal = SIGRTMIN+4; /* only systemd */
543
544         return kill_machine(bus, args, n);
545 }
546
547 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
548         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
549         unsigned i;
550
551         assert(args);
552
553         for (i = 1; i < n; i++) {
554                 int r;
555
556                 r = sd_bus_call_method(
557                                 bus,
558                                 "org.freedesktop.machine1",
559                                 "/org/freedesktop/machine1",
560                                 "org.freedesktop.machine1.Manager",
561                                 "TerminateMachine",
562                                 &error,
563                                 NULL,
564                                 "s", args[i]);
565                 if (r < 0) {
566                         log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
567                         return r;
568                 }
569         }
570
571         return 0;
572 }
573
574 static int openpt_in_namespace(pid_t pid, int flags) {
575         _cleanup_close_pair_ int pair[2] = { -1, -1 };
576         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
577         union {
578                 struct cmsghdr cmsghdr;
579                 uint8_t buf[CMSG_SPACE(sizeof(int))];
580         } control = {};
581         struct msghdr mh = {
582                 .msg_control = &control,
583                 .msg_controllen = sizeof(control),
584         };
585         struct cmsghdr *cmsg;
586         int master = -1, r;
587         pid_t child;
588         siginfo_t si;
589
590         r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
591         if (r < 0)
592                 return r;
593
594         if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
595                 return -errno;
596
597         child = fork();
598         if (child < 0)
599                 return -errno;
600
601         if (child == 0) {
602                 pair[0] = safe_close(pair[0]);
603
604                 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
605                 if (r < 0)
606                         _exit(EXIT_FAILURE);
607
608                 master = posix_openpt(flags);
609                 if (master < 0)
610                         _exit(EXIT_FAILURE);
611
612                 cmsg = CMSG_FIRSTHDR(&mh);
613                 cmsg->cmsg_level = SOL_SOCKET;
614                 cmsg->cmsg_type = SCM_RIGHTS;
615                 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
616                 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
617
618                 mh.msg_controllen = cmsg->cmsg_len;
619
620                 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
621                         _exit(EXIT_FAILURE);
622
623                 _exit(EXIT_SUCCESS);
624         }
625
626         pair[1] = safe_close(pair[1]);
627
628         r = wait_for_terminate(child, &si);
629         if (r < 0)
630                 return r;
631         if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
632                 return -EIO;
633
634         if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
635                 return -errno;
636
637         for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
638                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
639                         int *fds;
640                         unsigned n_fds;
641
642                         fds = (int*) CMSG_DATA(cmsg);
643                         n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
644
645                         if (n_fds != 1) {
646                                 close_many(fds, n_fds);
647                                 return -EIO;
648                         }
649
650                         master = fds[0];
651                 }
652
653         if (master < 0)
654                 return -EIO;
655
656         return master;
657 }
658
659 static int login_machine(sd_bus *bus, char **args, unsigned n) {
660         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
661         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
662         _cleanup_bus_unref_ sd_bus *container_bus = NULL;
663         _cleanup_close_ int master = -1;
664         _cleanup_free_ char *getty = NULL;
665         const char *path, *pty, *p;
666         uint32_t leader;
667         sigset_t mask;
668         int r;
669
670         assert(bus);
671         assert(args);
672
673         if (arg_transport != BUS_TRANSPORT_LOCAL) {
674                 log_error("Login only supported on local machines.");
675                 return -ENOTSUP;
676         }
677
678         r = sd_bus_call_method(
679                         bus,
680                         "org.freedesktop.machine1",
681                         "/org/freedesktop/machine1",
682                         "org.freedesktop.machine1.Manager",
683                         "GetMachine",
684                         &error,
685                         &reply,
686                         "s", args[1]);
687         if (r < 0) {
688                 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
689                 return r;
690         }
691
692         r = sd_bus_message_read(reply, "o", &path);
693         if (r < 0)
694                 return bus_log_parse_error(r);
695
696         r = sd_bus_get_property(
697                         bus,
698                         "org.freedesktop.machine1",
699                         path,
700                         "org.freedesktop.machine1.Machine",
701                         "Leader",
702                         &error,
703                         &reply2,
704                         "u");
705         if (r < 0) {
706                 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
707                 return r;
708         }
709
710         r = sd_bus_message_read(reply2, "u", &leader);
711         if (r < 0)
712                 return bus_log_parse_error(r);
713
714         master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
715         if (master < 0) {
716                 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
717                 return master;
718         }
719
720         pty = ptsname(master);
721         if (!pty) {
722                 log_error("Failed to get pty name: %m");
723                 return -errno;
724         }
725
726         p = startswith(pty, "/dev/pts/");
727         if (!p) {
728                 log_error("Invalid pty name %s.", pty);
729                 return -EIO;
730         }
731
732         r = sd_bus_open_system_container(&container_bus, args[1]);
733         if (r < 0) {
734                 log_error("Failed to get container bus: %s", strerror(-r));
735                 return r;
736         }
737
738         getty = strjoin("container-getty@", p, ".service", NULL);
739         if (!getty)
740                 return log_oom();
741
742         if (unlockpt(master) < 0) {
743                 log_error("Failed to unlock tty: %m");
744                 return -errno;
745         }
746
747         r = sd_bus_call_method(container_bus,
748                                "org.freedesktop.systemd1",
749                                "/org/freedesktop/systemd1",
750                                "org.freedesktop.systemd1.Manager",
751                                "StartUnit",
752                                &error, &reply3,
753                                "ss", getty, "replace");
754         if (r < 0) {
755                 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
756                 return r;
757         }
758
759         container_bus = sd_bus_unref(container_bus);
760
761         assert_se(sigemptyset(&mask) == 0);
762         sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
763         assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
764
765         log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
766
767         r = process_pty(master, &mask, 0, 0);
768         if (r < 0) {
769                 log_error("Failed to process pseudo tty: %s", strerror(-r));
770                 return r;
771         }
772
773         fputc('\n', stdout);
774
775         log_info("Connection to container %s terminated.", args[1]);
776
777         return 0;
778 }
779
780 static int help(void) {
781
782         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
783                "Send control commands to or query the virtual machine and container registration manager.\n\n"
784                "  -h --help              Show this help\n"
785                "     --version           Show package version\n"
786                "     --no-pager          Do not pipe output into a pager\n"
787                "     --no-legend         Do not show the headers and footers\n"
788                "  -H --host=[USER@]HOST  Operate on remote host\n"
789                "  -M --machine=CONTAINER Operate on local container\n"
790                "  -p --property=NAME     Show only properties by this name\n"
791                "  -a --all               Show all properties, including empty ones\n"
792                "  -l --full              Do not ellipsize output\n"
793                "     --kill-who=WHO      Who to send signal to\n"
794                "  -s --signal=SIGNAL     Which signal to send\n\n"
795                "Commands:\n"
796                "  list                   List running VMs and containers\n"
797                "  status NAME...         Show VM/container status\n"
798                "  show NAME...           Show properties of one or more VMs/containers\n"
799                "  login NAME             Get a login prompt on a container\n"
800                "  poweroff NAME...       Power off one or more containers\n"
801                "  reboot NAME...         Reboot one or more containers\n"
802                "  kill NAME...           Send signal to processes of a VM/container\n"
803                "  terminate NAME...      Terminate one or more VMs/containers\n",
804                program_invocation_short_name);
805
806         return 0;
807 }
808
809 static int parse_argv(int argc, char *argv[]) {
810
811         enum {
812                 ARG_VERSION = 0x100,
813                 ARG_NO_PAGER,
814                 ARG_NO_LEGEND,
815                 ARG_KILL_WHO,
816         };
817
818         static const struct option options[] = {
819                 { "help",            no_argument,       NULL, 'h'                 },
820                 { "version",         no_argument,       NULL, ARG_VERSION         },
821                 { "property",        required_argument, NULL, 'p'                 },
822                 { "all",             no_argument,       NULL, 'a'                 },
823                 { "full",            no_argument,       NULL, 'l'                 },
824                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
825                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
826                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
827                 { "signal",          required_argument, NULL, 's'                 },
828                 { "host",            required_argument, NULL, 'H'                 },
829                 { "machine",         required_argument, NULL, 'M'                 },
830                 {}
831         };
832
833         int c, r;
834
835         assert(argc >= 0);
836         assert(argv);
837
838         while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
839
840                 switch (c) {
841
842                 case 'h':
843                         return help();
844
845                 case ARG_VERSION:
846                         puts(PACKAGE_STRING);
847                         puts(SYSTEMD_FEATURES);
848                         return 0;
849
850                 case 'p':
851                         r = strv_extend(&arg_property, optarg);
852                         if (r < 0)
853                                 return log_oom();
854
855                         /* If the user asked for a particular
856                          * property, show it to him, even if it is
857                          * empty. */
858                         arg_all = true;
859                         break;
860
861                 case 'a':
862                         arg_all = true;
863                         break;
864
865                 case 'l':
866                         arg_full = true;
867                         break;
868
869                 case ARG_NO_PAGER:
870                         arg_no_pager = true;
871                         break;
872
873                 case ARG_NO_LEGEND:
874                         arg_legend = false;
875                         break;
876
877                 case ARG_KILL_WHO:
878                         arg_kill_who = optarg;
879                         break;
880
881                 case 's':
882                         arg_signal = signal_from_string_try_harder(optarg);
883                         if (arg_signal < 0) {
884                                 log_error("Failed to parse signal string %s.", optarg);
885                                 return -EINVAL;
886                         }
887                         break;
888
889                 case 'H':
890                         arg_transport = BUS_TRANSPORT_REMOTE;
891                         arg_host = optarg;
892                         break;
893
894                 case 'M':
895                         arg_transport = BUS_TRANSPORT_CONTAINER;
896                         arg_host = optarg;
897                         break;
898
899                 case '?':
900                         return -EINVAL;
901
902                 default:
903                         assert_not_reached("Unhandled option");
904                 }
905         }
906
907         return 1;
908 }
909
910 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
911
912         static const struct {
913                 const char* verb;
914                 const enum {
915                         MORE,
916                         LESS,
917                         EQUAL
918                 } argc_cmp;
919                 const int argc;
920                 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
921         } verbs[] = {
922                 { "list",                  LESS,   1, list_machines     },
923                 { "status",                MORE,   2, show              },
924                 { "show",                  MORE,   1, show              },
925                 { "terminate",             MORE,   2, terminate_machine },
926                 { "reboot",                MORE,   2, reboot_machine    },
927                 { "poweroff",              MORE,   2, poweroff_machine  },
928                 { "kill",                  MORE,   2, kill_machine      },
929                 { "login",                 MORE,   2, login_machine     },
930         };
931
932         int left;
933         unsigned i;
934
935         assert(argc >= 0);
936         assert(argv);
937
938         left = argc - optind;
939
940         if (left <= 0)
941                 /* Special rule: no arguments means "list" */
942                 i = 0;
943         else {
944                 if (streq(argv[optind], "help")) {
945                         help();
946                         return 0;
947                 }
948
949                 for (i = 0; i < ELEMENTSOF(verbs); i++)
950                         if (streq(argv[optind], verbs[i].verb))
951                                 break;
952
953                 if (i >= ELEMENTSOF(verbs)) {
954                         log_error("Unknown operation %s", argv[optind]);
955                         return -EINVAL;
956                 }
957         }
958
959         switch (verbs[i].argc_cmp) {
960
961         case EQUAL:
962                 if (left != verbs[i].argc) {
963                         log_error("Invalid number of arguments.");
964                         return -EINVAL;
965                 }
966
967                 break;
968
969         case MORE:
970                 if (left < verbs[i].argc) {
971                         log_error("Too few arguments.");
972                         return -EINVAL;
973                 }
974
975                 break;
976
977         case LESS:
978                 if (left > verbs[i].argc) {
979                         log_error("Too many arguments.");
980                         return -EINVAL;
981                 }
982
983                 break;
984
985         default:
986                 assert_not_reached("Unknown comparison operator.");
987         }
988
989         return verbs[i].dispatch(bus, argv + optind, left);
990 }
991
992 int main(int argc, char*argv[]) {
993         _cleanup_bus_unref_ sd_bus *bus = NULL;
994         int r;
995
996         setlocale(LC_ALL, "");
997         log_parse_environment();
998         log_open();
999
1000         r = parse_argv(argc, argv);
1001         if (r <= 0)
1002                 goto finish;
1003
1004         r = bus_open_transport(arg_transport, arg_host, false, &bus);
1005         if (r < 0) {
1006                 log_error("Failed to create bus connection: %s", strerror(-r));
1007                 goto finish;
1008         }
1009
1010         r = machinectl_main(bus, argc, argv);
1011
1012 finish:
1013         pager_close();
1014
1015         strv_free(arg_property);
1016
1017         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1018 }