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