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