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