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