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