]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machinectl.c
machinectl: add new "start" verb to start a container as a service in nspawn
[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>
785890ac 33#include <sys/mount.h>
f2cbe59e 34#include <libgen.h>
1ee306e1 35
a1da8583 36#include "sd-bus.h"
1ee306e1
LP
37#include "log.h"
38#include "util.h"
39#include "macro.h"
40#include "pager.h"
a1da8583
TG
41#include "bus-util.h"
42#include "bus-error.h"
1ee306e1
LP
43#include "build.h"
44#include "strv.h"
aa1936ea 45#include "unit-name.h"
1ee306e1 46#include "cgroup-show.h"
9d127096 47#include "cgroup-util.h"
04d39279 48#include "ptyfwd.h"
023fb90b 49#include "event-util.h"
785890ac
LP
50#include "path-util.h"
51#include "mkdir.h"
f2cbe59e 52#include "copy.h"
56159e0d 53#include "verbs.h"
1ee306e1
LP
54
55static char **arg_property = NULL;
56static bool arg_all = false;
57static bool arg_full = false;
58static bool arg_no_pager = false;
e56056e9 59static bool arg_legend = true;
1ee306e1
LP
60static const char *arg_kill_who = NULL;
61static int arg_signal = SIGTERM;
d21ed1ea 62static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
1ee306e1 63static char *arg_host = NULL;
785890ac
LP
64static bool arg_read_only = false;
65static bool arg_mkdir = false;
1ee306e1
LP
66
67static void pager_open_if_enabled(void) {
68
69 /* Cache result before we open the pager */
70 if (arg_no_pager)
71 return;
72
73 pager_open(false);
74}
75
56159e0d
LP
76static int list_machines(int argc, char *argv[], void *userdata) {
77
a1da8583
TG
78 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
79 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
80 const char *name, *class, *service, *object;
56159e0d 81 sd_bus *bus = userdata;
1ee306e1
LP
82 unsigned k = 0;
83 int r;
84
56159e0d
LP
85 assert(bus);
86
1ee306e1
LP
87 pager_open_if_enabled();
88
a1da8583
TG
89 r = sd_bus_call_method(
90 bus,
91 "org.freedesktop.machine1",
92 "/org/freedesktop/machine1",
93 "org.freedesktop.machine1.Manager",
94 "ListMachines",
95 &error,
96 &reply,
97 "");
98 if (r < 0) {
99 log_error("Could not get machines: %s", bus_error_message(&error, -r));
1ee306e1 100 return r;
1ee306e1
LP
101 }
102
e56056e9
TA
103 if (arg_legend)
104 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
1ee306e1 105
a1da8583
TG
106 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
107 if (r < 0)
5b30bef8 108 return bus_log_parse_error(r);
1ee306e1 109
a1da8583 110 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
1ee306e1
LP
111 printf("%-32s %-9s %-16s\n", name, class, service);
112
113 k++;
1ee306e1 114 }
a1da8583 115 if (r < 0)
5b30bef8 116 return bus_log_parse_error(r);
a1da8583
TG
117
118 r = sd_bus_message_exit_container(reply);
119 if (r < 0)
5b30bef8 120 return bus_log_parse_error(r);
1ee306e1 121
e56056e9
TA
122 if (arg_legend)
123 printf("\n%u machines listed.\n", k);
1ee306e1
LP
124
125 return 0;
126}
127
cd61c3bf
LP
128typedef struct ImageInfo {
129 const char *name;
130 const char *type;
131 bool read_only;
10f9c755
LP
132 usec_t crtime;
133 usec_t mtime;
b6b18498 134 uint64_t size;
cd61c3bf
LP
135} ImageInfo;
136
137static int compare_image_info(const void *a, const void *b) {
138 const ImageInfo *x = a, *y = b;
139
140 return strcmp(x->name, y->name);
141}
142
56159e0d 143static int list_images(int argc, char *argv[], void *userdata) {
cd61c3bf
LP
144
145 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
b6b18498 146 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("SIZE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
cd61c3bf
LP
147 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
148 _cleanup_free_ ImageInfo *images = NULL;
149 size_t n_images = 0, n_allocated = 0, j;
150 const char *name, *type, *object;
56159e0d 151 sd_bus *bus = userdata;
b6b18498 152 uint64_t crtime, mtime, size;
10f9c755 153 int read_only, r;
cd61c3bf 154
56159e0d
LP
155 assert(bus);
156
cd61c3bf
LP
157 pager_open_if_enabled();
158
159 r = sd_bus_call_method(
160 bus,
161 "org.freedesktop.machine1",
162 "/org/freedesktop/machine1",
163 "org.freedesktop.machine1.Manager",
164 "ListImages",
165 &error,
166 &reply,
167 "");
168 if (r < 0) {
169 log_error("Could not get images: %s", bus_error_message(&error, -r));
170 return r;
171 }
172
b6b18498 173 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
cd61c3bf
LP
174 if (r < 0)
175 return bus_log_parse_error(r);
176
b6b18498
LP
177 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
178 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
10f9c755 179 size_t l;
cd61c3bf
LP
180
181 if (name[0] == '.' && !arg_all)
182 continue;
183
184 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
185 return log_oom();
186
187 images[n_images].name = name;
188 images[n_images].type = type;
189 images[n_images].read_only = read_only;
10f9c755
LP
190 images[n_images].crtime = crtime;
191 images[n_images].mtime = mtime;
b6b18498 192 images[n_images].size = size;
10f9c755
LP
193
194 l = strlen(name);
195 if (l > max_name)
196 max_name = l;
cd61c3bf 197
10f9c755
LP
198 l = strlen(type);
199 if (l > max_type)
200 max_type = l;
cd61c3bf 201
10f9c755 202 if (crtime != 0) {
b6b18498 203 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
10f9c755
LP
204 if (l > max_crtime)
205 max_crtime = l;
206 }
207
208 if (mtime != 0) {
b6b18498 209 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
10f9c755
LP
210 if (l > max_mtime)
211 max_mtime = l;
212 }
cd61c3bf 213
b6b18498
LP
214 if (size != (uint64_t) -1) {
215 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
216 if (l > max_size)
217 max_size = l;
218 }
219
cd61c3bf
LP
220 n_images++;
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 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
230
231 if (arg_legend)
b6b18498 232 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
10f9c755
LP
233 (int) max_name, "NAME",
234 (int) max_type, "TYPE",
235 "RO",
b6b18498 236 (int) max_size, "SIZE",
10f9c755
LP
237 (int) max_crtime, "CREATED",
238 (int) max_mtime, "MODIFIED");
cd61c3bf
LP
239
240 for (j = 0; j < n_images; j++) {
b6b18498 241 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
10f9c755 242
b6b18498 243 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
cd61c3bf
LP
244 (int) max_name, images[j].name,
245 (int) max_type, images[j].type,
8937e7b6 246 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
b6b18498
LP
247 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
248 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
249 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
cd61c3bf
LP
250 }
251
252 if (r < 0)
253 return bus_log_parse_error(r);
254
255
256 if (arg_legend)
257 printf("\n%zu images listed.\n", n_images);
258
259 return 0;
260}
261
89f7c846 262static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
a1da8583
TG
263 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
264 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
aa1936ea 265 _cleanup_free_ char *path = NULL;
aa1936ea 266 const char *cgroup;
aa1936ea
LP
267 int r, output_flags;
268 unsigned c;
269
270 assert(bus);
271 assert(unit);
272
d21ed1ea 273 if (arg_transport == BUS_TRANSPORT_REMOTE)
aa1936ea
LP
274 return 0;
275
276 path = unit_dbus_path_from_name(unit);
277 if (!path)
278 return log_oom();
279
a7893c6b 280 r = sd_bus_get_property(
aa1936ea
LP
281 bus,
282 "org.freedesktop.systemd1",
283 path,
89f7c846 284 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
a7893c6b 285 "ControlGroup",
aa1936ea 286 &error,
a1da8583 287 &reply,
a7893c6b 288 "s");
aa1936ea 289 if (r < 0) {
a1da8583 290 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
aa1936ea
LP
291 return r;
292 }
293
a7893c6b 294 r = sd_bus_message_read(reply, "s", &cgroup);
5b30bef8
LP
295 if (r < 0)
296 return bus_log_parse_error(r);
aa1936ea 297
9d127096
LP
298 if (isempty(cgroup))
299 return 0;
300
301 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
302 return 0;
303
aa1936ea
LP
304 output_flags =
305 arg_all * OUTPUT_SHOW_ALL |
306 arg_full * OUTPUT_FULL_WIDTH;
307
308 c = columns();
309 if (c > 18)
310 c -= 18;
311 else
312 c = 0;
313
9d127096 314 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
aa1936ea
LP
315 return 0;
316}
317
f48e75cb 318static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
878cd7e9
LP
319 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
320 int r;
321
322 assert(bus);
323 assert(name);
324 assert(prefix);
325 assert(prefix2);
326
327 r = sd_bus_call_method(bus,
328 "org.freedesktop.machine1",
329 "/org/freedesktop/machine1",
330 "org.freedesktop.machine1.Manager",
331 "GetMachineAddresses",
332 NULL,
333 &reply,
334 "s", name);
335 if (r < 0)
336 return r;
337
0dd25fb9 338 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
878cd7e9
LP
339 if (r < 0)
340 return bus_log_parse_error(r);
341
0dd25fb9
LP
342 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
343 int family;
878cd7e9
LP
344 const void *a;
345 size_t sz;
346 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
347
0dd25fb9 348 r = sd_bus_message_read(reply, "i", &family);
878cd7e9
LP
349 if (r < 0)
350 return bus_log_parse_error(r);
351
352 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
353 if (r < 0)
354 return bus_log_parse_error(r);
355
f48e75cb
LP
356 fputs(prefix, stdout);
357 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
358 if (family == AF_INET6 && ifi > 0)
359 printf("%%%i", ifi);
360 fputc('\n', stdout);
878cd7e9
LP
361
362 r = sd_bus_message_exit_container(reply);
363 if (r < 0)
364 return bus_log_parse_error(r);
365
366 if (prefix != prefix2)
367 prefix = prefix2;
368 }
369 if (r < 0)
370 return bus_log_parse_error(r);
371
372 r = sd_bus_message_exit_container(reply);
373 if (r < 0)
374 return bus_log_parse_error(r);
375
376 return 0;
377}
378
717603e3
LP
379static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
380 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
381 const char *k, *v, *pretty = NULL;
382 int r;
383
384 assert(bus);
385 assert(name);
386 assert(prefix);
387
388 r = sd_bus_call_method(bus,
389 "org.freedesktop.machine1",
390 "/org/freedesktop/machine1",
391 "org.freedesktop.machine1.Manager",
392 "GetMachineOSRelease",
393 NULL,
394 &reply,
395 "s", name);
396 if (r < 0)
397 return r;
398
399 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
400 if (r < 0)
401 return bus_log_parse_error(r);
402
403 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
404 if (streq(k, "PRETTY_NAME"))
405 pretty = v;
406
407 }
408 if (r < 0)
409 return bus_log_parse_error(r);
410
411 r = sd_bus_message_exit_container(reply);
412 if (r < 0)
413 return bus_log_parse_error(r);
414
415 if (pretty)
416 printf("%s%s\n", prefix, pretty);
417
418 return 0;
419}
420
1ee306e1 421typedef struct MachineStatusInfo {
9f6eb1cd 422 char *name;
1ee306e1 423 sd_id128_t id;
9f6eb1cd
KS
424 char *class;
425 char *service;
89f7c846 426 char *unit;
9f6eb1cd 427 char *root_directory;
1ee306e1
LP
428 pid_t leader;
429 usec_t timestamp;
f48e75cb
LP
430 int *netif;
431 unsigned n_netif;
1ee306e1
LP
432} MachineStatusInfo;
433
a1da8583 434static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
1ee306e1
LP
435 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
436 char since2[FORMAT_TIMESTAMP_MAX], *s2;
f48e75cb
LP
437 int ifi = -1;
438
56159e0d 439 assert(bus);
1ee306e1
LP
440 assert(i);
441
442 fputs(strna(i->name), stdout);
443
444 if (!sd_id128_equal(i->id, SD_ID128_NULL))
445 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
446 else
447 putchar('\n');
448
449 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
450 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
451
452 if (s1)
453 printf("\t Since: %s; %s\n", s2, s1);
454 else if (s2)
455 printf("\t Since: %s\n", s2);
456
457 if (i->leader > 0) {
458 _cleanup_free_ char *t = NULL;
459
460 printf("\t Leader: %u", (unsigned) i->leader);
461
462 get_process_comm(i->leader, &t);
463 if (t)
464 printf(" (%s)", t);
465
466 putchar('\n');
467 }
468
469 if (i->service) {
470 printf("\t Service: %s", i->service);
471
472 if (i->class)
473 printf("; class %s", i->class);
474
475 putchar('\n');
476 } else if (i->class)
477 printf("\t Class: %s\n", i->class);
478
1ee306e1
LP
479 if (i->root_directory)
480 printf("\t Root: %s\n", i->root_directory);
481
f48e75cb
LP
482 if (i->n_netif > 0) {
483 unsigned c;
484
485 fputs("\t Iface:", stdout);
486
487 for (c = 0; c < i->n_netif; c++) {
488 char name[IF_NAMESIZE+1] = "";
489
490 if (if_indextoname(i->netif[c], name)) {
491 fputc(' ', stdout);
492 fputs(name, stdout);
493
494 if (ifi < 0)
495 ifi = i->netif[c];
496 else
497 ifi = 0;
498 } else
499 printf(" %i", i->netif[c]);
500 }
501
502 fputc('\n', stdout);
503 }
504
505 print_addresses(bus, i->name, ifi,
878cd7e9
LP
506 "\t Address: ",
507 "\t ");
508
717603e3
LP
509 print_os_release(bus, i->name, "\t OS: ");
510
89f7c846
LP
511 if (i->unit) {
512 printf("\t Unit: %s\n", i->unit);
513 show_unit_cgroup(bus, i->unit, i->leader);
1ee306e1
LP
514 }
515}
516
f48e75cb
LP
517static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
518 MachineStatusInfo *i = userdata;
519 size_t l;
520 const void *v;
521 int r;
522
523 assert_cc(sizeof(int32_t) == sizeof(int));
524 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
525 if (r < 0)
526 return r;
e7e9b6bb
ZJS
527 if (r == 0)
528 return -EBADMSG;
f48e75cb
LP
529
530 i->n_netif = l / sizeof(int32_t);
531 i->netif = memdup(v, l);
532 if (!i->netif)
533 return -ENOMEM;
534
535 return 0;
536}
537
fefdc04b 538static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
a6c61602 539
9f6eb1cd 540 static const struct bus_properties_map map[] = {
f48e75cb
LP
541 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
542 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
543 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
544 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
545 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
546 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
547 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
548 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
549 { "NetworkInterfaces", "ai", map_netif, 0 },
9f6eb1cd
KS
550 {}
551 };
a6c61602
LP
552
553 MachineStatusInfo info = {};
a1da8583
TG
554 int r;
555
56159e0d
LP
556 assert(verb);
557 assert(bus);
1ee306e1
LP
558 assert(path);
559 assert(new_line);
560
9f6eb1cd
KS
561 r = bus_map_all_properties(bus,
562 "org.freedesktop.machine1",
563 path,
564 map,
565 &info);
f647962d
MS
566 if (r < 0)
567 return log_error_errno(r, "Could not get properties: %m");
1ee306e1 568
1ee306e1
LP
569 if (*new_line)
570 printf("\n");
1ee306e1
LP
571 *new_line = true;
572
9f6eb1cd 573 print_machine_status_info(bus, &info);
1ee306e1 574
9f6eb1cd
KS
575 free(info.name);
576 free(info.class);
577 free(info.service);
89f7c846 578 free(info.unit);
9f6eb1cd 579 free(info.root_directory);
f48e75cb 580 free(info.netif);
1ee306e1 581
9f6eb1cd
KS
582 return r;
583}
1ee306e1 584
fefdc04b 585static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
9f6eb1cd 586 int r;
1ee306e1 587
56159e0d
LP
588 assert(bus);
589 assert(path);
590 assert(new_line);
591
9f6eb1cd
KS
592 if (*new_line)
593 printf("\n");
1ee306e1 594
9f6eb1cd 595 *new_line = true;
a1da8583 596
27e72d6b 597 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
a1da8583 598 if (r < 0)
da927ba9 599 log_error_errno(r, "Could not get properties: %m");
1ee306e1 600
a7893c6b 601 return r;
1ee306e1
LP
602}
603
fefdc04b 604static int show_machine(int argc, char *argv[], void *userdata) {
56159e0d 605
a1da8583 606 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d 607 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
9f6eb1cd 608 bool properties, new_line = false;
56159e0d
LP
609 sd_bus *bus = userdata;
610 int r = 0, i;
1ee306e1
LP
611
612 assert(bus);
1ee306e1 613
56159e0d 614 properties = !strstr(argv[0], "status");
1ee306e1
LP
615
616 pager_open_if_enabled();
617
56159e0d 618 if (properties && argc <= 1) {
a1da8583 619
9f6eb1cd 620 /* If no argument is specified, inspect the manager
1ee306e1 621 * itself */
fefdc04b 622 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
8c841f21 623 if (r < 0)
9f6eb1cd 624 return r;
1ee306e1
LP
625 }
626
56159e0d 627 for (i = 1; i < argc; i++) {
1ee306e1
LP
628 const char *path = NULL;
629
a1da8583
TG
630 r = sd_bus_call_method(
631 bus,
632 "org.freedesktop.machine1",
633 "/org/freedesktop/machine1",
634 "org.freedesktop.machine1.Manager",
635 "GetMachine",
636 &error,
637 &reply,
56159e0d 638 "s", argv[i]);
a1da8583
TG
639 if (r < 0) {
640 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
641 return r;
642 }
643
644 r = sd_bus_message_read(reply, "o", &path);
5b30bef8
LP
645 if (r < 0)
646 return bus_log_parse_error(r);
1ee306e1 647
9f6eb1cd 648 if (properties)
fefdc04b 649 r = show_machine_properties(bus, path, &new_line);
9f6eb1cd 650 else
fefdc04b
LP
651 r = show_machine_info(argv[0], bus, path, &new_line);
652 }
653
654 return r;
655}
656
657typedef struct ImageStatusInfo {
658 char *name;
659 char *path;
660 char *type;
661 int read_only;
662 usec_t crtime;
663 usec_t mtime;
b6b18498
LP
664 uint64_t size;
665 uint64_t limit;
666 uint64_t size_exclusive;
667 uint64_t limit_exclusive;
fefdc04b
LP
668} ImageStatusInfo;
669
670static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
671 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
672 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
b6b18498
LP
673 char bs[FORMAT_BYTES_MAX], *s3;
674 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
fefdc04b
LP
675
676 assert(bus);
677 assert(i);
678
679 if (i->name) {
680 fputs(i->name, stdout);
681 putchar('\n');
682 }
683
684 if (i->path)
685 printf("\t Type: %s\n", i->type);
686
687 if (i->path)
688 printf("\t Path: %s\n", i->path);
689
690 printf("\t RO: %s%s%s\n",
691 i->read_only ? ansi_highlight_red() : "",
692 i->read_only ? "read-only" : "writable",
693 i->read_only ? ansi_highlight_off() : "");
694
695 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
696 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
b6b18498 697 if (s1 && s2)
fefdc04b
LP
698 printf("\t Created: %s; %s\n", s2, s1);
699 else if (s2)
700 printf("\t Created: %s\n", s2);
701
702 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
703 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
b6b18498 704 if (s1 && s2)
fefdc04b
LP
705 printf("\tModified: %s; %s\n", s2, s1);
706 else if (s2)
707 printf("\tModified: %s\n", s2);
b6b18498
LP
708
709 s3 = format_bytes(bs, sizeof(bs), i->size);
710 s4 = i->size_exclusive != i->size ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->size_exclusive) : NULL;
711 if (s3 && s4)
712 printf("\t Size: %s (exclusive: %s)\n", s3, s4);
713 else if (s3)
714 printf("\t Size: %s\n", s3);
715
716 s3 = format_bytes(bs, sizeof(bs), i->limit);
717 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
718 if (s3 && s4)
719 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
720 else if (s3)
721 printf("\t Limit: %s\n", s3);
fefdc04b
LP
722}
723
724static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
725
726 static const struct bus_properties_map map[] = {
b6b18498
LP
727 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
728 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
729 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
730 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
731 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
732 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
733 { "Size", "t", NULL, offsetof(ImageStatusInfo, size) },
734 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
735 { "SizeExclusive", "t", NULL, offsetof(ImageStatusInfo, size_exclusive) },
736 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
fefdc04b
LP
737 {}
738 };
739
740 ImageStatusInfo info = {};
741 int r;
742
743 assert(verb);
744 assert(bus);
745 assert(path);
746 assert(new_line);
747
748 r = bus_map_all_properties(bus,
749 "org.freedesktop.machine1",
750 path,
751 map,
752 &info);
753 if (r < 0)
754 return log_error_errno(r, "Could not get properties: %m");
755
756 if (*new_line)
757 printf("\n");
758 *new_line = true;
759
760 print_image_status_info(bus, &info);
761
762 free(info.name);
763 free(info.path);
764 free(info.type);
765
766 return r;
767}
768
769static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
770 int r;
771
772 assert(bus);
773 assert(path);
774 assert(new_line);
775
776 if (*new_line)
777 printf("\n");
778
779 *new_line = true;
780
781 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
782 if (r < 0)
783 log_error_errno(r, "Could not get properties: %m");
784
785 return r;
786}
787
788static int show_image(int argc, char *argv[], void *userdata) {
789
790 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
791 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
792 bool properties, new_line = false;
793 sd_bus *bus = userdata;
794 int r = 0, i;
795
796 assert(bus);
797
798 properties = !strstr(argv[0], "status");
799
800 pager_open_if_enabled();
801
802 if (properties && argc <= 1) {
803
804 /* If no argument is specified, inspect the manager
805 * itself */
806 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
807 if (r < 0)
808 return r;
809 }
810
811 for (i = 1; i < argc; i++) {
812 const char *path = NULL;
813
814 r = sd_bus_call_method(
815 bus,
816 "org.freedesktop.machine1",
817 "/org/freedesktop/machine1",
818 "org.freedesktop.machine1.Manager",
819 "GetImage",
820 &error,
821 &reply,
822 "s", argv[i]);
823 if (r < 0) {
824 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
825 return r;
826 }
827
828 r = sd_bus_message_read(reply, "o", &path);
829 if (r < 0)
830 return bus_log_parse_error(r);
831
832 if (properties)
833 r = show_image_properties(bus, path, &new_line);
834 else
835 r = show_image_info(argv[0], bus, path, &new_line);
1ee306e1
LP
836 }
837
9f6eb1cd 838 return r;
1ee306e1
LP
839}
840
56159e0d 841static int kill_machine(int argc, char *argv[], void *userdata) {
a1da8583 842 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d
LP
843 sd_bus *bus = userdata;
844 int i;
1ee306e1 845
56159e0d 846 assert(bus);
1ee306e1
LP
847
848 if (!arg_kill_who)
849 arg_kill_who = "all";
850
56159e0d 851 for (i = 1; i < argc; i++) {
1ee306e1
LP
852 int r;
853
a1da8583 854 r = sd_bus_call_method(
56159e0d
LP
855 bus,
856 "org.freedesktop.machine1",
857 "/org/freedesktop/machine1",
858 "org.freedesktop.machine1.Manager",
859 "KillMachine",
860 &error,
861 NULL,
862 "ssi", argv[i], arg_kill_who, arg_signal);
a1da8583
TG
863 if (r < 0) {
864 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
1ee306e1 865 return r;
a1da8583 866 }
1ee306e1
LP
867 }
868
869 return 0;
870}
871
56159e0d 872static int reboot_machine(int argc, char *argv[], void *userdata) {
1dba654b
LP
873 arg_kill_who = "leader";
874 arg_signal = SIGINT; /* sysvinit + systemd */
1ee306e1 875
56159e0d 876 return kill_machine(argc, argv, userdata);
1dba654b 877}
1ee306e1 878
56159e0d 879static int poweroff_machine(int argc, char *argv[], void *userdata) {
1dba654b
LP
880 arg_kill_who = "leader";
881 arg_signal = SIGRTMIN+4; /* only systemd */
1ee306e1 882
56159e0d 883 return kill_machine(argc, argv, userdata);
1ee306e1
LP
884}
885
56159e0d 886static int terminate_machine(int argc, char *argv[], void *userdata) {
923d8fd3 887 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d
LP
888 sd_bus *bus = userdata;
889 int i;
923d8fd3 890
56159e0d 891 assert(bus);
923d8fd3 892
56159e0d 893 for (i = 1; i < argc; i++) {
1dba654b 894 int r;
923d8fd3
LP
895
896 r = sd_bus_call_method(
897 bus,
898 "org.freedesktop.machine1",
899 "/org/freedesktop/machine1",
900 "org.freedesktop.machine1.Manager",
1dba654b 901 "TerminateMachine",
923d8fd3 902 &error,
1dba654b 903 NULL,
56159e0d 904 "s", argv[i]);
923d8fd3 905 if (r < 0) {
1dba654b 906 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
923d8fd3
LP
907 return r;
908 }
923d8fd3
LP
909 }
910
911 return 0;
912}
913
785890ac
LP
914static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
915 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
916 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
917 const char *object;
918 uint32_t leader;
919 int r;
920
921 assert(bus);
922 assert(name);
923 assert(ret);
924
925 r = sd_bus_call_method(
926 bus,
927 "org.freedesktop.machine1",
928 "/org/freedesktop/machine1",
929 "org.freedesktop.machine1.Manager",
930 "GetMachine",
931 &error,
932 &reply,
933 "s", name);
934 if (r < 0) {
935 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
936 return r;
937 }
938
939 r = sd_bus_message_read(reply, "o", &object);
940 if (r < 0)
941 return bus_log_parse_error(r);
942
943 r = sd_bus_get_property(
944 bus,
945 "org.freedesktop.machine1",
946 object,
947 "org.freedesktop.machine1.Machine",
948 "Leader",
949 &error,
950 &reply2,
951 "u");
952 if (r < 0)
953 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
954
955 r = sd_bus_message_read(reply2, "u", &leader);
956 if (r < 0)
957 return bus_log_parse_error(r);
958
959 *ret = leader;
960 return 0;
961}
962
56159e0d 963static int copy_files(int argc, char *argv[], void *userdata) {
f2cbe59e
LP
964 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
965 _cleanup_close_ int hostfd = -1;
56159e0d 966 sd_bus *bus = userdata;
f2cbe59e
LP
967 pid_t child, leader;
968 bool copy_from;
969 siginfo_t si;
970 int r;
971
56159e0d 972 assert(bus);
f2cbe59e 973
56159e0d
LP
974 copy_from = streq(argv[0], "copy-from");
975 dest = argv[3] ?: argv[2];
976 host_path = strdupa(copy_from ? dest : argv[2]);
977 container_path = strdupa(copy_from ? argv[2] : dest);
f2cbe59e
LP
978
979 if (!path_is_absolute(container_path)) {
980 log_error("Container path not absolute.");
981 return -EINVAL;
982 }
983
984 t = strdup(host_path);
985 host_basename = basename(t);
986 host_dirname = dirname(host_path);
987
988 t = strdup(container_path);
989 container_basename = basename(t);
990 container_dirname = dirname(container_path);
991
56159e0d 992 r = machine_get_leader(bus, argv[1], &leader);
f2cbe59e
LP
993 if (r < 0)
994 return r;
995
996 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
997 if (r < 0)
998 return log_error_errno(errno, "Failed to open source directory: %m");
999
1000 child = fork();
1001 if (child < 0)
1002 return log_error_errno(errno, "Failed to fork(): %m");
1003
1004 if (child == 0) {
1005 int containerfd;
1006 const char *q;
1007 int mntfd;
1008
1009 q = procfs_file_alloca(leader, "ns/mnt");
1010 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1011 if (mntfd < 0) {
1012 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1013 _exit(EXIT_FAILURE);
1014 }
1015
1016 if (setns(mntfd, CLONE_NEWNS) < 0) {
1017 log_error_errno(errno, "Failed to join namespace of leader: %m");
1018 _exit(EXIT_FAILURE);
1019 }
1020
1021 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1022 if (containerfd < 0) {
1023 log_error_errno(errno, "Failed top open destination directory: %m");
1024 _exit(EXIT_FAILURE);
1025 }
1026
1027 if (copy_from)
1028 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
1029 else
1030 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
1031 if (r < 0) {
1032 log_error_errno(errno, "Failed to copy tree: %m");
1033 _exit(EXIT_FAILURE);
1034 }
1035
1036 _exit(EXIT_SUCCESS);
1037 }
1038
1039 r = wait_for_terminate(child, &si);
1040 if (r < 0)
1041 return log_error_errno(r, "Failed to wait for client: %m");
1042 if (si.si_code != CLD_EXITED) {
1043 log_error("Client died abnormally.");
1044 return -EIO;
1045 }
1046 if (si.si_status != EXIT_SUCCESS)
1047 return -EIO;
1048
1049 return 0;
1050}
1051
56159e0d 1052static int bind_mount(int argc, char *argv[], void *userdata) {
785890ac 1053 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
56159e0d 1054 sd_bus *bus = userdata;
785890ac
LP
1055 pid_t child, leader;
1056 const char *dest;
1057 siginfo_t si;
1058 bool mount_slave_created = false, mount_slave_mounted = false,
1059 mount_tmp_created = false, mount_tmp_mounted = false,
1060 mount_outside_created = false, mount_outside_mounted = false;
1061 int r;
1062
56159e0d
LP
1063 assert(bus);
1064
785890ac
LP
1065 /* One day, when bind mounting /proc/self/fd/n works across
1066 * namespace boundaries we should rework this logic to make
1067 * use of it... */
1068
56159e0d 1069 dest = argv[3] ?: argv[2];
785890ac
LP
1070 if (!path_is_absolute(dest)) {
1071 log_error("Destination path not absolute.");
1072 return -EINVAL;
1073 }
1074
56159e0d 1075 p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
785890ac
LP
1076 if (access(p, F_OK) < 0) {
1077 log_error("Container does not allow propagation of mount points.");
1078 return -ENOTSUP;
1079 }
1080
56159e0d 1081 r = machine_get_leader(bus, argv[1], &leader);
785890ac
LP
1082 if (r < 0)
1083 return r;
1084
1085 /* Our goal is to install a new bind mount into the container,
1086 possibly read-only. This is irritatingly complex
1087 unfortunately, currently.
1088
1089 First, we start by creating a private playground in /tmp,
1090 that we can mount MS_SLAVE. (Which is necessary, since
1091 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
1092 mounts.) */
1093
1094 if (!mkdtemp(mount_slave))
1095 return log_error_errno(errno, "Failed to create playground: %m");
1096
1097 mount_slave_created = true;
1098
1099 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
1100 r = log_error_errno(errno, "Failed to make bind mount: %m");
1101 goto finish;
1102 }
1103
1104 mount_slave_mounted = true;
1105
1106 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
1107 r = log_error_errno(errno, "Failed to remount slave: %m");
1108 goto finish;
1109 }
1110
1111 /* Second, we mount the source directory to a directory inside
1112 of our MS_SLAVE playground. */
1113 mount_tmp = strappenda(mount_slave, "/mount");
1114 if (mkdir(mount_tmp, 0700) < 0) {
1115 r = log_error_errno(errno, "Failed to create temporary mount: %m");
1116 goto finish;
1117 }
1118
1119 mount_tmp_created = true;
1120
56159e0d 1121 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
785890ac
LP
1122 r = log_error_errno(errno, "Failed to overmount: %m");
1123 goto finish;
1124 }
1125
1126 mount_tmp_mounted = true;
1127
1128 /* Third, we remount the new bind mount read-only if requested. */
1129 if (arg_read_only)
1130 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
1131 r = log_error_errno(errno, "Failed to mark read-only: %m");
1132 goto finish;
1133 }
1134
1135 /* Fourth, we move the new bind mount into the propagation
1136 * directory. This way it will appear there read-only
1137 * right-away. */
1138
56159e0d 1139 mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
785890ac
LP
1140 if (!mkdtemp(mount_outside)) {
1141 r = log_error_errno(errno, "Cannot create propagation directory: %m");
1142 goto finish;
1143 }
1144
1145 mount_outside_created = true;
1146
1147 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
1148 r = log_error_errno(errno, "Failed to move: %m");
1149 goto finish;
1150 }
1151
1152 mount_outside_mounted = true;
1153 mount_tmp_mounted = false;
1154
1155 (void) rmdir(mount_tmp);
1156 mount_tmp_created = false;
1157
1158 (void) umount(mount_slave);
1159 mount_slave_mounted = false;
1160
1161 (void) rmdir(mount_slave);
1162 mount_slave_created = false;
1163
1164 child = fork();
1165 if (child < 0) {
1166 r = log_error_errno(errno, "Failed to fork(): %m");
1167 goto finish;
1168 }
1169
1170 if (child == 0) {
1171 const char *mount_inside;
1172 int mntfd;
1173 const char *q;
1174
1175 q = procfs_file_alloca(leader, "ns/mnt");
1176 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1177 if (mntfd < 0) {
1178 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1179 _exit(EXIT_FAILURE);
1180 }
1181
1182 if (setns(mntfd, CLONE_NEWNS) < 0) {
1183 log_error_errno(errno, "Failed to join namespace of leader: %m");
1184 _exit(EXIT_FAILURE);
1185 }
1186
1187 if (arg_mkdir)
1188 mkdir_p(dest, 0755);
1189
1190 /* Fifth, move the mount to the right place inside */
1191 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
1192 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
1193 log_error_errno(errno, "Failed to mount: %m");
1194 _exit(EXIT_FAILURE);
1195 }
1196
1197 _exit(EXIT_SUCCESS);
1198 }
1199
1200 r = wait_for_terminate(child, &si);
1201 if (r < 0) {
1202 log_error_errno(r, "Failed to wait for client: %m");
1203 goto finish;
1204 }
1205 if (si.si_code != CLD_EXITED) {
1206 log_error("Client died abnormally.");
1207 r = -EIO;
1208 goto finish;
1209 }
1210 if (si.si_status != EXIT_SUCCESS) {
1211 r = -EIO;
1212 goto finish;
1213 }
1214
1215 r = 0;
1216
1217finish:
1218 if (mount_outside_mounted)
1219 umount(mount_outside);
1220 if (mount_outside_created)
1221 rmdir(mount_outside);
1222
1223 if (mount_tmp_mounted)
1224 umount(mount_tmp);
1225 if (mount_tmp_created)
1226 umount(mount_tmp);
1227
1228 if (mount_slave_mounted)
1229 umount(mount_slave);
1230 if (mount_slave_created)
1231 umount(mount_slave);
1232
1233 return r;
1234}
1235
56159e0d 1236static int login_machine(int argc, char *argv[], void *userdata) {
04d39279 1237 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
d04c1fb8 1238 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
023fb90b
LP
1239 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1240 _cleanup_event_unref_ sd_event *event = NULL;
40205d70 1241 int master = -1, r, ret = 0;
56159e0d 1242 sd_bus *bus = userdata;
bf441e3d 1243 const char *pty;
04d39279 1244 sigset_t mask;
c7b7d449 1245 char last_char = 0;
04d39279
LP
1246
1247 assert(bus);
04d39279 1248
bf441e3d 1249 if (arg_transport != BUS_TRANSPORT_LOCAL &&
de33fc62 1250 arg_transport != BUS_TRANSPORT_MACHINE) {
923d8fd3 1251 log_error("Login only supported on local machines.");
04d39279
LP
1252 return -ENOTSUP;
1253 }
1254
023fb90b 1255 r = sd_event_default(&event);
f647962d
MS
1256 if (r < 0)
1257 return log_error_errno(r, "Failed to get event loop: %m");
023fb90b
LP
1258
1259 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
1260 if (r < 0)
1261 return log_error_errno(r, "Failed to attach bus to event loop: %m");
023fb90b 1262
d04c1fb8
LP
1263 r = sd_bus_message_new_method_call(bus,
1264 &m,
1265 "org.freedesktop.machine1",
1266 "/org/freedesktop/machine1",
1267 "org.freedesktop.machine1.Manager",
1268 "OpenMachineLogin");
1269 if (r < 0)
1270 return bus_log_create_error(r);
1271
1272 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1273 if (r < 0)
1274 return bus_log_create_error(r);
1275
1276 r = sd_bus_message_append(m, "s", argv[1]);
1277 if (r < 0)
1278 return bus_log_create_error(r);
1279
1280 r = sd_bus_call(bus, m, 0, &error, &reply);
40205d70
LP
1281 if (r < 0) {
1282 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
785890ac 1283 return r;
40205d70 1284 }
04d39279 1285
40205d70
LP
1286 r = sd_bus_message_read(reply, "hs", &master, &pty);
1287 if (r < 0)
ee451d76 1288 return bus_log_parse_error(r);
04d39279 1289
04d39279
LP
1290 assert_se(sigemptyset(&mask) == 0);
1291 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1292 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1293
56159e0d 1294 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", argv[1]);
04d39279 1295
023fb90b
LP
1296 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1297 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1298
9b15b784 1299 r = pty_forward_new(event, master, true, &forward);
f647962d
MS
1300 if (r < 0)
1301 return log_error_errno(r, "Failed to create PTY forwarder: %m");
023fb90b
LP
1302
1303 r = sd_event_loop(event);
f647962d
MS
1304 if (r < 0)
1305 return log_error_errno(r, "Failed to run event loop: %m");
04d39279 1306
c7b7d449
LP
1307 pty_forward_last_char(forward, &last_char);
1308
023fb90b
LP
1309 forward = pty_forward_free(forward);
1310
c7b7d449
LP
1311 if (last_char != '\n')
1312 fputc('\n', stdout);
04d39279 1313
56159e0d 1314 log_info("Connection to container %s terminated.", argv[1]);
04d39279 1315
023fb90b
LP
1316 sd_event_get_exit_code(event, &ret);
1317 return ret;
04d39279
LP
1318}
1319
08682124
LP
1320static int remove_image(int argc, char *argv[], void *userdata) {
1321 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1322 sd_bus *bus = userdata;
ebd93cb6 1323 int r, i;
08682124
LP
1324
1325 assert(bus);
1326
1327 for (i = 1; i < argc; i++) {
08682124
LP
1328 r = sd_bus_call_method(
1329 bus,
1330 "org.freedesktop.machine1",
1331 "/org/freedesktop/machine1",
1332 "org.freedesktop.machine1.Manager",
1333 "RemoveImage",
1334 &error,
1335 NULL,
1336 "s", argv[i]);
1337 if (r < 0) {
1338 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1339 return r;
1340 }
1341 }
1342
1343 return 0;
1344}
1345
ebd93cb6
LP
1346static int rename_image(int argc, char *argv[], void *userdata) {
1347 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1348 sd_bus *bus = userdata;
1349 int r;
1350
1351 r = sd_bus_call_method(
1352 bus,
1353 "org.freedesktop.machine1",
1354 "/org/freedesktop/machine1",
1355 "org.freedesktop.machine1.Manager",
1356 "RenameImage",
1357 &error,
1358 NULL,
1359 "ss", argv[1], argv[2]);
1360 if (r < 0) {
1361 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1362 return r;
1363 }
1364
1365 return 0;
1366}
1367
1368static int clone_image(int argc, char *argv[], void *userdata) {
1369 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1370 sd_bus *bus = userdata;
1371 int r;
1372
1373 r = sd_bus_call_method(
1374 bus,
1375 "org.freedesktop.machine1",
1376 "/org/freedesktop/machine1",
1377 "org.freedesktop.machine1.Manager",
1378 "CloneImage",
1379 &error,
1380 NULL,
1381 "ssb", argv[1], argv[2], arg_read_only);
1382 if (r < 0) {
1383 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1384 return r;
1385 }
1386
1387 return 0;
1388}
1389
1390static int read_only_image(int argc, char *argv[], void *userdata) {
1391 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1392 sd_bus *bus = userdata;
1393 int b = true, r;
1394
1395 if (argc > 2) {
1396 b = parse_boolean(argv[2]);
1397 if (b < 0) {
1398 log_error("Failed to parse boolean argument: %s", argv[2]);
1399 return -EINVAL;
1400 }
1401 }
1402
1403 r = sd_bus_call_method(
1404 bus,
1405 "org.freedesktop.machine1",
1406 "/org/freedesktop/machine1",
1407 "org.freedesktop.machine1.Manager",
1408 "MarkImageReadOnly",
1409 &error,
1410 NULL,
1411 "sb", argv[1], b);
1412 if (r < 0) {
1413 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1414 return r;
1415 }
1416
1417 return 0;
1418}
1419
ebd011d9
LP
1420static int start_machine(int argc, char *argv[], void *userdata) {
1421 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1422 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1423 sd_bus *bus = userdata;
1424 int r, i;
1425
1426 assert(bus);
1427
1428 r = bus_wait_for_jobs_new(bus, &w);
1429 if (r < 0)
1430 return log_oom();
1431
1432 for (i = 1; i < argc; i++) {
1433 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1434 _cleanup_free_ char *e = NULL, *unit = NULL;
1435 const char *object;
1436
1437 if (!machine_name_is_valid(argv[i])) {
1438 log_error("Invalid machine name %s.", argv[i]);
1439 return -EINVAL;
1440 }
1441
1442 e = unit_name_escape(argv[i]);
1443 if (!e)
1444 return log_oom();
1445
1446 unit = unit_name_build("systemd-nspawn", e, ".service");
1447 if (!unit)
1448 return log_oom();
1449
1450 r = sd_bus_message_new_method_call(
1451 bus,
1452 &m,
1453 "org.freedesktop.systemd1",
1454 "/org/freedesktop/systemd1",
1455 "org.freedesktop.systemd1.Manager",
1456 "StartUnit");
1457 if (r < 0)
1458 return bus_log_create_error(r);
1459
1460 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1461 if (r < 0)
1462 return bus_log_create_error(r);
1463
1464 r = sd_bus_message_append(m, "ss", unit, "fail");
1465 if (r < 0)
1466 return bus_log_create_error(r);
1467
1468 r = sd_bus_call(bus, m, 0, &error, &reply);
1469 if (r < 0) {
1470 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1471 return r;
1472 }
1473
1474 r = sd_bus_message_read(reply, "o", &object);
1475 if (r < 0)
1476 return bus_log_parse_error(r);
1477
1478 r = bus_wait_for_jobs_add(w, object);
1479 if (r < 0)
1480 return log_oom();
1481 }
1482
1483 r = bus_wait_for_jobs(w, false);
1484 if (r < 0)
1485 return r;
1486
1487 return 0;
1488}
1489
56159e0d
LP
1490static int help(int argc, char *argv[], void *userdata) {
1491
1ee306e1 1492 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
f2cbe59e
LP
1493 "Send control commands to or query the virtual machine and container\n"
1494 "registration manager.\n\n"
1495 " -h --help Show this help\n"
1496 " --version Show package version\n"
1497 " --no-pager Do not pipe output into a pager\n"
1498 " --no-legend Do not show the headers and footers\n"
1499 " -H --host=[USER@]HOST Operate on remote host\n"
1500 " -M --machine=CONTAINER Operate on local container\n"
1501 " -p --property=NAME Show only properties by this name\n"
1502 " -a --all Show all properties, including empty ones\n"
1503 " -l --full Do not ellipsize output\n"
1504 " --kill-who=WHO Who to send signal to\n"
1505 " -s --signal=SIGNAL Which signal to send\n"
1506 " --read-only Create read-only bind mount\n"
1507 " --mkdir Create directory before bind mounting, if missing\n\n"
cd61c3bf 1508 "Machine Commands:\n"
f2cbe59e 1509 " list List running VMs and containers\n"
fefdc04b 1510 " status NAME... Show VM/container details\n"
f2cbe59e
LP
1511 " show NAME... Show properties of one or more VMs/containers\n"
1512 " login NAME Get a login prompt on a container\n"
ebd011d9 1513 " start NAME... Start container as a service\n"
f2cbe59e
LP
1514 " poweroff NAME... Power off one or more containers\n"
1515 " reboot NAME... Reboot one or more containers\n"
f2cbe59e 1516 " terminate NAME... Terminate one or more VMs/containers\n"
ebd93cb6 1517 " kill NAME... Send signal to processes of a VM/container\n"
f2cbe59e 1518 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
ebd93cb6
LP
1519 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
1520 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
fefdc04b
LP
1521 "Image Commands:\n"
1522 " list-images Show available images\n"
1523 " image-status NAME... Show image details\n"
08682124 1524 " show-image NAME... Show properties of image\n"
ebd93cb6
LP
1525 " clone NAME NAME Clone an image\n"
1526 " rename NAME NAME Rename an image\n"
1527 " read-only NAME [BOOL] Mark or unmark image read-only\n"
08682124 1528 " remove NAME... Remove an image\n",
1ee306e1 1529 program_invocation_short_name);
56159e0d
LP
1530
1531 return 0;
1ee306e1
LP
1532}
1533
1534static int parse_argv(int argc, char *argv[]) {
1535
1536 enum {
1537 ARG_VERSION = 0x100,
1538 ARG_NO_PAGER,
e56056e9 1539 ARG_NO_LEGEND,
1ee306e1 1540 ARG_KILL_WHO,
785890ac
LP
1541 ARG_READ_ONLY,
1542 ARG_MKDIR,
1ee306e1
LP
1543 };
1544
1545 static const struct option options[] = {
1546 { "help", no_argument, NULL, 'h' },
1547 { "version", no_argument, NULL, ARG_VERSION },
1548 { "property", required_argument, NULL, 'p' },
1549 { "all", no_argument, NULL, 'a' },
1550 { "full", no_argument, NULL, 'l' },
1551 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
e56056e9 1552 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1ee306e1
LP
1553 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1554 { "signal", required_argument, NULL, 's' },
1555 { "host", required_argument, NULL, 'H' },
a7893c6b 1556 { "machine", required_argument, NULL, 'M' },
785890ac
LP
1557 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1558 { "mkdir", no_argument, NULL, ARG_MKDIR },
eb9da376 1559 {}
1ee306e1
LP
1560 };
1561
a7893c6b 1562 int c, r;
1ee306e1
LP
1563
1564 assert(argc >= 0);
1565 assert(argv);
1566
601185b4 1567 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1ee306e1
LP
1568
1569 switch (c) {
1570
1571 case 'h':
56159e0d 1572 return help(0, NULL, NULL);
1ee306e1
LP
1573
1574 case ARG_VERSION:
1575 puts(PACKAGE_STRING);
1576 puts(SYSTEMD_FEATURES);
1577 return 0;
1578
a7893c6b
LP
1579 case 'p':
1580 r = strv_extend(&arg_property, optarg);
1581 if (r < 0)
1582 return log_oom();
1ee306e1
LP
1583
1584 /* If the user asked for a particular
1585 * property, show it to him, even if it is
1586 * empty. */
1587 arg_all = true;
1588 break;
1ee306e1
LP
1589
1590 case 'a':
1591 arg_all = true;
1592 break;
1593
1594 case 'l':
1595 arg_full = true;
1596 break;
1597
1598 case ARG_NO_PAGER:
1599 arg_no_pager = true;
1600 break;
1601
e56056e9
TA
1602 case ARG_NO_LEGEND:
1603 arg_legend = false;
1604 break;
1605
1ee306e1
LP
1606 case ARG_KILL_WHO:
1607 arg_kill_who = optarg;
1608 break;
1609
1610 case 's':
1611 arg_signal = signal_from_string_try_harder(optarg);
1612 if (arg_signal < 0) {
1613 log_error("Failed to parse signal string %s.", optarg);
1614 return -EINVAL;
1615 }
1616 break;
1617
1ee306e1 1618 case 'H':
d21ed1ea 1619 arg_transport = BUS_TRANSPORT_REMOTE;
a7893c6b
LP
1620 arg_host = optarg;
1621 break;
1622
1623 case 'M':
de33fc62 1624 arg_transport = BUS_TRANSPORT_MACHINE;
a7893c6b 1625 arg_host = optarg;
1ee306e1
LP
1626 break;
1627
785890ac
LP
1628 case ARG_READ_ONLY:
1629 arg_read_only = true;
1630 break;
1631
1632 case ARG_MKDIR:
1633 arg_mkdir = true;
1634 break;
1635
1ee306e1
LP
1636 case '?':
1637 return -EINVAL;
1638
1639 default:
eb9da376 1640 assert_not_reached("Unhandled option");
1ee306e1 1641 }
1ee306e1
LP
1642
1643 return 1;
1644}
1645
56159e0d
LP
1646static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1647
1648 static const Verb verbs[] = {
1649 { "help", VERB_ANY, VERB_ANY, 0, help },
1650 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
1651 { "list-images", VERB_ANY, 1, 0, list_images },
fefdc04b
LP
1652 { "status", 2, VERB_ANY, 0, show_machine },
1653 { "image-status",2, VERB_ANY, 0, show_image },
1654 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
1655 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
56159e0d
LP
1656 { "terminate", 2, VERB_ANY, 0, terminate_machine },
1657 { "reboot", 2, VERB_ANY, 0, reboot_machine },
1658 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
1659 { "kill", 2, VERB_ANY, 0, kill_machine },
1660 { "login", 2, 2, 0, login_machine },
1661 { "bind", 3, 4, 0, bind_mount },
1662 { "copy-to", 3, 4, 0, copy_files },
1663 { "copy-from", 3, 4, 0, copy_files },
08682124 1664 { "remove", 2, VERB_ANY, 0, remove_image },
ebd93cb6
LP
1665 { "rename", 3, 3, 0, rename_image },
1666 { "clone", 3, 3, 0, clone_image },
1667 { "read-only", 2, 3, 0, read_only_image },
ebd011d9 1668 { "start", 2, VERB_ANY, 0, start_machine },
56159e0d 1669 {}
1ee306e1
LP
1670 };
1671
56159e0d 1672 return dispatch_verb(argc, argv, verbs, bus);
1ee306e1
LP
1673}
1674
1675int main(int argc, char*argv[]) {
24996861 1676 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
84f6181c 1677 int r;
1ee306e1
LP
1678
1679 setlocale(LC_ALL, "");
1680 log_parse_environment();
1681 log_open();
1682
1683 r = parse_argv(argc, argv);
84f6181c 1684 if (r <= 0)
1ee306e1 1685 goto finish;
1ee306e1 1686
d21ed1ea 1687 r = bus_open_transport(arg_transport, arg_host, false, &bus);
a1da8583 1688 if (r < 0) {
da927ba9 1689 log_error_errno(r, "Failed to create bus connection: %m");
a1da8583
TG
1690 goto finish;
1691 }
1ee306e1 1692
56159e0d 1693 r = machinectl_main(argc, argv, bus);
1ee306e1
LP
1694
1695finish:
1ee306e1
LP
1696 pager_close();
1697
84f6181c
LP
1698 strv_free(arg_property);
1699
1700 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1ee306e1 1701}