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