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