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