chiark / gitweb /
machinectl: make sure we are not reading an unitialized variable
[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_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 int help(void) {
783
784         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
785                "Send control commands to or query the virtual machine and container registration manager.\n\n"
786                "  -h --help              Show this help\n"
787                "     --version           Show package version\n"
788                "     --no-pager          Do not pipe output into a pager\n"
789                "     --no-legend         Do not show the headers and footers\n"
790                "  -H --host=[USER@]HOST  Operate on remote host\n"
791                "  -M --machine=CONTAINER Operate on local container\n"
792                "  -p --property=NAME     Show only properties by this name\n"
793                "  -a --all               Show all properties, including empty ones\n"
794                "  -l --full              Do not ellipsize output\n"
795                "     --kill-who=WHO      Who to send signal to\n"
796                "  -s --signal=SIGNAL     Which signal to send\n\n"
797                "Commands:\n"
798                "  list                   List running VMs and containers\n"
799                "  status NAME...         Show VM/container status\n"
800                "  show NAME...           Show properties of one or more VMs/containers\n"
801                "  login NAME             Get a login prompt on a container\n"
802                "  poweroff NAME...       Power off one or more containers\n"
803                "  reboot NAME...         Reboot one or more containers\n"
804                "  kill NAME...           Send signal to processes of a VM/container\n"
805                "  terminate NAME...      Terminate one or more VMs/containers\n",
806                program_invocation_short_name);
807
808         return 0;
809 }
810
811 static int parse_argv(int argc, char *argv[]) {
812
813         enum {
814                 ARG_VERSION = 0x100,
815                 ARG_NO_PAGER,
816                 ARG_NO_LEGEND,
817                 ARG_KILL_WHO,
818         };
819
820         static const struct option options[] = {
821                 { "help",            no_argument,       NULL, 'h'                 },
822                 { "version",         no_argument,       NULL, ARG_VERSION         },
823                 { "property",        required_argument, NULL, 'p'                 },
824                 { "all",             no_argument,       NULL, 'a'                 },
825                 { "full",            no_argument,       NULL, 'l'                 },
826                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
827                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
828                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
829                 { "signal",          required_argument, NULL, 's'                 },
830                 { "host",            required_argument, NULL, 'H'                 },
831                 { "machine",         required_argument, NULL, 'M'                 },
832                 {}
833         };
834
835         int c, r;
836
837         assert(argc >= 0);
838         assert(argv);
839
840         while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
841
842                 switch (c) {
843
844                 case 'h':
845                         return help();
846
847                 case ARG_VERSION:
848                         puts(PACKAGE_STRING);
849                         puts(SYSTEMD_FEATURES);
850                         return 0;
851
852                 case 'p':
853                         r = strv_extend(&arg_property, optarg);
854                         if (r < 0)
855                                 return log_oom();
856
857                         /* If the user asked for a particular
858                          * property, show it to him, even if it is
859                          * empty. */
860                         arg_all = true;
861                         break;
862
863                 case 'a':
864                         arg_all = true;
865                         break;
866
867                 case 'l':
868                         arg_full = true;
869                         break;
870
871                 case ARG_NO_PAGER:
872                         arg_no_pager = true;
873                         break;
874
875                 case ARG_NO_LEGEND:
876                         arg_legend = false;
877                         break;
878
879                 case ARG_KILL_WHO:
880                         arg_kill_who = optarg;
881                         break;
882
883                 case 's':
884                         arg_signal = signal_from_string_try_harder(optarg);
885                         if (arg_signal < 0) {
886                                 log_error("Failed to parse signal string %s.", optarg);
887                                 return -EINVAL;
888                         }
889                         break;
890
891                 case 'H':
892                         arg_transport = BUS_TRANSPORT_REMOTE;
893                         arg_host = optarg;
894                         break;
895
896                 case 'M':
897                         arg_transport = BUS_TRANSPORT_CONTAINER;
898                         arg_host = optarg;
899                         break;
900
901                 case '?':
902                         return -EINVAL;
903
904                 default:
905                         assert_not_reached("Unhandled option");
906                 }
907         }
908
909         return 1;
910 }
911
912 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
913
914         static const struct {
915                 const char* verb;
916                 const enum {
917                         MORE,
918                         LESS,
919                         EQUAL
920                 } argc_cmp;
921                 const int argc;
922                 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
923         } verbs[] = {
924                 { "list",                  LESS,   1, list_machines     },
925                 { "status",                MORE,   2, show              },
926                 { "show",                  MORE,   1, show              },
927                 { "terminate",             MORE,   2, terminate_machine },
928                 { "reboot",                MORE,   2, reboot_machine    },
929                 { "poweroff",              MORE,   2, poweroff_machine  },
930                 { "kill",                  MORE,   2, kill_machine      },
931                 { "login",                 MORE,   2, login_machine     },
932         };
933
934         int left;
935         unsigned i;
936
937         assert(argc >= 0);
938         assert(argv);
939
940         left = argc - optind;
941
942         if (left <= 0)
943                 /* Special rule: no arguments means "list" */
944                 i = 0;
945         else {
946                 if (streq(argv[optind], "help")) {
947                         help();
948                         return 0;
949                 }
950
951                 for (i = 0; i < ELEMENTSOF(verbs); i++)
952                         if (streq(argv[optind], verbs[i].verb))
953                                 break;
954
955                 if (i >= ELEMENTSOF(verbs)) {
956                         log_error("Unknown operation %s", argv[optind]);
957                         return -EINVAL;
958                 }
959         }
960
961         switch (verbs[i].argc_cmp) {
962
963         case EQUAL:
964                 if (left != verbs[i].argc) {
965                         log_error("Invalid number of arguments.");
966                         return -EINVAL;
967                 }
968
969                 break;
970
971         case MORE:
972                 if (left < verbs[i].argc) {
973                         log_error("Too few arguments.");
974                         return -EINVAL;
975                 }
976
977                 break;
978
979         case LESS:
980                 if (left > verbs[i].argc) {
981                         log_error("Too many arguments.");
982                         return -EINVAL;
983                 }
984
985                 break;
986
987         default:
988                 assert_not_reached("Unknown comparison operator.");
989         }
990
991         return verbs[i].dispatch(bus, argv + optind, left);
992 }
993
994 int main(int argc, char*argv[]) {
995         _cleanup_bus_unref_ sd_bus *bus = NULL;
996         int r;
997
998         setlocale(LC_ALL, "");
999         log_parse_environment();
1000         log_open();
1001
1002         r = parse_argv(argc, argv);
1003         if (r <= 0)
1004                 goto finish;
1005
1006         r = bus_open_transport(arg_transport, arg_host, false, &bus);
1007         if (r < 0) {
1008                 log_error("Failed to create bus connection: %s", strerror(-r));
1009                 goto finish;
1010         }
1011
1012         r = machinectl_main(bus, argc, argv);
1013
1014 finish:
1015         pager_close();
1016
1017         strv_free(arg_property);
1018
1019         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1020 }