]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machinectl.c
treewide: use log_*_errno whenever %m is in the format string
[thirdparty/systemd.git] / src / machine / machinectl.c
CommitLineData
1ee306e1
LP
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
8bdbb8d9 22#include <sys/socket.h>
1ee306e1
LP
23#include <unistd.h>
24#include <errno.h>
25#include <string.h>
26#include <getopt.h>
27#include <pwd.h>
28#include <locale.h>
04d39279 29#include <fcntl.h>
878cd7e9
LP
30#include <netinet/in.h>
31#include <arpa/inet.h>
f48e75cb 32#include <net/if.h>
1ee306e1 33
a1da8583 34#include "sd-bus.h"
1ee306e1
LP
35#include "log.h"
36#include "util.h"
37#include "macro.h"
38#include "pager.h"
a1da8583
TG
39#include "bus-util.h"
40#include "bus-error.h"
1ee306e1
LP
41#include "build.h"
42#include "strv.h"
aa1936ea 43#include "unit-name.h"
1ee306e1 44#include "cgroup-show.h"
9d127096 45#include "cgroup-util.h"
04d39279 46#include "ptyfwd.h"
023fb90b 47#include "event-util.h"
1ee306e1
LP
48
49static char **arg_property = NULL;
50static bool arg_all = false;
51static bool arg_full = false;
52static bool arg_no_pager = false;
e56056e9 53static bool arg_legend = true;
1ee306e1
LP
54static const char *arg_kill_who = NULL;
55static int arg_signal = SIGTERM;
d21ed1ea 56static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
1ee306e1 57static char *arg_host = NULL;
1ee306e1
LP
58
59static 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
a1da8583
TG
68static 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;
1ee306e1
LP
72 unsigned k = 0;
73 int r;
74
75 pager_open_if_enabled();
76
a1da8583
TG
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));
1ee306e1 88 return r;
1ee306e1
LP
89 }
90
e56056e9
TA
91 if (arg_legend)
92 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
1ee306e1 93
a1da8583
TG
94 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
95 if (r < 0)
5b30bef8 96 return bus_log_parse_error(r);
1ee306e1 97
a1da8583 98 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
1ee306e1
LP
99 printf("%-32s %-9s %-16s\n", name, class, service);
100
101 k++;
1ee306e1 102 }
a1da8583 103 if (r < 0)
5b30bef8 104 return bus_log_parse_error(r);
a1da8583
TG
105
106 r = sd_bus_message_exit_container(reply);
107 if (r < 0)
5b30bef8 108 return bus_log_parse_error(r);
1ee306e1 109
e56056e9
TA
110 if (arg_legend)
111 printf("\n%u machines listed.\n", k);
1ee306e1
LP
112
113 return 0;
114}
115
89f7c846 116static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
a1da8583
TG
117 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
118 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
aa1936ea 119 _cleanup_free_ char *path = NULL;
aa1936ea 120 const char *cgroup;
aa1936ea
LP
121 int r, output_flags;
122 unsigned c;
123
124 assert(bus);
125 assert(unit);
126
d21ed1ea 127 if (arg_transport == BUS_TRANSPORT_REMOTE)
aa1936ea
LP
128 return 0;
129
130 path = unit_dbus_path_from_name(unit);
131 if (!path)
132 return log_oom();
133
a7893c6b 134 r = sd_bus_get_property(
aa1936ea
LP
135 bus,
136 "org.freedesktop.systemd1",
137 path,
89f7c846 138 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
a7893c6b 139 "ControlGroup",
aa1936ea 140 &error,
a1da8583 141 &reply,
a7893c6b 142 "s");
aa1936ea 143 if (r < 0) {
a1da8583 144 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
aa1936ea
LP
145 return r;
146 }
147
a7893c6b 148 r = sd_bus_message_read(reply, "s", &cgroup);
5b30bef8
LP
149 if (r < 0)
150 return bus_log_parse_error(r);
aa1936ea 151
9d127096
LP
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
aa1936ea
LP
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
9d127096 168 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
aa1936ea
LP
169 return 0;
170}
171
f48e75cb 172static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
878cd7e9
LP
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
0dd25fb9 192 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
878cd7e9
LP
193 if (r < 0)
194 return bus_log_parse_error(r);
195
0dd25fb9
LP
196 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
197 int family;
878cd7e9
LP
198 const void *a;
199 size_t sz;
200 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
201
0dd25fb9 202 r = sd_bus_message_read(reply, "i", &family);
878cd7e9
LP
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
f48e75cb
LP
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);
878cd7e9
LP
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
717603e3
LP
233static 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
1ee306e1 275typedef struct MachineStatusInfo {
9f6eb1cd 276 char *name;
1ee306e1 277 sd_id128_t id;
9f6eb1cd
KS
278 char *class;
279 char *service;
89f7c846 280 char *unit;
9f6eb1cd 281 char *root_directory;
1ee306e1
LP
282 pid_t leader;
283 usec_t timestamp;
f48e75cb
LP
284 int *netif;
285 unsigned n_netif;
1ee306e1
LP
286} MachineStatusInfo;
287
a1da8583 288static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
1ee306e1
LP
289 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
290 char since2[FORMAT_TIMESTAMP_MAX], *s2;
f48e75cb
LP
291 int ifi = -1;
292
1ee306e1
LP
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
1ee306e1
LP
332 if (i->root_directory)
333 printf("\t Root: %s\n", i->root_directory);
334
f48e75cb
LP
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,
878cd7e9
LP
359 "\t Address: ",
360 "\t ");
361
717603e3
LP
362 print_os_release(bus, i->name, "\t OS: ");
363
89f7c846
LP
364 if (i->unit) {
365 printf("\t Unit: %s\n", i->unit);
366 show_unit_cgroup(bus, i->unit, i->leader);
1ee306e1
LP
367 }
368}
369
f48e75cb
LP
370static 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;
e7e9b6bb
ZJS
380 if (r == 0)
381 return -EBADMSG;
f48e75cb
LP
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
9f6eb1cd 391static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
a6c61602 392
9f6eb1cd 393 static const struct bus_properties_map map[] = {
f48e75cb
LP
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 },
9f6eb1cd
KS
403 {}
404 };
a6c61602
LP
405
406 MachineStatusInfo info = {};
a1da8583
TG
407 int r;
408
1ee306e1
LP
409 assert(path);
410 assert(new_line);
411
9f6eb1cd
KS
412 r = bus_map_all_properties(bus,
413 "org.freedesktop.machine1",
414 path,
415 map,
416 &info);
f647962d
MS
417 if (r < 0)
418 return log_error_errno(r, "Could not get properties: %m");
1ee306e1 419
1ee306e1
LP
420 if (*new_line)
421 printf("\n");
1ee306e1
LP
422 *new_line = true;
423
9f6eb1cd 424 print_machine_status_info(bus, &info);
1ee306e1 425
9f6eb1cd
KS
426 free(info.name);
427 free(info.class);
428 free(info.service);
89f7c846 429 free(info.unit);
9f6eb1cd 430 free(info.root_directory);
f48e75cb 431 free(info.netif);
1ee306e1 432
9f6eb1cd
KS
433 return r;
434}
1ee306e1 435
9f6eb1cd
KS
436static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
437 int r;
1ee306e1 438
9f6eb1cd
KS
439 if (*new_line)
440 printf("\n");
1ee306e1 441
9f6eb1cd 442 *new_line = true;
a1da8583 443
27e72d6b 444 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
a1da8583 445 if (r < 0)
da927ba9 446 log_error_errno(r, "Could not get properties: %m");
1ee306e1 447
a7893c6b 448 return r;
1ee306e1
LP
449}
450
a1da8583
TG
451static 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;
9f6eb1cd 454 int r = 0;
1ee306e1 455 unsigned i;
9f6eb1cd 456 bool properties, new_line = false;
1ee306e1
LP
457
458 assert(bus);
459 assert(args);
460
9f6eb1cd 461 properties = !strstr(args[0], "status");
1ee306e1
LP
462
463 pager_open_if_enabled();
464
9f6eb1cd 465 if (properties && n <= 1) {
a1da8583 466
9f6eb1cd 467 /* If no argument is specified, inspect the manager
1ee306e1 468 * itself */
9f6eb1cd 469 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
8c841f21 470 if (r < 0)
9f6eb1cd 471 return r;
1ee306e1
LP
472 }
473
474 for (i = 1; i < n; i++) {
475 const char *path = NULL;
476
a1da8583
TG
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);
5b30bef8
LP
492 if (r < 0)
493 return bus_log_parse_error(r);
1ee306e1 494
9f6eb1cd
KS
495 if (properties)
496 r = show_properties(bus, path, &new_line);
497 else
498 r = show_info(args[0], bus, path, &new_line);
1ee306e1
LP
499 }
500
9f6eb1cd 501 return r;
1ee306e1
LP
502}
503
a1da8583
TG
504static int kill_machine(sd_bus *bus, char **args, unsigned n) {
505 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1ee306e1
LP
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
a1da8583
TG
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));
1ee306e1 527 return r;
a1da8583 528 }
1ee306e1
LP
529 }
530
531 return 0;
532}
533
1dba654b
LP
534static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
535 arg_kill_who = "leader";
536 arg_signal = SIGINT; /* sysvinit + systemd */
1ee306e1 537
1dba654b
LP
538 return kill_machine(bus, args, n);
539}
1ee306e1 540
1dba654b
LP
541static int poweroff_machine(sd_bus *bus, char **args, unsigned n) {
542 arg_kill_who = "leader";
543 arg_signal = SIGRTMIN+4; /* only systemd */
1ee306e1 544
1dba654b 545 return kill_machine(bus, args, n);
1ee306e1
LP
546}
547
1dba654b 548static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
923d8fd3
LP
549 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
550 unsigned i;
923d8fd3
LP
551
552 assert(args);
553
923d8fd3 554 for (i = 1; i < n; i++) {
1dba654b 555 int r;
923d8fd3
LP
556
557 r = sd_bus_call_method(
558 bus,
559 "org.freedesktop.machine1",
560 "/org/freedesktop/machine1",
561 "org.freedesktop.machine1.Manager",
1dba654b 562 "TerminateMachine",
923d8fd3 563 &error,
1dba654b 564 NULL,
923d8fd3 565 "s", args[i]);
923d8fd3 566 if (r < 0) {
1dba654b 567 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
923d8fd3
LP
568 return r;
569 }
923d8fd3
LP
570 }
571
572 return 0;
573}
574
04d39279 575static int openpt_in_namespace(pid_t pid, int flags) {
3d94f76c 576 _cleanup_close_pair_ int pair[2] = { -1, -1 };
a4475f57 577 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
04d39279
LP
578 union {
579 struct cmsghdr cmsghdr;
580 uint8_t buf[CMSG_SPACE(sizeof(int))];
34a6778f
ZJS
581 } control = {};
582 struct msghdr mh = {
583 .msg_control = &control,
584 .msg_controllen = sizeof(control),
585 };
04d39279 586 struct cmsghdr *cmsg;
f69157a6 587 int master = -1, r;
04d39279
LP
588 pid_t child;
589 siginfo_t si;
590
878cd7e9 591 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
bc9fd78c
LP
592 if (r < 0)
593 return r;
04d39279 594
bc9fd78c 595 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
04d39279
LP
596 return -errno;
597
04d39279
LP
598 child = fork();
599 if (child < 0)
600 return -errno;
601
602 if (child == 0) {
03e334a1 603 pair[0] = safe_close(pair[0]);
04d39279 604
878cd7e9 605 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
04d39279
LP
606 if (r < 0)
607 _exit(EXIT_FAILURE);
608
04d39279
LP
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
bc9fd78c 621 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
04d39279
LP
622 _exit(EXIT_FAILURE);
623
624 _exit(EXIT_SUCCESS);
625 }
626
03e334a1 627 pair[1] = safe_close(pair[1]);
04d39279 628
fbadf045 629 r = wait_for_terminate(child, &si);
878cd7e9
LP
630 if (r < 0)
631 return r;
632 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
633 return -EIO;
fbadf045 634
bc9fd78c 635 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
04d39279
LP
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
6261f11f
LP
654 if (master < 0)
655 return -EIO;
656
04d39279
LP
657 return master;
658}
659
660static 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;
24996861 663 _cleanup_bus_close_unref_ sd_bus *container_bus = NULL;
023fb90b
LP
664 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
665 _cleanup_event_unref_ sd_event *event = NULL;
04d39279
LP
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;
023fb90b 671 int r, ret = 0;
04d39279
LP
672
673 assert(bus);
674 assert(args);
675
676 if (arg_transport != BUS_TRANSPORT_LOCAL) {
923d8fd3 677 log_error("Login only supported on local machines.");
04d39279
LP
678 return -ENOTSUP;
679 }
680
023fb90b 681 r = sd_event_default(&event);
f647962d
MS
682 if (r < 0)
683 return log_error_errno(r, "Failed to get event loop: %m");
023fb90b
LP
684
685 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
686 if (r < 0)
687 return log_error_errno(r, "Failed to attach bus to event loop: %m");
023fb90b 688
04d39279
LP
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);
5b30bef8
LP
704 if (r < 0)
705 return bus_log_parse_error(r);
04d39279
LP
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");
f647962d
MS
716 if (r < 0)
717 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
04d39279
LP
718
719 r = sd_bus_message_read(reply2, "u", &leader);
5b30bef8
LP
720 if (r < 0)
721 return bus_log_parse_error(r);
04d39279
LP
722
723 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
f647962d
MS
724 if (master < 0)
725 return log_error_errno(master, "Failed to acquire pseudo tty: %m");
04d39279
LP
726
727 pty = ptsname(master);
728 if (!pty) {
56f64d95 729 log_error_errno(errno, "Failed to get pty name: %m");
04d39279
LP
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
3db729cb 739 r = sd_bus_open_system_container(&container_bus, args[1]);
f647962d
MS
740 if (r < 0)
741 return log_error_errno(r, "Failed to get container bus: %m");
04d39279
LP
742
743 getty = strjoin("container-getty@", p, ".service", NULL);
744 if (!getty)
745 return log_oom();
746
747 if (unlockpt(master) < 0) {
56f64d95 748 log_error_errno(errno, "Failed to unlock tty: %m");
04d39279
LP
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
a6c61602
LP
764 container_bus = sd_bus_unref(container_bus);
765
04d39279
LP
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
023fb90b
LP
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);
f647962d
MS
776 if (r < 0)
777 return log_error_errno(r, "Failed to create PTY forwarder: %m");
023fb90b
LP
778
779 r = sd_event_loop(event);
f647962d
MS
780 if (r < 0)
781 return log_error_errno(r, "Failed to run event loop: %m");
04d39279 782
023fb90b
LP
783 forward = pty_forward_free(forward);
784
04d39279
LP
785 fputc('\n', stdout);
786
787 log_info("Connection to container %s terminated.", args[1]);
788
023fb90b
LP
789 sd_event_get_exit_code(event, &ret);
790 return ret;
04d39279
LP
791}
792
601185b4 793static void help(void) {
1ee306e1
LP
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"
a7893c6b 798 " --no-pager Do not pipe output into a pager\n"
e56056e9 799 " --no-legend Do not show the headers and footers\n"
53755121
LP
800 " -H --host=[USER@]HOST Operate on remote host\n"
801 " -M --machine=CONTAINER Operate on local container\n"
1ee306e1
LP
802 " -p --property=NAME Show only properties by this name\n"
803 " -a --all Show all properties, including empty ones\n"
1ee306e1 804 " -l --full Do not ellipsize output\n"
a7893c6b
LP
805 " --kill-who=WHO Who to send signal to\n"
806 " -s --signal=SIGNAL Which signal to send\n\n"
1ee306e1
LP
807 "Commands:\n"
808 " list List running VMs and containers\n"
4f8f66cb
ZJS
809 " status NAME... Show VM/container status\n"
810 " show NAME... Show properties of one or more VMs/containers\n"
1dba654b
LP
811 " login NAME Get a login prompt on a container\n"
812 " poweroff NAME... Power off one or more containers\n"
923d8fd3 813 " reboot NAME... Reboot one or more containers\n"
1dba654b
LP
814 " kill NAME... Send signal to processes of a VM/container\n"
815 " terminate NAME... Terminate one or more VMs/containers\n",
1ee306e1 816 program_invocation_short_name);
1ee306e1
LP
817}
818
819static int parse_argv(int argc, char *argv[]) {
820
821 enum {
822 ARG_VERSION = 0x100,
823 ARG_NO_PAGER,
e56056e9 824 ARG_NO_LEGEND,
1ee306e1 825 ARG_KILL_WHO,
1ee306e1
LP
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 },
e56056e9 835 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1ee306e1
LP
836 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
837 { "signal", required_argument, NULL, 's' },
838 { "host", required_argument, NULL, 'H' },
a7893c6b 839 { "machine", required_argument, NULL, 'M' },
eb9da376 840 {}
1ee306e1
LP
841 };
842
a7893c6b 843 int c, r;
1ee306e1
LP
844
845 assert(argc >= 0);
846 assert(argv);
847
601185b4 848 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1ee306e1
LP
849
850 switch (c) {
851
852 case 'h':
601185b4
ZJS
853 help();
854 return 0;
1ee306e1
LP
855
856 case ARG_VERSION:
857 puts(PACKAGE_STRING);
858 puts(SYSTEMD_FEATURES);
859 return 0;
860
a7893c6b
LP
861 case 'p':
862 r = strv_extend(&arg_property, optarg);
863 if (r < 0)
864 return log_oom();
1ee306e1
LP
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;
1ee306e1
LP
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
e56056e9
TA
884 case ARG_NO_LEGEND:
885 arg_legend = false;
886 break;
887
1ee306e1
LP
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
1ee306e1 900 case 'H':
d21ed1ea 901 arg_transport = BUS_TRANSPORT_REMOTE;
a7893c6b
LP
902 arg_host = optarg;
903 break;
904
905 case 'M':
d21ed1ea 906 arg_transport = BUS_TRANSPORT_CONTAINER;
a7893c6b 907 arg_host = optarg;
1ee306e1
LP
908 break;
909
910 case '?':
911 return -EINVAL;
912
913 default:
eb9da376 914 assert_not_reached("Unhandled option");
1ee306e1 915 }
1ee306e1
LP
916
917 return 1;
918}
919
04d39279 920static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
1ee306e1
LP
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;
a1da8583 930 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
1ee306e1
LP
931 } verbs[] = {
932 { "list", LESS, 1, list_machines },
933 { "status", MORE, 2, show },
934 { "show", MORE, 1, show },
935 { "terminate", MORE, 2, terminate_machine },
923d8fd3 936 { "reboot", MORE, 2, reboot_machine },
1dba654b 937 { "poweroff", MORE, 2, poweroff_machine },
1ee306e1 938 { "kill", MORE, 2, kill_machine },
04d39279 939 { "login", MORE, 2, login_machine },
1ee306e1
LP
940 };
941
942 int left;
943 unsigned i;
944
945 assert(argc >= 0);
946 assert(argv);
1ee306e1
LP
947
948 left = argc - optind;
949
950 if (left <= 0)
46e65dcc 951 /* Special rule: no arguments means "list" */
1ee306e1
LP
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
1ee306e1
LP
999 return verbs[i].dispatch(bus, argv + optind, left);
1000}
1001
1002int main(int argc, char*argv[]) {
24996861 1003 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
84f6181c 1004 int r;
1ee306e1
LP
1005
1006 setlocale(LC_ALL, "");
1007 log_parse_environment();
1008 log_open();
1009
1010 r = parse_argv(argc, argv);
84f6181c 1011 if (r <= 0)
1ee306e1 1012 goto finish;
1ee306e1 1013
d21ed1ea 1014 r = bus_open_transport(arg_transport, arg_host, false, &bus);
a1da8583 1015 if (r < 0) {
da927ba9 1016 log_error_errno(r, "Failed to create bus connection: %m");
a1da8583
TG
1017 goto finish;
1018 }
1ee306e1 1019
04d39279 1020 r = machinectl_main(bus, argc, argv);
1ee306e1
LP
1021
1022finish:
1ee306e1
LP
1023 pager_close();
1024
84f6181c
LP
1025 strv_free(arg_property);
1026
1027 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1ee306e1 1028}