chiark / gitweb /
sd-rtnl: fix reading of nla type
[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', "(iay)");
192         if (r < 0)
193                 return bus_log_parse_error(r);
194
195         while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
196                 int 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, "i", &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         if (r == 0)
380                 return -EBADMSG;
381
382         i->n_netif = l / sizeof(int32_t);
383         i->netif = memdup(v, l);
384         if (!i->netif)
385                 return -ENOMEM;
386
387         return 0;
388 }
389
390 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
391
392         static const struct bus_properties_map map[]  = {
393                 { "Name",              "s",  NULL,          offsetof(MachineStatusInfo, name) },
394                 { "Class",             "s",  NULL,          offsetof(MachineStatusInfo, class) },
395                 { "Service",           "s",  NULL,          offsetof(MachineStatusInfo, service) },
396                 { "Unit",              "s",  NULL,          offsetof(MachineStatusInfo, unit) },
397                 { "RootDirectory",     "s",  NULL,          offsetof(MachineStatusInfo, root_directory) },
398                 { "Leader",            "u",  NULL,          offsetof(MachineStatusInfo, leader) },
399                 { "Timestamp",         "t",  NULL,          offsetof(MachineStatusInfo, timestamp) },
400                 { "Id",                "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
401                 { "NetworkInterfaces", "ai", map_netif,     0 },
402                 {}
403         };
404
405         MachineStatusInfo info = {};
406         int r;
407
408         assert(path);
409         assert(new_line);
410
411         r = bus_map_all_properties(bus,
412                                    "org.freedesktop.machine1",
413                                    path,
414                                    map,
415                                    &info);
416         if (r < 0) {
417                 log_error("Could not get properties: %s", strerror(-r));
418                 return r;
419         }
420
421         if (*new_line)
422                 printf("\n");
423         *new_line = true;
424
425         print_machine_status_info(bus, &info);
426
427         free(info.name);
428         free(info.class);
429         free(info.service);
430         free(info.unit);
431         free(info.root_directory);
432         free(info.netif);
433
434         return r;
435 }
436
437 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
438         int r;
439
440         if (*new_line)
441                 printf("\n");
442
443         *new_line = true;
444
445         r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
446         if (r < 0)
447                 log_error("Could not get properties: %s", strerror(-r));
448
449         return r;
450 }
451
452 static int show(sd_bus *bus, char **args, unsigned n) {
453         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
454         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
455         int r = 0;
456         unsigned i;
457         bool properties, new_line = false;
458
459         assert(bus);
460         assert(args);
461
462         properties = !strstr(args[0], "status");
463
464         pager_open_if_enabled();
465
466         if (properties && n <= 1) {
467
468                 /* If no argument is specified, inspect the manager
469                  * itself */
470                 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
471                 if (r < 0)
472                         return r;
473         }
474
475         for (i = 1; i < n; i++) {
476                 const char *path = NULL;
477
478                 r = sd_bus_call_method(
479                                         bus,
480                                         "org.freedesktop.machine1",
481                                         "/org/freedesktop/machine1",
482                                         "org.freedesktop.machine1.Manager",
483                                         "GetMachine",
484                                         &error,
485                                         &reply,
486                                         "s", args[i]);
487                 if (r < 0) {
488                         log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
489                         return r;
490                 }
491
492                 r = sd_bus_message_read(reply, "o", &path);
493                 if (r < 0)
494                         return bus_log_parse_error(r);
495
496                 if (properties)
497                         r = show_properties(bus, path, &new_line);
498                 else
499                         r = show_info(args[0], bus, path, &new_line);
500         }
501
502         return r;
503 }
504
505 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
506         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
507         unsigned i;
508
509         assert(args);
510
511         if (!arg_kill_who)
512                 arg_kill_who = "all";
513
514         for (i = 1; i < n; i++) {
515                 int r;
516
517                 r = sd_bus_call_method(
518                                         bus,
519                                         "org.freedesktop.machine1",
520                                         "/org/freedesktop/machine1",
521                                         "org.freedesktop.machine1.Manager",
522                                         "KillMachine",
523                                         &error,
524                                         NULL,
525                                         "ssi", args[i], arg_kill_who, arg_signal);
526                 if (r < 0) {
527                         log_error("Could not kill machine: %s", bus_error_message(&error, -r));
528                         return r;
529                 }
530         }
531
532         return 0;
533 }
534
535 static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
536         arg_kill_who = "leader";
537         arg_signal = SIGINT; /* sysvinit + systemd */
538
539         return kill_machine(bus, args, n);
540 }
541
542 static int poweroff_machine(sd_bus *bus, char **args, unsigned n) {
543         arg_kill_who = "leader";
544         arg_signal = SIGRTMIN+4; /* only systemd */
545
546         return kill_machine(bus, args, n);
547 }
548
549 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
550         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
551         unsigned i;
552
553         assert(args);
554
555         for (i = 1; i < n; i++) {
556                 int r;
557
558                 r = sd_bus_call_method(
559                                 bus,
560                                 "org.freedesktop.machine1",
561                                 "/org/freedesktop/machine1",
562                                 "org.freedesktop.machine1.Manager",
563                                 "TerminateMachine",
564                                 &error,
565                                 NULL,
566                                 "s", args[i]);
567                 if (r < 0) {
568                         log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
569                         return r;
570                 }
571         }
572
573         return 0;
574 }
575
576 static int openpt_in_namespace(pid_t pid, int flags) {
577         _cleanup_close_pair_ int pair[2] = { -1, -1 };
578         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
579         union {
580                 struct cmsghdr cmsghdr;
581                 uint8_t buf[CMSG_SPACE(sizeof(int))];
582         } control = {};
583         struct msghdr mh = {
584                 .msg_control = &control,
585                 .msg_controllen = sizeof(control),
586         };
587         struct cmsghdr *cmsg;
588         int master = -1, r;
589         pid_t child;
590         siginfo_t si;
591
592         r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
593         if (r < 0)
594                 return r;
595
596         if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
597                 return -errno;
598
599         child = fork();
600         if (child < 0)
601                 return -errno;
602
603         if (child == 0) {
604                 pair[0] = safe_close(pair[0]);
605
606                 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
607                 if (r < 0)
608                         _exit(EXIT_FAILURE);
609
610                 master = posix_openpt(flags);
611                 if (master < 0)
612                         _exit(EXIT_FAILURE);
613
614                 cmsg = CMSG_FIRSTHDR(&mh);
615                 cmsg->cmsg_level = SOL_SOCKET;
616                 cmsg->cmsg_type = SCM_RIGHTS;
617                 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
618                 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
619
620                 mh.msg_controllen = cmsg->cmsg_len;
621
622                 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
623                         _exit(EXIT_FAILURE);
624
625                 _exit(EXIT_SUCCESS);
626         }
627
628         pair[1] = safe_close(pair[1]);
629
630         r = wait_for_terminate(child, &si);
631         if (r < 0)
632                 return r;
633         if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
634                 return -EIO;
635
636         if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
637                 return -errno;
638
639         for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
640                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
641                         int *fds;
642                         unsigned n_fds;
643
644                         fds = (int*) CMSG_DATA(cmsg);
645                         n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
646
647                         if (n_fds != 1) {
648                                 close_many(fds, n_fds);
649                                 return -EIO;
650                         }
651
652                         master = fds[0];
653                 }
654
655         if (master < 0)
656                 return -EIO;
657
658         return master;
659 }
660
661 static int login_machine(sd_bus *bus, char **args, unsigned n) {
662         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
663         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
664         _cleanup_bus_close_unref_ sd_bus *container_bus = NULL;
665         _cleanup_close_ int master = -1;
666         _cleanup_free_ char *getty = NULL;
667         const char *path, *pty, *p;
668         uint32_t leader;
669         sigset_t mask;
670         int r;
671
672         assert(bus);
673         assert(args);
674
675         if (arg_transport != BUS_TRANSPORT_LOCAL) {
676                 log_error("Login only supported on local machines.");
677                 return -ENOTSUP;
678         }
679
680         r = sd_bus_call_method(
681                         bus,
682                         "org.freedesktop.machine1",
683                         "/org/freedesktop/machine1",
684                         "org.freedesktop.machine1.Manager",
685                         "GetMachine",
686                         &error,
687                         &reply,
688                         "s", args[1]);
689         if (r < 0) {
690                 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
691                 return r;
692         }
693
694         r = sd_bus_message_read(reply, "o", &path);
695         if (r < 0)
696                 return bus_log_parse_error(r);
697
698         r = sd_bus_get_property(
699                         bus,
700                         "org.freedesktop.machine1",
701                         path,
702                         "org.freedesktop.machine1.Machine",
703                         "Leader",
704                         &error,
705                         &reply2,
706                         "u");
707         if (r < 0) {
708                 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
709                 return r;
710         }
711
712         r = sd_bus_message_read(reply2, "u", &leader);
713         if (r < 0)
714                 return bus_log_parse_error(r);
715
716         master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
717         if (master < 0) {
718                 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
719                 return master;
720         }
721
722         pty = ptsname(master);
723         if (!pty) {
724                 log_error("Failed to get pty name: %m");
725                 return -errno;
726         }
727
728         p = startswith(pty, "/dev/pts/");
729         if (!p) {
730                 log_error("Invalid pty name %s.", pty);
731                 return -EIO;
732         }
733
734         r = sd_bus_open_system_container(&container_bus, args[1]);
735         if (r < 0) {
736                 log_error("Failed to get container bus: %s", strerror(-r));
737                 return r;
738         }
739
740         getty = strjoin("container-getty@", p, ".service", NULL);
741         if (!getty)
742                 return log_oom();
743
744         if (unlockpt(master) < 0) {
745                 log_error("Failed to unlock tty: %m");
746                 return -errno;
747         }
748
749         r = sd_bus_call_method(container_bus,
750                                "org.freedesktop.systemd1",
751                                "/org/freedesktop/systemd1",
752                                "org.freedesktop.systemd1.Manager",
753                                "StartUnit",
754                                &error, &reply3,
755                                "ss", getty, "replace");
756         if (r < 0) {
757                 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
758                 return r;
759         }
760
761         container_bus = sd_bus_unref(container_bus);
762
763         assert_se(sigemptyset(&mask) == 0);
764         sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
765         assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
766
767         log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
768
769         r = process_pty(master, &mask, 0, 0);
770         if (r < 0) {
771                 log_error("Failed to process pseudo tty: %s", strerror(-r));
772                 return r;
773         }
774
775         fputc('\n', stdout);
776
777         log_info("Connection to container %s terminated.", args[1]);
778
779         return 0;
780 }
781
782 static void help(void) {
783         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
784                "Send control commands to or query the virtual machine and container registration manager.\n\n"
785                "  -h --help              Show this help\n"
786                "     --version           Show package version\n"
787                "     --no-pager          Do not pipe output into a pager\n"
788                "     --no-legend         Do not show the headers and footers\n"
789                "  -H --host=[USER@]HOST  Operate on remote host\n"
790                "  -M --machine=CONTAINER Operate on local container\n"
791                "  -p --property=NAME     Show only properties by this name\n"
792                "  -a --all               Show all properties, including empty ones\n"
793                "  -l --full              Do not ellipsize output\n"
794                "     --kill-who=WHO      Who to send signal to\n"
795                "  -s --signal=SIGNAL     Which signal to send\n\n"
796                "Commands:\n"
797                "  list                   List running VMs and containers\n"
798                "  status NAME...         Show VM/container status\n"
799                "  show NAME...           Show properties of one or more VMs/containers\n"
800                "  login NAME             Get a login prompt on a container\n"
801                "  poweroff NAME...       Power off one or more containers\n"
802                "  reboot NAME...         Reboot one or more containers\n"
803                "  kill NAME...           Send signal to processes of a VM/container\n"
804                "  terminate NAME...      Terminate one or more VMs/containers\n",
805                program_invocation_short_name);
806 }
807
808 static int parse_argv(int argc, char *argv[]) {
809
810         enum {
811                 ARG_VERSION = 0x100,
812                 ARG_NO_PAGER,
813                 ARG_NO_LEGEND,
814                 ARG_KILL_WHO,
815         };
816
817         static const struct option options[] = {
818                 { "help",            no_argument,       NULL, 'h'                 },
819                 { "version",         no_argument,       NULL, ARG_VERSION         },
820                 { "property",        required_argument, NULL, 'p'                 },
821                 { "all",             no_argument,       NULL, 'a'                 },
822                 { "full",            no_argument,       NULL, 'l'                 },
823                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
824                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
825                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
826                 { "signal",          required_argument, NULL, 's'                 },
827                 { "host",            required_argument, NULL, 'H'                 },
828                 { "machine",         required_argument, NULL, 'M'                 },
829                 {}
830         };
831
832         int c, r;
833
834         assert(argc >= 0);
835         assert(argv);
836
837         while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
838
839                 switch (c) {
840
841                 case 'h':
842                         help();
843                         return 0;
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         return 1;
907 }
908
909 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
910
911         static const struct {
912                 const char* verb;
913                 const enum {
914                         MORE,
915                         LESS,
916                         EQUAL
917                 } argc_cmp;
918                 const int argc;
919                 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
920         } verbs[] = {
921                 { "list",                  LESS,   1, list_machines     },
922                 { "status",                MORE,   2, show              },
923                 { "show",                  MORE,   1, show              },
924                 { "terminate",             MORE,   2, terminate_machine },
925                 { "reboot",                MORE,   2, reboot_machine    },
926                 { "poweroff",              MORE,   2, poweroff_machine  },
927                 { "kill",                  MORE,   2, kill_machine      },
928                 { "login",                 MORE,   2, login_machine     },
929         };
930
931         int left;
932         unsigned i;
933
934         assert(argc >= 0);
935         assert(argv);
936
937         left = argc - optind;
938
939         if (left <= 0)
940                 /* Special rule: no arguments means "list" */
941                 i = 0;
942         else {
943                 if (streq(argv[optind], "help")) {
944                         help();
945                         return 0;
946                 }
947
948                 for (i = 0; i < ELEMENTSOF(verbs); i++)
949                         if (streq(argv[optind], verbs[i].verb))
950                                 break;
951
952                 if (i >= ELEMENTSOF(verbs)) {
953                         log_error("Unknown operation %s", argv[optind]);
954                         return -EINVAL;
955                 }
956         }
957
958         switch (verbs[i].argc_cmp) {
959
960         case EQUAL:
961                 if (left != verbs[i].argc) {
962                         log_error("Invalid number of arguments.");
963                         return -EINVAL;
964                 }
965
966                 break;
967
968         case MORE:
969                 if (left < verbs[i].argc) {
970                         log_error("Too few arguments.");
971                         return -EINVAL;
972                 }
973
974                 break;
975
976         case LESS:
977                 if (left > verbs[i].argc) {
978                         log_error("Too many arguments.");
979                         return -EINVAL;
980                 }
981
982                 break;
983
984         default:
985                 assert_not_reached("Unknown comparison operator.");
986         }
987
988         return verbs[i].dispatch(bus, argv + optind, left);
989 }
990
991 int main(int argc, char*argv[]) {
992         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
993         int r;
994
995         setlocale(LC_ALL, "");
996         log_parse_environment();
997         log_open();
998
999         r = parse_argv(argc, argv);
1000         if (r <= 0)
1001                 goto finish;
1002
1003         r = bus_open_transport(arg_transport, arg_host, false, &bus);
1004         if (r < 0) {
1005                 log_error("Failed to create bus connection: %s", strerror(-r));
1006                 goto finish;
1007         }
1008
1009         r = machinectl_main(bus, argc, argv);
1010
1011 finish:
1012         pager_close();
1013
1014         strv_free(arg_property);
1015
1016         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1017 }