]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machinectl.c
importd: minor log improvements
[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>
d538bfc7 35#undef basename
a1da8583 36#include "sd-bus.h"
1ee306e1
LP
37#include "log.h"
38#include "util.h"
39#include "macro.h"
40#include "pager.h"
acf97e21 41#include "spawn-polkit-agent.h"
a1da8583
TG
42#include "bus-util.h"
43#include "bus-error.h"
1ee306e1
LP
44#include "build.h"
45#include "strv.h"
aa1936ea 46#include "unit-name.h"
1ee306e1 47#include "cgroup-show.h"
8b0cc9a3 48#include "logs-show.h"
9d127096 49#include "cgroup-util.h"
04d39279 50#include "ptyfwd.h"
023fb90b 51#include "event-util.h"
785890ac
LP
52#include "path-util.h"
53#include "mkdir.h"
f2cbe59e 54#include "copy.h"
56159e0d 55#include "verbs.h"
3d7415f4 56#include "import-util.h"
1ee306e1
LP
57
58static char **arg_property = NULL;
59static bool arg_all = false;
60static bool arg_full = false;
61static bool arg_no_pager = false;
e56056e9 62static bool arg_legend = true;
1ee306e1
LP
63static const char *arg_kill_who = NULL;
64static int arg_signal = SIGTERM;
d21ed1ea 65static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
1ee306e1 66static char *arg_host = NULL;
785890ac
LP
67static bool arg_read_only = false;
68static bool arg_mkdir = false;
d8f52ed2 69static bool arg_quiet = false;
acf97e21 70static bool arg_ask_password = true;
8b0cc9a3
LP
71static unsigned arg_lines = 10;
72static OutputMode arg_output = OUTPUT_SHORT;
3d7415f4
LP
73static bool arg_force = false;
74static const char* arg_verify = NULL;
75static const char* arg_dkr_index_url = NULL;
1ee306e1
LP
76
77static void pager_open_if_enabled(void) {
78
1ee306e1
LP
79 if (arg_no_pager)
80 return;
81
82 pager_open(false);
83}
84
acf97e21
LP
85static void polkit_agent_open_if_enabled(void) {
86
87 /* Open the polkit agent as a child process if necessary */
88
89 if (!arg_ask_password)
90 return;
91
92 if (arg_transport != BUS_TRANSPORT_LOCAL)
93 return;
94
95 polkit_agent_open();
96}
97
8b0cc9a3
LP
98static OutputFlags get_output_flags(void) {
99 return
100 arg_all * OUTPUT_SHOW_ALL |
101 arg_full * OUTPUT_FULL_WIDTH |
102 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
103 on_tty() * OUTPUT_COLOR |
104 !arg_quiet * OUTPUT_WARN_CUTOFF;
105}
106
0b63e278
LP
107typedef struct MachineInfo {
108 const char *name;
109 const char *class;
110 const char *service;
111} MachineInfo;
112
113static int compare_machine_info(const void *a, const void *b) {
114 const MachineInfo *x = a, *y = b;
115
116 return strcmp(x->name, y->name);
117}
118
56159e0d
LP
119static int list_machines(int argc, char *argv[], void *userdata) {
120
0b63e278 121 size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
a1da8583 122 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
0b63e278
LP
123 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
124 _cleanup_free_ MachineInfo *machines = NULL;
a1da8583 125 const char *name, *class, *service, *object;
0b63e278 126 size_t n_machines = 0, n_allocated = 0, j;
56159e0d 127 sd_bus *bus = userdata;
1ee306e1
LP
128 int r;
129
56159e0d
LP
130 assert(bus);
131
1ee306e1
LP
132 pager_open_if_enabled();
133
a1da8583
TG
134 r = sd_bus_call_method(
135 bus,
136 "org.freedesktop.machine1",
137 "/org/freedesktop/machine1",
138 "org.freedesktop.machine1.Manager",
139 "ListMachines",
140 &error,
141 &reply,
3d7415f4 142 NULL);
a1da8583
TG
143 if (r < 0) {
144 log_error("Could not get machines: %s", bus_error_message(&error, -r));
1ee306e1 145 return r;
1ee306e1
LP
146 }
147
3d7415f4 148 r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
a1da8583 149 if (r < 0)
5b30bef8 150 return bus_log_parse_error(r);
1ee306e1 151
a1da8583 152 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
0b63e278
LP
153 size_t l;
154
155 if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
156 return log_oom();
1ee306e1 157
0b63e278
LP
158 machines[n_machines].name = name;
159 machines[n_machines].class = class;
160 machines[n_machines].service = service;
161
162 l = strlen(name);
163 if (l > max_name)
164 max_name = l;
165
166 l = strlen(class);
167 if (l > max_class)
168 max_class = l;
169
170 l = strlen(service);
171 if (l > max_service)
172 max_service = l;
173
174 n_machines ++;
1ee306e1 175 }
a1da8583 176 if (r < 0)
5b30bef8 177 return bus_log_parse_error(r);
a1da8583
TG
178
179 r = sd_bus_message_exit_container(reply);
180 if (r < 0)
5b30bef8 181 return bus_log_parse_error(r);
1ee306e1 182
0b63e278
LP
183 qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
184
185 if (arg_legend)
186 printf("%-*s %-*s %-*s\n",
187 (int) max_name, "MACHINE",
188 (int) max_class, "CLASS",
189 (int) max_service, "SERVICE");
190
191 for (j = 0; j < n_machines; j++)
192 printf("%-*s %-*s %-*s\n",
193 (int) max_name, machines[j].name,
194 (int) max_class, machines[j].class,
195 (int) max_service, machines[j].service);
196
e56056e9 197 if (arg_legend)
0b63e278 198 printf("\n%zu machines listed.\n", n_machines);
1ee306e1
LP
199
200 return 0;
201}
202
cd61c3bf
LP
203typedef struct ImageInfo {
204 const char *name;
205 const char *type;
206 bool read_only;
10f9c755
LP
207 usec_t crtime;
208 usec_t mtime;
b6b18498 209 uint64_t size;
cd61c3bf
LP
210} ImageInfo;
211
212static int compare_image_info(const void *a, const void *b) {
213 const ImageInfo *x = a, *y = b;
214
215 return strcmp(x->name, y->name);
216}
217
56159e0d 218static int list_images(int argc, char *argv[], void *userdata) {
cd61c3bf
LP
219
220 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
c19de711 221 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("USAGE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
cd61c3bf
LP
222 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
223 _cleanup_free_ ImageInfo *images = NULL;
224 size_t n_images = 0, n_allocated = 0, j;
225 const char *name, *type, *object;
56159e0d 226 sd_bus *bus = userdata;
b6b18498 227 uint64_t crtime, mtime, size;
10f9c755 228 int read_only, r;
cd61c3bf 229
56159e0d
LP
230 assert(bus);
231
cd61c3bf
LP
232 pager_open_if_enabled();
233
234 r = sd_bus_call_method(
235 bus,
236 "org.freedesktop.machine1",
237 "/org/freedesktop/machine1",
238 "org.freedesktop.machine1.Manager",
239 "ListImages",
240 &error,
241 &reply,
242 "");
243 if (r < 0) {
244 log_error("Could not get images: %s", bus_error_message(&error, -r));
245 return r;
246 }
247
b6b18498 248 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
cd61c3bf
LP
249 if (r < 0)
250 return bus_log_parse_error(r);
251
b6b18498
LP
252 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
253 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
10f9c755 254 size_t l;
cd61c3bf
LP
255
256 if (name[0] == '.' && !arg_all)
257 continue;
258
259 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
260 return log_oom();
261
262 images[n_images].name = name;
263 images[n_images].type = type;
264 images[n_images].read_only = read_only;
10f9c755
LP
265 images[n_images].crtime = crtime;
266 images[n_images].mtime = mtime;
b6b18498 267 images[n_images].size = size;
10f9c755
LP
268
269 l = strlen(name);
270 if (l > max_name)
271 max_name = l;
cd61c3bf 272
10f9c755
LP
273 l = strlen(type);
274 if (l > max_type)
275 max_type = l;
cd61c3bf 276
10f9c755 277 if (crtime != 0) {
b6b18498 278 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
10f9c755
LP
279 if (l > max_crtime)
280 max_crtime = l;
281 }
282
283 if (mtime != 0) {
b6b18498 284 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
10f9c755
LP
285 if (l > max_mtime)
286 max_mtime = l;
287 }
cd61c3bf 288
b6b18498
LP
289 if (size != (uint64_t) -1) {
290 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
291 if (l > max_size)
292 max_size = l;
293 }
294
cd61c3bf
LP
295 n_images++;
296 }
297 if (r < 0)
298 return bus_log_parse_error(r);
299
300 r = sd_bus_message_exit_container(reply);
301 if (r < 0)
302 return bus_log_parse_error(r);
303
304 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
305
306 if (arg_legend)
b6b18498 307 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
10f9c755
LP
308 (int) max_name, "NAME",
309 (int) max_type, "TYPE",
310 "RO",
c19de711 311 (int) max_size, "USAGE",
10f9c755
LP
312 (int) max_crtime, "CREATED",
313 (int) max_mtime, "MODIFIED");
cd61c3bf
LP
314
315 for (j = 0; j < n_images; j++) {
b6b18498 316 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
10f9c755 317
b6b18498 318 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
cd61c3bf
LP
319 (int) max_name, images[j].name,
320 (int) max_type, images[j].type,
8937e7b6 321 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
b6b18498
LP
322 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
323 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
324 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
cd61c3bf
LP
325 }
326
cd61c3bf
LP
327 if (arg_legend)
328 printf("\n%zu images listed.\n", n_images);
329
330 return 0;
331}
332
89f7c846 333static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
a1da8583
TG
334 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
335 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
aa1936ea 336 _cleanup_free_ char *path = NULL;
aa1936ea 337 const char *cgroup;
8b0cc9a3 338 int r;
aa1936ea
LP
339 unsigned c;
340
341 assert(bus);
342 assert(unit);
343
d21ed1ea 344 if (arg_transport == BUS_TRANSPORT_REMOTE)
aa1936ea
LP
345 return 0;
346
347 path = unit_dbus_path_from_name(unit);
348 if (!path)
349 return log_oom();
350
a7893c6b 351 r = sd_bus_get_property(
aa1936ea
LP
352 bus,
353 "org.freedesktop.systemd1",
354 path,
89f7c846 355 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
a7893c6b 356 "ControlGroup",
aa1936ea 357 &error,
a1da8583 358 &reply,
a7893c6b 359 "s");
aa1936ea 360 if (r < 0) {
a1da8583 361 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
aa1936ea
LP
362 return r;
363 }
364
a7893c6b 365 r = sd_bus_message_read(reply, "s", &cgroup);
5b30bef8
LP
366 if (r < 0)
367 return bus_log_parse_error(r);
aa1936ea 368
9d127096
LP
369 if (isempty(cgroup))
370 return 0;
371
372 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
373 return 0;
374
aa1936ea
LP
375 c = columns();
376 if (c > 18)
377 c -= 18;
378 else
379 c = 0;
380
8b0cc9a3 381 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
aa1936ea
LP
382 return 0;
383}
384
f48e75cb 385static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
878cd7e9
LP
386 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
387 int r;
388
389 assert(bus);
390 assert(name);
391 assert(prefix);
392 assert(prefix2);
393
394 r = sd_bus_call_method(bus,
395 "org.freedesktop.machine1",
396 "/org/freedesktop/machine1",
397 "org.freedesktop.machine1.Manager",
398 "GetMachineAddresses",
399 NULL,
400 &reply,
401 "s", name);
402 if (r < 0)
403 return r;
404
0dd25fb9 405 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
878cd7e9
LP
406 if (r < 0)
407 return bus_log_parse_error(r);
408
0dd25fb9
LP
409 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
410 int family;
878cd7e9
LP
411 const void *a;
412 size_t sz;
413 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
414
0dd25fb9 415 r = sd_bus_message_read(reply, "i", &family);
878cd7e9
LP
416 if (r < 0)
417 return bus_log_parse_error(r);
418
419 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
420 if (r < 0)
421 return bus_log_parse_error(r);
422
f48e75cb
LP
423 fputs(prefix, stdout);
424 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
425 if (family == AF_INET6 && ifi > 0)
426 printf("%%%i", ifi);
427 fputc('\n', stdout);
878cd7e9
LP
428
429 r = sd_bus_message_exit_container(reply);
430 if (r < 0)
431 return bus_log_parse_error(r);
432
433 if (prefix != prefix2)
434 prefix = prefix2;
435 }
436 if (r < 0)
437 return bus_log_parse_error(r);
438
439 r = sd_bus_message_exit_container(reply);
440 if (r < 0)
441 return bus_log_parse_error(r);
442
443 return 0;
444}
445
717603e3
LP
446static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
447 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
448 const char *k, *v, *pretty = NULL;
449 int r;
450
451 assert(bus);
452 assert(name);
453 assert(prefix);
454
455 r = sd_bus_call_method(bus,
456 "org.freedesktop.machine1",
457 "/org/freedesktop/machine1",
458 "org.freedesktop.machine1.Manager",
459 "GetMachineOSRelease",
460 NULL,
461 &reply,
462 "s", name);
463 if (r < 0)
464 return r;
465
466 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
467 if (r < 0)
468 return bus_log_parse_error(r);
469
470 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
471 if (streq(k, "PRETTY_NAME"))
472 pretty = v;
473
474 }
475 if (r < 0)
476 return bus_log_parse_error(r);
477
478 r = sd_bus_message_exit_container(reply);
479 if (r < 0)
480 return bus_log_parse_error(r);
481
482 if (pretty)
483 printf("%s%s\n", prefix, pretty);
484
485 return 0;
486}
487
1ee306e1 488typedef struct MachineStatusInfo {
9f6eb1cd 489 char *name;
1ee306e1 490 sd_id128_t id;
9f6eb1cd
KS
491 char *class;
492 char *service;
89f7c846 493 char *unit;
9f6eb1cd 494 char *root_directory;
1ee306e1 495 pid_t leader;
8b0cc9a3 496 struct dual_timestamp timestamp;
f48e75cb
LP
497 int *netif;
498 unsigned n_netif;
1ee306e1
LP
499} MachineStatusInfo;
500
a1da8583 501static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
1ee306e1
LP
502 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
503 char since2[FORMAT_TIMESTAMP_MAX], *s2;
f48e75cb
LP
504 int ifi = -1;
505
56159e0d 506 assert(bus);
1ee306e1
LP
507 assert(i);
508
509 fputs(strna(i->name), stdout);
510
511 if (!sd_id128_equal(i->id, SD_ID128_NULL))
512 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
513 else
514 putchar('\n');
515
8b0cc9a3
LP
516 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
517 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
1ee306e1
LP
518
519 if (s1)
520 printf("\t Since: %s; %s\n", s2, s1);
521 else if (s2)
522 printf("\t Since: %s\n", s2);
523
524 if (i->leader > 0) {
525 _cleanup_free_ char *t = NULL;
526
527 printf("\t Leader: %u", (unsigned) i->leader);
528
529 get_process_comm(i->leader, &t);
530 if (t)
531 printf(" (%s)", t);
532
533 putchar('\n');
534 }
535
536 if (i->service) {
537 printf("\t Service: %s", i->service);
538
539 if (i->class)
540 printf("; class %s", i->class);
541
542 putchar('\n');
543 } else if (i->class)
544 printf("\t Class: %s\n", i->class);
545
1ee306e1
LP
546 if (i->root_directory)
547 printf("\t Root: %s\n", i->root_directory);
548
f48e75cb
LP
549 if (i->n_netif > 0) {
550 unsigned c;
551
552 fputs("\t Iface:", stdout);
553
554 for (c = 0; c < i->n_netif; c++) {
555 char name[IF_NAMESIZE+1] = "";
556
557 if (if_indextoname(i->netif[c], name)) {
558 fputc(' ', stdout);
559 fputs(name, stdout);
560
561 if (ifi < 0)
562 ifi = i->netif[c];
563 else
564 ifi = 0;
565 } else
566 printf(" %i", i->netif[c]);
567 }
568
569 fputc('\n', stdout);
570 }
571
572 print_addresses(bus, i->name, ifi,
878cd7e9
LP
573 "\t Address: ",
574 "\t ");
575
717603e3
LP
576 print_os_release(bus, i->name, "\t OS: ");
577
89f7c846
LP
578 if (i->unit) {
579 printf("\t Unit: %s\n", i->unit);
580 show_unit_cgroup(bus, i->unit, i->leader);
8b0cc9a3
LP
581
582 if (arg_transport == BUS_TRANSPORT_LOCAL) {
583
584 show_journal_by_unit(
585 stdout,
586 i->unit,
587 arg_output,
588 0,
589 i->timestamp.monotonic,
590 arg_lines,
591 0,
592 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
593 SD_JOURNAL_LOCAL_ONLY,
594 true,
595 NULL);
596 }
1ee306e1
LP
597 }
598}
599
f48e75cb
LP
600static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
601 MachineStatusInfo *i = userdata;
602 size_t l;
603 const void *v;
604 int r;
605
606 assert_cc(sizeof(int32_t) == sizeof(int));
607 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
608 if (r < 0)
609 return r;
e7e9b6bb
ZJS
610 if (r == 0)
611 return -EBADMSG;
f48e75cb
LP
612
613 i->n_netif = l / sizeof(int32_t);
614 i->netif = memdup(v, l);
615 if (!i->netif)
616 return -ENOMEM;
617
618 return 0;
619}
620
fefdc04b 621static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
a6c61602 622
9f6eb1cd 623 static const struct bus_properties_map map[] = {
8b0cc9a3
LP
624 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
625 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
626 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
627 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
628 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
629 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
630 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
631 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
632 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
633 { "NetworkInterfaces", "ai", map_netif, 0 },
9f6eb1cd
KS
634 {}
635 };
a6c61602
LP
636
637 MachineStatusInfo info = {};
a1da8583
TG
638 int r;
639
56159e0d
LP
640 assert(verb);
641 assert(bus);
1ee306e1
LP
642 assert(path);
643 assert(new_line);
644
9f6eb1cd
KS
645 r = bus_map_all_properties(bus,
646 "org.freedesktop.machine1",
647 path,
648 map,
649 &info);
f647962d
MS
650 if (r < 0)
651 return log_error_errno(r, "Could not get properties: %m");
1ee306e1 652
1ee306e1
LP
653 if (*new_line)
654 printf("\n");
1ee306e1
LP
655 *new_line = true;
656
9f6eb1cd 657 print_machine_status_info(bus, &info);
1ee306e1 658
9f6eb1cd
KS
659 free(info.name);
660 free(info.class);
661 free(info.service);
89f7c846 662 free(info.unit);
9f6eb1cd 663 free(info.root_directory);
f48e75cb 664 free(info.netif);
1ee306e1 665
9f6eb1cd
KS
666 return r;
667}
1ee306e1 668
fefdc04b 669static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
9f6eb1cd 670 int r;
1ee306e1 671
56159e0d
LP
672 assert(bus);
673 assert(path);
674 assert(new_line);
675
9f6eb1cd
KS
676 if (*new_line)
677 printf("\n");
1ee306e1 678
9f6eb1cd 679 *new_line = true;
a1da8583 680
27e72d6b 681 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
a1da8583 682 if (r < 0)
da927ba9 683 log_error_errno(r, "Could not get properties: %m");
1ee306e1 684
a7893c6b 685 return r;
1ee306e1
LP
686}
687
fefdc04b 688static int show_machine(int argc, char *argv[], void *userdata) {
56159e0d 689
a1da8583 690 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d 691 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
9f6eb1cd 692 bool properties, new_line = false;
56159e0d
LP
693 sd_bus *bus = userdata;
694 int r = 0, i;
1ee306e1
LP
695
696 assert(bus);
1ee306e1 697
56159e0d 698 properties = !strstr(argv[0], "status");
1ee306e1
LP
699
700 pager_open_if_enabled();
701
56159e0d 702 if (properties && argc <= 1) {
a1da8583 703
9f6eb1cd 704 /* If no argument is specified, inspect the manager
1ee306e1 705 * itself */
fefdc04b 706 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
8c841f21 707 if (r < 0)
9f6eb1cd 708 return r;
1ee306e1
LP
709 }
710
56159e0d 711 for (i = 1; i < argc; i++) {
1ee306e1
LP
712 const char *path = NULL;
713
a1da8583
TG
714 r = sd_bus_call_method(
715 bus,
716 "org.freedesktop.machine1",
717 "/org/freedesktop/machine1",
718 "org.freedesktop.machine1.Manager",
719 "GetMachine",
720 &error,
721 &reply,
56159e0d 722 "s", argv[i]);
a1da8583
TG
723 if (r < 0) {
724 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
725 return r;
726 }
727
728 r = sd_bus_message_read(reply, "o", &path);
5b30bef8
LP
729 if (r < 0)
730 return bus_log_parse_error(r);
1ee306e1 731
9f6eb1cd 732 if (properties)
fefdc04b 733 r = show_machine_properties(bus, path, &new_line);
9f6eb1cd 734 else
fefdc04b
LP
735 r = show_machine_info(argv[0], bus, path, &new_line);
736 }
737
738 return r;
739}
740
741typedef struct ImageStatusInfo {
742 char *name;
743 char *path;
744 char *type;
745 int read_only;
746 usec_t crtime;
747 usec_t mtime;
c19de711 748 uint64_t usage;
b6b18498 749 uint64_t limit;
c19de711 750 uint64_t usage_exclusive;
b6b18498 751 uint64_t limit_exclusive;
fefdc04b
LP
752} ImageStatusInfo;
753
754static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
755 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
756 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
b6b18498
LP
757 char bs[FORMAT_BYTES_MAX], *s3;
758 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
fefdc04b
LP
759
760 assert(bus);
761 assert(i);
762
763 if (i->name) {
764 fputs(i->name, stdout);
765 putchar('\n');
766 }
767
9a14fb62 768 if (i->type)
fefdc04b
LP
769 printf("\t Type: %s\n", i->type);
770
771 if (i->path)
772 printf("\t Path: %s\n", i->path);
773
774 printf("\t RO: %s%s%s\n",
775 i->read_only ? ansi_highlight_red() : "",
776 i->read_only ? "read-only" : "writable",
777 i->read_only ? ansi_highlight_off() : "");
778
779 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
780 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
b6b18498 781 if (s1 && s2)
fefdc04b
LP
782 printf("\t Created: %s; %s\n", s2, s1);
783 else if (s2)
784 printf("\t Created: %s\n", s2);
785
786 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
787 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
b6b18498 788 if (s1 && s2)
fefdc04b
LP
789 printf("\tModified: %s; %s\n", s2, s1);
790 else if (s2)
791 printf("\tModified: %s\n", s2);
b6b18498 792
c19de711
LP
793 s3 = format_bytes(bs, sizeof(bs), i->usage);
794 s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
b6b18498 795 if (s3 && s4)
c19de711 796 printf("\t Usage: %s (exclusive: %s)\n", s3, s4);
b6b18498 797 else if (s3)
c19de711 798 printf("\t Usage: %s\n", s3);
b6b18498
LP
799
800 s3 = format_bytes(bs, sizeof(bs), i->limit);
801 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
802 if (s3 && s4)
803 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
804 else if (s3)
805 printf("\t Limit: %s\n", s3);
fefdc04b
LP
806}
807
808static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
809
810 static const struct bus_properties_map map[] = {
b6b18498
LP
811 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
812 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
813 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
814 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
815 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
816 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
c19de711 817 { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
b6b18498 818 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
c19de711 819 { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
b6b18498 820 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
fefdc04b
LP
821 {}
822 };
823
824 ImageStatusInfo info = {};
825 int r;
826
827 assert(verb);
828 assert(bus);
829 assert(path);
830 assert(new_line);
831
832 r = bus_map_all_properties(bus,
833 "org.freedesktop.machine1",
834 path,
835 map,
836 &info);
837 if (r < 0)
838 return log_error_errno(r, "Could not get properties: %m");
839
840 if (*new_line)
841 printf("\n");
842 *new_line = true;
843
844 print_image_status_info(bus, &info);
845
846 free(info.name);
847 free(info.path);
848 free(info.type);
849
850 return r;
851}
852
853static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
854 int r;
855
856 assert(bus);
857 assert(path);
858 assert(new_line);
859
860 if (*new_line)
861 printf("\n");
862
863 *new_line = true;
864
865 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
866 if (r < 0)
867 log_error_errno(r, "Could not get properties: %m");
868
869 return r;
870}
871
872static int show_image(int argc, char *argv[], void *userdata) {
873
874 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
875 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
876 bool properties, new_line = false;
877 sd_bus *bus = userdata;
878 int r = 0, i;
879
880 assert(bus);
881
882 properties = !strstr(argv[0], "status");
883
884 pager_open_if_enabled();
885
886 if (properties && argc <= 1) {
887
888 /* If no argument is specified, inspect the manager
889 * itself */
890 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
891 if (r < 0)
892 return r;
893 }
894
895 for (i = 1; i < argc; i++) {
896 const char *path = NULL;
897
898 r = sd_bus_call_method(
899 bus,
900 "org.freedesktop.machine1",
901 "/org/freedesktop/machine1",
902 "org.freedesktop.machine1.Manager",
903 "GetImage",
904 &error,
905 &reply,
906 "s", argv[i]);
907 if (r < 0) {
908 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
909 return r;
910 }
911
912 r = sd_bus_message_read(reply, "o", &path);
913 if (r < 0)
914 return bus_log_parse_error(r);
915
916 if (properties)
917 r = show_image_properties(bus, path, &new_line);
918 else
919 r = show_image_info(argv[0], bus, path, &new_line);
1ee306e1
LP
920 }
921
9f6eb1cd 922 return r;
1ee306e1
LP
923}
924
56159e0d 925static int kill_machine(int argc, char *argv[], void *userdata) {
a1da8583 926 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d
LP
927 sd_bus *bus = userdata;
928 int i;
1ee306e1 929
56159e0d 930 assert(bus);
1ee306e1 931
acf97e21
LP
932 polkit_agent_open_if_enabled();
933
1ee306e1
LP
934 if (!arg_kill_who)
935 arg_kill_who = "all";
936
56159e0d 937 for (i = 1; i < argc; i++) {
1ee306e1
LP
938 int r;
939
a1da8583 940 r = sd_bus_call_method(
56159e0d
LP
941 bus,
942 "org.freedesktop.machine1",
943 "/org/freedesktop/machine1",
944 "org.freedesktop.machine1.Manager",
945 "KillMachine",
946 &error,
947 NULL,
948 "ssi", argv[i], arg_kill_who, arg_signal);
a1da8583
TG
949 if (r < 0) {
950 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
1ee306e1 951 return r;
a1da8583 952 }
1ee306e1
LP
953 }
954
955 return 0;
956}
957
56159e0d 958static int reboot_machine(int argc, char *argv[], void *userdata) {
1dba654b
LP
959 arg_kill_who = "leader";
960 arg_signal = SIGINT; /* sysvinit + systemd */
1ee306e1 961
56159e0d 962 return kill_machine(argc, argv, userdata);
1dba654b 963}
1ee306e1 964
56159e0d 965static int poweroff_machine(int argc, char *argv[], void *userdata) {
1dba654b
LP
966 arg_kill_who = "leader";
967 arg_signal = SIGRTMIN+4; /* only systemd */
1ee306e1 968
56159e0d 969 return kill_machine(argc, argv, userdata);
1ee306e1
LP
970}
971
56159e0d 972static int terminate_machine(int argc, char *argv[], void *userdata) {
923d8fd3 973 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d
LP
974 sd_bus *bus = userdata;
975 int i;
923d8fd3 976
56159e0d 977 assert(bus);
923d8fd3 978
acf97e21
LP
979 polkit_agent_open_if_enabled();
980
56159e0d 981 for (i = 1; i < argc; i++) {
1dba654b 982 int r;
923d8fd3
LP
983
984 r = sd_bus_call_method(
985 bus,
986 "org.freedesktop.machine1",
987 "/org/freedesktop/machine1",
988 "org.freedesktop.machine1.Manager",
1dba654b 989 "TerminateMachine",
923d8fd3 990 &error,
1dba654b 991 NULL,
56159e0d 992 "s", argv[i]);
923d8fd3 993 if (r < 0) {
1dba654b 994 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
923d8fd3
LP
995 return r;
996 }
923d8fd3
LP
997 }
998
999 return 0;
1000}
1001
785890ac
LP
1002static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
1003 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1004 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
1005 const char *object;
1006 uint32_t leader;
1007 int r;
1008
1009 assert(bus);
1010 assert(name);
1011 assert(ret);
1012
1013 r = sd_bus_call_method(
1014 bus,
1015 "org.freedesktop.machine1",
1016 "/org/freedesktop/machine1",
1017 "org.freedesktop.machine1.Manager",
1018 "GetMachine",
1019 &error,
1020 &reply,
1021 "s", name);
1022 if (r < 0) {
1023 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
1024 return r;
1025 }
1026
1027 r = sd_bus_message_read(reply, "o", &object);
1028 if (r < 0)
1029 return bus_log_parse_error(r);
1030
1031 r = sd_bus_get_property(
1032 bus,
1033 "org.freedesktop.machine1",
1034 object,
1035 "org.freedesktop.machine1.Machine",
1036 "Leader",
1037 &error,
1038 &reply2,
1039 "u");
1040 if (r < 0)
1041 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
1042
1043 r = sd_bus_message_read(reply2, "u", &leader);
1044 if (r < 0)
1045 return bus_log_parse_error(r);
1046
1047 *ret = leader;
1048 return 0;
1049}
1050
56159e0d 1051static int copy_files(int argc, char *argv[], void *userdata) {
f2cbe59e
LP
1052 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
1053 _cleanup_close_ int hostfd = -1;
56159e0d 1054 sd_bus *bus = userdata;
f2cbe59e
LP
1055 pid_t child, leader;
1056 bool copy_from;
1057 siginfo_t si;
1058 int r;
1059
56159e0d 1060 assert(bus);
f2cbe59e 1061
56159e0d
LP
1062 copy_from = streq(argv[0], "copy-from");
1063 dest = argv[3] ?: argv[2];
1064 host_path = strdupa(copy_from ? dest : argv[2]);
1065 container_path = strdupa(copy_from ? argv[2] : dest);
f2cbe59e
LP
1066
1067 if (!path_is_absolute(container_path)) {
1068 log_error("Container path not absolute.");
1069 return -EINVAL;
1070 }
1071
bb4a2282 1072 t = strdupa(host_path);
f2cbe59e
LP
1073 host_basename = basename(t);
1074 host_dirname = dirname(host_path);
1075
bb4a2282 1076 t = strdupa(container_path);
f2cbe59e
LP
1077 container_basename = basename(t);
1078 container_dirname = dirname(container_path);
1079
56159e0d 1080 r = machine_get_leader(bus, argv[1], &leader);
f2cbe59e
LP
1081 if (r < 0)
1082 return r;
1083
1084 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1085 if (r < 0)
1086 return log_error_errno(errno, "Failed to open source directory: %m");
1087
1088 child = fork();
1089 if (child < 0)
1090 return log_error_errno(errno, "Failed to fork(): %m");
1091
1092 if (child == 0) {
1093 int containerfd;
1094 const char *q;
1095 int mntfd;
1096
1097 q = procfs_file_alloca(leader, "ns/mnt");
1098 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1099 if (mntfd < 0) {
1100 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1101 _exit(EXIT_FAILURE);
1102 }
1103
1104 if (setns(mntfd, CLONE_NEWNS) < 0) {
1105 log_error_errno(errno, "Failed to join namespace of leader: %m");
1106 _exit(EXIT_FAILURE);
1107 }
1108
1109 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1110 if (containerfd < 0) {
1111 log_error_errno(errno, "Failed top open destination directory: %m");
1112 _exit(EXIT_FAILURE);
1113 }
1114
1115 if (copy_from)
1116 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
1117 else
1118 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
1119 if (r < 0) {
1120 log_error_errno(errno, "Failed to copy tree: %m");
1121 _exit(EXIT_FAILURE);
1122 }
1123
1124 _exit(EXIT_SUCCESS);
1125 }
1126
1127 r = wait_for_terminate(child, &si);
1128 if (r < 0)
1129 return log_error_errno(r, "Failed to wait for client: %m");
1130 if (si.si_code != CLD_EXITED) {
1131 log_error("Client died abnormally.");
1132 return -EIO;
1133 }
1134 if (si.si_status != EXIT_SUCCESS)
1135 return -EIO;
1136
1137 return 0;
1138}
1139
56159e0d 1140static int bind_mount(int argc, char *argv[], void *userdata) {
785890ac 1141 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
56159e0d 1142 sd_bus *bus = userdata;
785890ac
LP
1143 pid_t child, leader;
1144 const char *dest;
1145 siginfo_t si;
1146 bool mount_slave_created = false, mount_slave_mounted = false,
1147 mount_tmp_created = false, mount_tmp_mounted = false,
1148 mount_outside_created = false, mount_outside_mounted = false;
1149 int r;
1150
56159e0d
LP
1151 assert(bus);
1152
785890ac
LP
1153 /* One day, when bind mounting /proc/self/fd/n works across
1154 * namespace boundaries we should rework this logic to make
1155 * use of it... */
1156
56159e0d 1157 dest = argv[3] ?: argv[2];
785890ac
LP
1158 if (!path_is_absolute(dest)) {
1159 log_error("Destination path not absolute.");
1160 return -EINVAL;
1161 }
1162
56159e0d 1163 p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
785890ac
LP
1164 if (access(p, F_OK) < 0) {
1165 log_error("Container does not allow propagation of mount points.");
1166 return -ENOTSUP;
1167 }
1168
56159e0d 1169 r = machine_get_leader(bus, argv[1], &leader);
785890ac
LP
1170 if (r < 0)
1171 return r;
1172
1173 /* Our goal is to install a new bind mount into the container,
1174 possibly read-only. This is irritatingly complex
1175 unfortunately, currently.
1176
1177 First, we start by creating a private playground in /tmp,
1178 that we can mount MS_SLAVE. (Which is necessary, since
1179 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
1180 mounts.) */
1181
1182 if (!mkdtemp(mount_slave))
1183 return log_error_errno(errno, "Failed to create playground: %m");
1184
1185 mount_slave_created = true;
1186
1187 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
1188 r = log_error_errno(errno, "Failed to make bind mount: %m");
1189 goto finish;
1190 }
1191
1192 mount_slave_mounted = true;
1193
1194 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
1195 r = log_error_errno(errno, "Failed to remount slave: %m");
1196 goto finish;
1197 }
1198
1199 /* Second, we mount the source directory to a directory inside
1200 of our MS_SLAVE playground. */
1201 mount_tmp = strappenda(mount_slave, "/mount");
1202 if (mkdir(mount_tmp, 0700) < 0) {
1203 r = log_error_errno(errno, "Failed to create temporary mount: %m");
1204 goto finish;
1205 }
1206
1207 mount_tmp_created = true;
1208
56159e0d 1209 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
785890ac
LP
1210 r = log_error_errno(errno, "Failed to overmount: %m");
1211 goto finish;
1212 }
1213
1214 mount_tmp_mounted = true;
1215
1216 /* Third, we remount the new bind mount read-only if requested. */
1217 if (arg_read_only)
1218 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
1219 r = log_error_errno(errno, "Failed to mark read-only: %m");
1220 goto finish;
1221 }
1222
1223 /* Fourth, we move the new bind mount into the propagation
1224 * directory. This way it will appear there read-only
1225 * right-away. */
1226
56159e0d 1227 mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
785890ac
LP
1228 if (!mkdtemp(mount_outside)) {
1229 r = log_error_errno(errno, "Cannot create propagation directory: %m");
1230 goto finish;
1231 }
1232
1233 mount_outside_created = true;
1234
1235 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
1236 r = log_error_errno(errno, "Failed to move: %m");
1237 goto finish;
1238 }
1239
1240 mount_outside_mounted = true;
1241 mount_tmp_mounted = false;
1242
1243 (void) rmdir(mount_tmp);
1244 mount_tmp_created = false;
1245
1246 (void) umount(mount_slave);
1247 mount_slave_mounted = false;
1248
1249 (void) rmdir(mount_slave);
1250 mount_slave_created = false;
1251
1252 child = fork();
1253 if (child < 0) {
1254 r = log_error_errno(errno, "Failed to fork(): %m");
1255 goto finish;
1256 }
1257
1258 if (child == 0) {
1259 const char *mount_inside;
1260 int mntfd;
1261 const char *q;
1262
1263 q = procfs_file_alloca(leader, "ns/mnt");
1264 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1265 if (mntfd < 0) {
1266 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1267 _exit(EXIT_FAILURE);
1268 }
1269
1270 if (setns(mntfd, CLONE_NEWNS) < 0) {
1271 log_error_errno(errno, "Failed to join namespace of leader: %m");
1272 _exit(EXIT_FAILURE);
1273 }
1274
1275 if (arg_mkdir)
1276 mkdir_p(dest, 0755);
1277
1278 /* Fifth, move the mount to the right place inside */
1279 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
1280 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
1281 log_error_errno(errno, "Failed to mount: %m");
1282 _exit(EXIT_FAILURE);
1283 }
1284
1285 _exit(EXIT_SUCCESS);
1286 }
1287
1288 r = wait_for_terminate(child, &si);
1289 if (r < 0) {
1290 log_error_errno(r, "Failed to wait for client: %m");
1291 goto finish;
1292 }
1293 if (si.si_code != CLD_EXITED) {
1294 log_error("Client died abnormally.");
1295 r = -EIO;
1296 goto finish;
1297 }
1298 if (si.si_status != EXIT_SUCCESS) {
1299 r = -EIO;
1300 goto finish;
1301 }
1302
1303 r = 0;
1304
1305finish:
1306 if (mount_outside_mounted)
1307 umount(mount_outside);
1308 if (mount_outside_created)
1309 rmdir(mount_outside);
1310
1311 if (mount_tmp_mounted)
1312 umount(mount_tmp);
1313 if (mount_tmp_created)
1314 umount(mount_tmp);
1315
1316 if (mount_slave_mounted)
1317 umount(mount_slave);
1318 if (mount_slave_created)
1319 umount(mount_slave);
1320
1321 return r;
1322}
1323
0ec5543c
LP
1324static int on_machine_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1325 PTYForward ** forward = (PTYForward**) userdata;
1326 int r;
1327
1328 assert(bus);
1329 assert(m);
1330 assert(forward);
1331
1332 if (*forward) {
1333 /* If the forwarder is already initialized, tell it to
da054c37
LP
1334 * exit on the next vhangup(), so that we still flush
1335 * out what might be queued and exit then. */
0ec5543c 1336
da054c37 1337 r = pty_forward_set_ignore_vhangup(*forward, false);
0ec5543c
LP
1338 if (r >= 0)
1339 return 0;
1340
da054c37 1341 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
0ec5543c
LP
1342 }
1343
1344 /* On error, or when the forwarder is not initialized yet, quit immediately */
1345 sd_event_exit(sd_bus_get_event(bus), EXIT_FAILURE);
1346 return 0;
1347}
1348
56159e0d 1349static int login_machine(int argc, char *argv[], void *userdata) {
04d39279 1350 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
d04c1fb8 1351 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
0ec5543c 1352 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
023fb90b
LP
1353 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1354 _cleanup_event_unref_ sd_event *event = NULL;
40205d70 1355 int master = -1, r, ret = 0;
56159e0d 1356 sd_bus *bus = userdata;
0ec5543c 1357 const char *pty, *match;
04d39279 1358 sigset_t mask;
c7b7d449 1359 char last_char = 0;
0ec5543c 1360 bool machine_died;
04d39279
LP
1361
1362 assert(bus);
04d39279 1363
bf441e3d 1364 if (arg_transport != BUS_TRANSPORT_LOCAL &&
de33fc62 1365 arg_transport != BUS_TRANSPORT_MACHINE) {
923d8fd3 1366 log_error("Login only supported on local machines.");
04d39279
LP
1367 return -ENOTSUP;
1368 }
1369
acf97e21
LP
1370 polkit_agent_open_if_enabled();
1371
023fb90b 1372 r = sd_event_default(&event);
f647962d
MS
1373 if (r < 0)
1374 return log_error_errno(r, "Failed to get event loop: %m");
023fb90b
LP
1375
1376 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
1377 if (r < 0)
1378 return log_error_errno(r, "Failed to attach bus to event loop: %m");
023fb90b 1379
0ec5543c
LP
1380 match = strappenda("type='signal',"
1381 "sender='org.freedesktop.machine1',"
1382 "path='/org/freedesktop/machine1',",
1383 "interface='org.freedesktop.machine1.Manager',"
1384 "member='MachineRemoved',"
1385 "arg0='",
1386 argv[1],
1387 "'");
1388
1389 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1390 if (r < 0)
1391 return log_error_errno(r, "Failed to add machine removal match: %m");
1392
d04c1fb8
LP
1393 r = sd_bus_message_new_method_call(bus,
1394 &m,
1395 "org.freedesktop.machine1",
1396 "/org/freedesktop/machine1",
1397 "org.freedesktop.machine1.Manager",
1398 "OpenMachineLogin");
1399 if (r < 0)
1400 return bus_log_create_error(r);
1401
acf97e21 1402 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
d04c1fb8
LP
1403 if (r < 0)
1404 return bus_log_create_error(r);
1405
1406 r = sd_bus_message_append(m, "s", argv[1]);
1407 if (r < 0)
1408 return bus_log_create_error(r);
1409
1410 r = sd_bus_call(bus, m, 0, &error, &reply);
40205d70
LP
1411 if (r < 0) {
1412 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
785890ac 1413 return r;
40205d70 1414 }
04d39279 1415
40205d70
LP
1416 r = sd_bus_message_read(reply, "hs", &master, &pty);
1417 if (r < 0)
ee451d76 1418 return bus_log_parse_error(r);
04d39279 1419
04d39279
LP
1420 assert_se(sigemptyset(&mask) == 0);
1421 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1422 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1423
0ec5543c 1424 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
04d39279 1425
023fb90b
LP
1426 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1427 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1428
9b15b784 1429 r = pty_forward_new(event, master, true, &forward);
f647962d
MS
1430 if (r < 0)
1431 return log_error_errno(r, "Failed to create PTY forwarder: %m");
023fb90b
LP
1432
1433 r = sd_event_loop(event);
f647962d
MS
1434 if (r < 0)
1435 return log_error_errno(r, "Failed to run event loop: %m");
04d39279 1436
0ec5543c 1437 pty_forward_get_last_char(forward, &last_char);
da054c37 1438 machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
c7b7d449 1439
023fb90b
LP
1440 forward = pty_forward_free(forward);
1441
c7b7d449
LP
1442 if (last_char != '\n')
1443 fputc('\n', stdout);
04d39279 1444
0ec5543c
LP
1445 if (machine_died)
1446 log_info("Machine %s terminated.", argv[1]);
1447 else
1448 log_info("Connection to machine %s terminated.", argv[1]);
04d39279 1449
023fb90b
LP
1450 sd_event_get_exit_code(event, &ret);
1451 return ret;
04d39279
LP
1452}
1453
08682124
LP
1454static int remove_image(int argc, char *argv[], void *userdata) {
1455 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1456 sd_bus *bus = userdata;
ebd93cb6 1457 int r, i;
08682124
LP
1458
1459 assert(bus);
1460
acf97e21
LP
1461 polkit_agent_open_if_enabled();
1462
08682124 1463 for (i = 1; i < argc; i++) {
08682124
LP
1464 r = sd_bus_call_method(
1465 bus,
1466 "org.freedesktop.machine1",
1467 "/org/freedesktop/machine1",
1468 "org.freedesktop.machine1.Manager",
1469 "RemoveImage",
1470 &error,
1471 NULL,
1472 "s", argv[i]);
1473 if (r < 0) {
1474 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1475 return r;
1476 }
1477 }
1478
1479 return 0;
1480}
1481
ebd93cb6
LP
1482static int rename_image(int argc, char *argv[], void *userdata) {
1483 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1484 sd_bus *bus = userdata;
1485 int r;
1486
acf97e21
LP
1487 polkit_agent_open_if_enabled();
1488
ebd93cb6
LP
1489 r = sd_bus_call_method(
1490 bus,
1491 "org.freedesktop.machine1",
1492 "/org/freedesktop/machine1",
1493 "org.freedesktop.machine1.Manager",
1494 "RenameImage",
1495 &error,
1496 NULL,
1497 "ss", argv[1], argv[2]);
1498 if (r < 0) {
1499 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1500 return r;
1501 }
1502
1503 return 0;
1504}
1505
1506static int clone_image(int argc, char *argv[], void *userdata) {
1507 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1508 sd_bus *bus = userdata;
1509 int r;
1510
acf97e21
LP
1511 polkit_agent_open_if_enabled();
1512
ebd93cb6
LP
1513 r = sd_bus_call_method(
1514 bus,
1515 "org.freedesktop.machine1",
1516 "/org/freedesktop/machine1",
1517 "org.freedesktop.machine1.Manager",
1518 "CloneImage",
1519 &error,
1520 NULL,
1521 "ssb", argv[1], argv[2], arg_read_only);
1522 if (r < 0) {
1523 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1524 return r;
1525 }
1526
1527 return 0;
1528}
1529
1530static int read_only_image(int argc, char *argv[], void *userdata) {
1531 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1532 sd_bus *bus = userdata;
1533 int b = true, r;
1534
1535 if (argc > 2) {
1536 b = parse_boolean(argv[2]);
1537 if (b < 0) {
1538 log_error("Failed to parse boolean argument: %s", argv[2]);
1539 return -EINVAL;
1540 }
1541 }
1542
acf97e21
LP
1543 polkit_agent_open_if_enabled();
1544
ebd93cb6
LP
1545 r = sd_bus_call_method(
1546 bus,
1547 "org.freedesktop.machine1",
1548 "/org/freedesktop/machine1",
1549 "org.freedesktop.machine1.Manager",
1550 "MarkImageReadOnly",
1551 &error,
1552 NULL,
1553 "sb", argv[1], b);
1554 if (r < 0) {
1555 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1556 return r;
1557 }
1558
1559 return 0;
1560}
1561
ebd011d9
LP
1562static int start_machine(int argc, char *argv[], void *userdata) {
1563 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1564 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1565 sd_bus *bus = userdata;
1566 int r, i;
1567
1568 assert(bus);
1569
acf97e21
LP
1570 polkit_agent_open_if_enabled();
1571
ebd011d9
LP
1572 r = bus_wait_for_jobs_new(bus, &w);
1573 if (r < 0)
1574 return log_oom();
1575
1576 for (i = 1; i < argc; i++) {
1577 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1578 _cleanup_free_ char *e = NULL, *unit = NULL;
1579 const char *object;
1580
1581 if (!machine_name_is_valid(argv[i])) {
1582 log_error("Invalid machine name %s.", argv[i]);
1583 return -EINVAL;
1584 }
1585
1586 e = unit_name_escape(argv[i]);
1587 if (!e)
1588 return log_oom();
1589
1590 unit = unit_name_build("systemd-nspawn", e, ".service");
1591 if (!unit)
1592 return log_oom();
1593
1594 r = sd_bus_message_new_method_call(
1595 bus,
1596 &m,
1597 "org.freedesktop.systemd1",
1598 "/org/freedesktop/systemd1",
1599 "org.freedesktop.systemd1.Manager",
1600 "StartUnit");
1601 if (r < 0)
1602 return bus_log_create_error(r);
1603
acf97e21 1604 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
ebd011d9
LP
1605 if (r < 0)
1606 return bus_log_create_error(r);
1607
1608 r = sd_bus_message_append(m, "ss", unit, "fail");
1609 if (r < 0)
1610 return bus_log_create_error(r);
1611
1612 r = sd_bus_call(bus, m, 0, &error, &reply);
1613 if (r < 0) {
1614 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1615 return r;
1616 }
1617
1618 r = sd_bus_message_read(reply, "o", &object);
1619 if (r < 0)
1620 return bus_log_parse_error(r);
1621
1622 r = bus_wait_for_jobs_add(w, object);
1623 if (r < 0)
1624 return log_oom();
1625 }
1626
d8f52ed2 1627 r = bus_wait_for_jobs(w, arg_quiet);
ebd011d9
LP
1628 if (r < 0)
1629 return r;
1630
1631 return 0;
1632}
1633
d8f52ed2
LP
1634static int enable_machine(int argc, char *argv[], void *userdata) {
1635 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1636 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1637 int carries_install_info = 0;
1638 const char *method = NULL;
1639 sd_bus *bus = userdata;
1640 int r, i;
1641
1642 assert(bus);
1643
acf97e21
LP
1644 polkit_agent_open_if_enabled();
1645
d8f52ed2
LP
1646 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1647
1648 r = sd_bus_message_new_method_call(
1649 bus,
1650 &m,
1651 "org.freedesktop.systemd1",
1652 "/org/freedesktop/systemd1",
1653 "org.freedesktop.systemd1.Manager",
1654 method);
1655 if (r < 0)
1656 return bus_log_create_error(r);
1657
acf97e21 1658 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
d8f52ed2
LP
1659 if (r < 0)
1660 return bus_log_create_error(r);
1661
1662 r = sd_bus_message_open_container(m, 'a', "s");
1663 if (r < 0)
1664 return bus_log_create_error(r);
1665
1666 for (i = 1; i < argc; i++) {
1667 _cleanup_free_ char *e = NULL, *unit = NULL;
1668
1669 if (!machine_name_is_valid(argv[i])) {
1670 log_error("Invalid machine name %s.", argv[i]);
1671 return -EINVAL;
1672 }
1673
1674 e = unit_name_escape(argv[i]);
1675 if (!e)
1676 return log_oom();
1677
1678 unit = unit_name_build("systemd-nspawn", e, ".service");
1679 if (!unit)
1680 return log_oom();
1681
1682 r = sd_bus_message_append(m, "s", unit);
1683 if (r < 0)
1684 return bus_log_create_error(r);
1685 }
1686
1687 r = sd_bus_message_close_container(m);
1688 if (r < 0)
1689 return bus_log_create_error(r);
1690
1691 if (streq(argv[0], "enable"))
1692 r = sd_bus_message_append(m, "bb", false, false);
1693 else
1694 r = sd_bus_message_append(m, "b", false);
1695 if (r < 0)
1696 return bus_log_create_error(r);
1697
1698 r = sd_bus_call(bus, m, 0, &error, &reply);
1699 if (r < 0) {
1700 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1701 return r;
1702 }
1703
1704 if (streq(argv[0], "enable")) {
1705 r = sd_bus_message_read(reply, "b", carries_install_info);
1706 if (r < 0)
1707 return bus_log_parse_error(r);
1708 }
1709
1710 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1711 if (r < 0)
1712 return r;
1713
1714 m = sd_bus_message_unref(m);
1715
1716 r = sd_bus_message_new_method_call(
1717 bus,
1718 &m,
1719 "org.freedesktop.systemd1",
1720 "/org/freedesktop/systemd1",
1721 "org.freedesktop.systemd1.Manager",
1722 "Reload");
1723 if (r < 0)
1724 return bus_log_create_error(r);
1725
acf97e21 1726 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
d8f52ed2
LP
1727 if (r < 0)
1728 return bus_log_create_error(r);
1729
1730 r = sd_bus_call(bus, m, 0, &error, NULL);
1731 if (r < 0) {
1732 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1733 return r;
1734 }
1735
1736 return 0;
1737}
1738
3d7415f4
LP
1739typedef struct PullContext {
1740 const char *path;
1741 int result;
1742} PullContext;
1743
1744static int match_log_message(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1745 PullContext *c = userdata;
1746 const char *line;
1747 unsigned priority;
1748 int r;
1749
1750 assert(bus);
1751 assert(m);
1752
1753 r = sd_bus_message_read(m, "us", &priority, &line);
1754 if (r < 0) {
1755 bus_log_parse_error(r);
1756 return 0;
1757 }
1758
1759 if (!streq_ptr(c->path, sd_bus_message_get_path(m)))
1760 return 0;
1761
1762 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1763 return 0;
1764
1765 log_full(priority, "%s", line);
1766 return 0;
1767}
1768
1769static int match_transfer_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1770 PullContext *c = userdata;
1771 const char *path, *result;
1772 uint32_t id;
1773 int r;
1774
1775 assert(bus);
1776 assert(m);
1777 assert(c);
1778
1779 r = sd_bus_message_read(m, "uos", &id, &path, &result);
1780 if (r < 0) {
1781 bus_log_parse_error(r);
1782 return 0;
1783 }
1784
1785 if (!streq_ptr(c->path, path))
1786 return 0;
1787
1788 c->result = streq_ptr(result, "done");
1789 return 0;
1790}
1791
1792static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
1793 _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1794 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1795 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1796 PullContext c = {
1797 .result = -1,
1798 };
1799 uint32_t id;
1800 int r;
1801
1802 assert(bus);
1803 assert(m);
1804
1805 polkit_agent_open_if_enabled();
1806
1807 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1808 if (r < 0)
1809 return bus_log_create_error(r);
1810
1811 r = sd_bus_add_match(
1812 bus,
1813 &slot_job_removed,
1814 "type='signal',"
1815 "sender='org.freedesktop.import1',"
1816 "interface='org.freedesktop.import1.Manager',"
1817 "member='TransferRemoved',"
1818 "path='/org/freedesktop/import1'",
1819 match_transfer_removed, &c);
1820 if (r < 0)
1821 return log_error_errno(r, "Failed to install match: %m");
1822
1823 r = sd_bus_add_match(
1824 bus,
1825 &slot_log_message,
1826 "type='signal',"
1827 "sender='org.freedesktop.import1',"
1828 "interface='org.freedesktop.import1.Transfer',"
1829 "member='LogMessage'",
1830 match_log_message, &c);
1831 if (r < 0)
1832 return log_error_errno(r, "Failed to install match: %m");
1833
1834 r = sd_bus_call(bus, m, 0, &error, &reply);
1835 if (r < 0) {
1836 log_error("Failed pull image: %s", bus_error_message(&error, -r));
1837 return r;
1838 }
1839
1840 r = sd_bus_message_read(reply, "uo", &id, &c.path);
1841 if (r < 0)
1842 return bus_log_parse_error(r);
1843
1844 for (;;) {
1845 r = sd_bus_process(bus, NULL);
1846 if (r < 0)
1847 return r;
1848
1849 /* The match sets this to NULL when we are done */
1850 if (c.result >= 0)
1851 break;
1852
1853 r = sd_bus_wait(bus, (uint64_t) -1);
1854 if (r < 0)
1855 return r;
1856 }
1857
1858 return c.result ? 0 : -EINVAL;
1859}
1860
1861static int pull_tar(int argc, char *argv[], void *userdata) {
1862 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1863 _cleanup_free_ char *l = NULL, *ll = NULL;
1864 const char *local, *remote;
1865 sd_bus *bus = userdata;
1866 int r;
1867
1868 assert(bus);
1869
1870 remote = argv[1];
1871 if (!http_url_is_valid(remote)) {
1872 log_error("URL '%s' is not valid.", remote);
1873 return -EINVAL;
1874 }
1875
1876 if (argc >= 3)
1877 local = argv[2];
1878 else {
1879 r = import_url_last_component(remote, &l);
1880 if (r < 0)
1881 return log_error_errno(r, "Failed to get final component of URL: %m");
1882
1883 local = l;
1884 }
1885
1886 if (isempty(local) || streq(local, "-"))
1887 local = NULL;
1888
1889 if (local) {
1890 r = tar_strip_suffixes(local, &ll);
1891 if (r < 0)
1892 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1893
1894 local = ll;
1895
1896 if (!machine_name_is_valid(local)) {
1897 log_error("Local name %s is not a suitable machine name.", local);
1898 return -EINVAL;
1899 }
1900 }
1901
1902 r = sd_bus_message_new_method_call(
1903 bus,
1904 &m,
1905 "org.freedesktop.import1",
1906 "/org/freedesktop/import1",
1907 "org.freedesktop.import1.Manager",
1908 "PullTar");
1909 if (r < 0)
1910 return bus_log_create_error(r);
1911
1912 r = sd_bus_message_append(
1913 m,
1914 "sssb",
1915 remote,
1916 local,
1917 arg_verify,
1918 arg_force);
1919 if (r < 0)
1920 return bus_log_create_error(r);
1921
1922 return pull_image_common(bus, m);
1923}
1924
1925static int pull_raw(int argc, char *argv[], void *userdata) {
1926 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1927 _cleanup_free_ char *l = NULL, *ll = NULL;
1928 const char *local, *remote;
1929 sd_bus *bus = userdata;
1930 int r;
1931
1932 assert(bus);
1933
1934 remote = argv[1];
1935 if (!http_url_is_valid(remote)) {
1936 log_error("URL '%s' is not valid.", remote);
1937 return -EINVAL;
1938 }
1939
1940 if (argc >= 3)
1941 local = argv[2];
1942 else {
1943 r = import_url_last_component(remote, &l);
1944 if (r < 0)
1945 return log_error_errno(r, "Failed to get final component of URL: %m");
1946
1947 local = l;
1948 }
1949
1950 if (isempty(local) || streq(local, "-"))
1951 local = NULL;
1952
1953 if (local) {
1954 r = raw_strip_suffixes(local, &ll);
1955 if (r < 0)
1956 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1957
1958 local = ll;
1959
1960 if (!machine_name_is_valid(local)) {
1961 log_error("Local name %s is not a suitable machine name.", local);
1962 return -EINVAL;
1963 }
1964 }
1965
1966 r = sd_bus_message_new_method_call(
1967 bus,
1968 &m,
1969 "org.freedesktop.import1",
1970 "/org/freedesktop/import1",
1971 "org.freedesktop.import1.Manager",
1972 "PullRaw");
1973 if (r < 0)
1974 return bus_log_create_error(r);
1975
1976 r = sd_bus_message_append(
1977 m,
1978 "sssb",
1979 remote,
1980 local,
1981 arg_verify,
1982 arg_force);
1983 if (r < 0)
1984 return bus_log_create_error(r);
1985
1986 return pull_image_common(bus, m);
1987}
1988
1989static int pull_dkr(int argc, char *argv[], void *userdata) {
1990 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1991 _cleanup_free_ char *l = NULL, *ll = NULL;
1992 const char *local, *remote, *tag;
1993 sd_bus *bus = userdata;
1994 int r;
1995
1996 if (!streq_ptr(arg_dkr_index_url, "no")) {
1997 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
1998 return -EINVAL;
1999 }
2000
2001 remote = argv[1];
2002 tag = strchr(remote, ':');
2003 if (tag) {
2004 remote = strndupa(remote, tag - remote);
2005 tag++;
2006 }
2007
2008 if (!dkr_name_is_valid(remote)) {
2009 log_error("DKR name '%s' is invalid.", remote);
2010 return -EINVAL;
2011 }
2012 if (tag && !dkr_tag_is_valid(tag)) {
2013 log_error("DKR tag '%s' is invalid.", remote);
2014 return -EINVAL;
2015 }
2016
2017 if (argc >= 3)
2018 local = argv[2];
2019 else {
2020 local = strchr(remote, '/');
2021 if (local)
2022 local++;
2023 else
2024 local = remote;
2025 }
2026
2027 if (isempty(local) || streq(local, "-"))
2028 local = NULL;
2029
2030 if (local) {
2031 if (!machine_name_is_valid(local)) {
2032 log_error("Local name %s is not a suitable machine name.", local);
2033 return -EINVAL;
2034 }
2035 }
2036
2037 r = sd_bus_message_new_method_call(
2038 bus,
2039 &m,
2040 "org.freedesktop.import1",
2041 "/org/freedesktop/import1",
2042 "org.freedesktop.import1.Manager",
2043 "PullDkr");
2044 if (r < 0)
2045 return bus_log_create_error(r);
2046
2047 r = sd_bus_message_append(
2048 m,
2049 "sssssb",
2050 arg_dkr_index_url,
2051 remote,
2052 tag,
2053 local,
2054 arg_verify,
2055 arg_force);
2056 if (r < 0)
2057 return bus_log_create_error(r);
2058
2059 return pull_image_common(bus, m);
2060}
2061
2062typedef struct TransferInfo {
2063 uint32_t id;
2064 const char *type;
2065 const char *remote;
2066 const char *local;
2067} TransferInfo;
2068
2069static int compare_transfer_info(const void *a, const void *b) {
2070 const TransferInfo *x = a, *y = b;
2071
2072 return strcmp(x->local, y->local);
2073}
2074
2075static int list_transfers(int argc, char *argv[], void *userdata) {
2076 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
2077 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
2078 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2079 _cleanup_free_ TransferInfo *transfers = NULL;
2080 size_t n_transfers = 0, n_allocated = 0, j;
2081 const char *type, *remote, *local, *object;
2082 sd_bus *bus = userdata;
2083 uint32_t id, max_id = 0;
2084 int r;
2085
2086 pager_open_if_enabled();
2087
2088 r = sd_bus_call_method(
2089 bus,
2090 "org.freedesktop.import1",
2091 "/org/freedesktop/import1",
2092 "org.freedesktop.import1.Manager",
2093 "ListTransfers",
2094 &error,
2095 &reply,
2096 NULL);
2097 if (r < 0) {
2098 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2099 return r;
2100 }
2101
2102 r = sd_bus_message_enter_container(reply, 'a', "(ussso)");
2103 if (r < 0)
2104 return bus_log_parse_error(r);
2105
2106 while ((r = sd_bus_message_read(reply, "(ussso)", &id, &type, &remote, &local, &object)) > 0) {
2107 size_t l;
2108
2109 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2110 return log_oom();
2111
2112 transfers[n_transfers].id = id;
2113 transfers[n_transfers].type = type;
2114 transfers[n_transfers].remote = remote;
2115 transfers[n_transfers].local = local;
2116
2117 l = strlen(type);
2118 if (l > max_type)
2119 max_type = l;
2120
2121 l = strlen(remote);
2122 if (l > max_remote)
2123 max_remote = l;
2124
2125 l = strlen(local);
2126 if (l > max_local)
2127 max_local = l;
2128
2129 if (id > max_id)
2130 max_id = id;
2131
2132 n_transfers ++;
2133 }
2134 if (r < 0)
2135 return bus_log_parse_error(r);
2136
2137 r = sd_bus_message_exit_container(reply);
2138 if (r < 0)
2139 return bus_log_parse_error(r);
2140
2141 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2142
2143 if (arg_legend)
2144 printf("%-*s %-*s %-*s %-*s\n",
2145 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
2146 (int) max_type, "TYPE",
2147 (int) max_local, "LOCAL",
2148 (int) max_remote, "REMOTE");
2149
2150 for (j = 0; j < n_transfers; j++)
2151 printf("%*" PRIu32 " %-*s %-*s %-*s\n",
2152 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
2153 (int) max_type, transfers[j].type,
2154 (int) max_local, transfers[j].local,
2155 (int) max_remote, transfers[j].remote);
2156
2157 if (arg_legend)
2158 printf("\n%zu transfers listed.\n", n_transfers);
2159
2160 return 0;
2161}
2162
2163static int cancel_transfer(int argc, char *argv[], void *userdata) {
2164 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2165 sd_bus *bus = userdata;
2166 int r, i;
2167
2168 assert(bus);
2169
2170 polkit_agent_open_if_enabled();
2171
2172 for (i = 1; i < argc; i++) {
2173 uint32_t id;
2174
2175 r = safe_atou32(argv[i], &id);
2176 if (r < 0)
2177 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2178
2179 r = sd_bus_call_method(
2180 bus,
2181 "org.freedesktop.import1",
2182 "/org/freedesktop/import1",
2183 "org.freedesktop.import1.Manager",
2184 "CancelTransfer",
2185 &error,
2186 NULL,
2187 "u", id);
2188 if (r < 0) {
2189 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2190 return r;
2191 }
2192 }
2193
2194 return 0;
2195}
2196
56159e0d
LP
2197static int help(int argc, char *argv[], void *userdata) {
2198
1ee306e1 2199 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
f2cbe59e
LP
2200 "Send control commands to or query the virtual machine and container\n"
2201 "registration manager.\n\n"
2202 " -h --help Show this help\n"
2203 " --version Show package version\n"
2204 " --no-pager Do not pipe output into a pager\n"
2205 " --no-legend Do not show the headers and footers\n"
acf97e21 2206 " --no-ask-password Do not ask for system passwords\n"
f2cbe59e
LP
2207 " -H --host=[USER@]HOST Operate on remote host\n"
2208 " -M --machine=CONTAINER Operate on local container\n"
2209 " -p --property=NAME Show only properties by this name\n"
d8f52ed2 2210 " -q --quiet Suppress output\n"
f2cbe59e
LP
2211 " -a --all Show all properties, including empty ones\n"
2212 " -l --full Do not ellipsize output\n"
2213 " --kill-who=WHO Who to send signal to\n"
2214 " -s --signal=SIGNAL Which signal to send\n"
2215 " --read-only Create read-only bind mount\n"
8b0cc9a3
LP
2216 " --mkdir Create directory before bind mounting, if missing\n"
2217 " -n --lines=INTEGER Number of journal entries to show\n"
2218 " -o --output=STRING Change journal output mode (short,\n"
2219 " short-monotonic, verbose, export, json,\n"
3d7415f4 2220 " json-pretty, json-sse, cat)\n"
7f444afa
LP
2221 " --verify=MODE Verification mode for downloaded images (no,\n"
2222 " checksum, signature)\n"
3d7415f4
LP
2223 " --force Download image even if already exists\n"
2224 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2225 " downloads\n\n"
cd61c3bf 2226 "Machine Commands:\n"
f2cbe59e 2227 " list List running VMs and containers\n"
fefdc04b 2228 " status NAME... Show VM/container details\n"
f2cbe59e 2229 " show NAME... Show properties of one or more VMs/containers\n"
ebd011d9 2230 " start NAME... Start container as a service\n"
e45fc5e7 2231 " login NAME Get a login prompt on a container\n"
d8f52ed2
LP
2232 " enable NAME... Enable automatic container start at boot\n"
2233 " disable NAME... Disable automatic container start at boot\n"
f2cbe59e
LP
2234 " poweroff NAME... Power off one or more containers\n"
2235 " reboot NAME... Reboot one or more containers\n"
f2cbe59e 2236 " terminate NAME... Terminate one or more VMs/containers\n"
ebd93cb6 2237 " kill NAME... Send signal to processes of a VM/container\n"
f2cbe59e 2238 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
ebd93cb6
LP
2239 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2240 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
fefdc04b 2241 "Image Commands:\n"
b5b38b41 2242 " list-images Show available container annd VM images\n"
fefdc04b 2243 " image-status NAME... Show image details\n"
08682124 2244 " show-image NAME... Show properties of image\n"
ebd93cb6
LP
2245 " clone NAME NAME Clone an image\n"
2246 " rename NAME NAME Rename an image\n"
2247 " read-only NAME [BOOL] Mark or unmark image read-only\n"
3d7415f4 2248 " remove NAME... Remove an image\n\n"
b5b38b41
LP
2249 "Image Transfer Commands:\n"
2250 " pull-tar URL [NAME] Download a TAR container image\n"
2251 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2252 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2253 " list-transfers Show list of downloads in progress\n"
3d7415f4 2254 " cancel-transfer Cancel a download\n"
f7621db0 2255 , program_invocation_short_name);
56159e0d
LP
2256
2257 return 0;
1ee306e1
LP
2258}
2259
2260static int parse_argv(int argc, char *argv[]) {
2261
2262 enum {
2263 ARG_VERSION = 0x100,
2264 ARG_NO_PAGER,
e56056e9 2265 ARG_NO_LEGEND,
1ee306e1 2266 ARG_KILL_WHO,
785890ac
LP
2267 ARG_READ_ONLY,
2268 ARG_MKDIR,
acf97e21 2269 ARG_NO_ASK_PASSWORD,
3d7415f4
LP
2270 ARG_VERIFY,
2271 ARG_FORCE,
2272 ARG_DKR_INDEX_URL,
1ee306e1
LP
2273 };
2274
2275 static const struct option options[] = {
2276 { "help", no_argument, NULL, 'h' },
2277 { "version", no_argument, NULL, ARG_VERSION },
2278 { "property", required_argument, NULL, 'p' },
2279 { "all", no_argument, NULL, 'a' },
2280 { "full", no_argument, NULL, 'l' },
2281 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
e56056e9 2282 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1ee306e1
LP
2283 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2284 { "signal", required_argument, NULL, 's' },
2285 { "host", required_argument, NULL, 'H' },
a7893c6b 2286 { "machine", required_argument, NULL, 'M' },
785890ac
LP
2287 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2288 { "mkdir", no_argument, NULL, ARG_MKDIR },
d8f52ed2 2289 { "quiet", no_argument, NULL, 'q' },
8b0cc9a3
LP
2290 { "lines", required_argument, NULL, 'n' },
2291 { "output", required_argument, NULL, 'o' },
acf97e21 2292 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
3d7415f4
LP
2293 { "verify", required_argument, NULL, ARG_VERIFY },
2294 { "force", no_argument, NULL, ARG_FORCE },
2295 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
eb9da376 2296 {}
1ee306e1
LP
2297 };
2298
a7893c6b 2299 int c, r;
1ee306e1
LP
2300
2301 assert(argc >= 0);
2302 assert(argv);
2303
8b0cc9a3 2304 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
1ee306e1
LP
2305
2306 switch (c) {
2307
2308 case 'h':
56159e0d 2309 return help(0, NULL, NULL);
1ee306e1
LP
2310
2311 case ARG_VERSION:
2312 puts(PACKAGE_STRING);
2313 puts(SYSTEMD_FEATURES);
2314 return 0;
2315
a7893c6b
LP
2316 case 'p':
2317 r = strv_extend(&arg_property, optarg);
2318 if (r < 0)
2319 return log_oom();
1ee306e1
LP
2320
2321 /* If the user asked for a particular
2322 * property, show it to him, even if it is
2323 * empty. */
2324 arg_all = true;
2325 break;
1ee306e1
LP
2326
2327 case 'a':
2328 arg_all = true;
2329 break;
2330
2331 case 'l':
2332 arg_full = true;
2333 break;
2334
8b0cc9a3
LP
2335 case 'n':
2336 if (safe_atou(optarg, &arg_lines) < 0) {
2337 log_error("Failed to parse lines '%s'", optarg);
2338 return -EINVAL;
2339 }
2340 break;
2341
2342 case 'o':
2343 arg_output = output_mode_from_string(optarg);
2344 if (arg_output < 0) {
2345 log_error("Unknown output '%s'.", optarg);
2346 return -EINVAL;
2347 }
2348 break;
2349
1ee306e1
LP
2350 case ARG_NO_PAGER:
2351 arg_no_pager = true;
2352 break;
2353
e56056e9
TA
2354 case ARG_NO_LEGEND:
2355 arg_legend = false;
2356 break;
2357
1ee306e1
LP
2358 case ARG_KILL_WHO:
2359 arg_kill_who = optarg;
2360 break;
2361
2362 case 's':
2363 arg_signal = signal_from_string_try_harder(optarg);
2364 if (arg_signal < 0) {
2365 log_error("Failed to parse signal string %s.", optarg);
2366 return -EINVAL;
2367 }
2368 break;
2369
acf97e21
LP
2370 case ARG_NO_ASK_PASSWORD:
2371 arg_ask_password = false;
2372 break;
2373
1ee306e1 2374 case 'H':
d21ed1ea 2375 arg_transport = BUS_TRANSPORT_REMOTE;
a7893c6b
LP
2376 arg_host = optarg;
2377 break;
2378
2379 case 'M':
de33fc62 2380 arg_transport = BUS_TRANSPORT_MACHINE;
a7893c6b 2381 arg_host = optarg;
1ee306e1
LP
2382 break;
2383
785890ac
LP
2384 case ARG_READ_ONLY:
2385 arg_read_only = true;
2386 break;
2387
2388 case ARG_MKDIR:
2389 arg_mkdir = true;
2390 break;
2391
d8f52ed2
LP
2392 case 'q':
2393 arg_quiet = true;
2394 break;
2395
3d7415f4
LP
2396 case ARG_VERIFY:
2397 arg_verify = optarg;
2398 break;
2399
2400 case ARG_FORCE:
2401 arg_force = true;
2402 break;
2403
2404 case ARG_DKR_INDEX_URL:
2405 if (!http_url_is_valid(optarg)) {
2406 log_error("Index URL is invalid: %s", optarg);
2407 return -EINVAL;
2408 }
2409
2410 arg_dkr_index_url = optarg;
2411 break;
2412
1ee306e1
LP
2413 case '?':
2414 return -EINVAL;
2415
2416 default:
eb9da376 2417 assert_not_reached("Unhandled option");
1ee306e1 2418 }
1ee306e1
LP
2419
2420 return 1;
2421}
2422
56159e0d
LP
2423static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2424
2425 static const Verb verbs[] = {
3d7415f4
LP
2426 { "help", VERB_ANY, VERB_ANY, 0, help },
2427 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2428 { "list-images", VERB_ANY, 1, 0, list_images },
2429 { "status", 2, VERB_ANY, 0, show_machine },
2430 { "image-status", 2, VERB_ANY, 0, show_image },
2431 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2432 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2433 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2434 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2435 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2436 { "kill", 2, VERB_ANY, 0, kill_machine },
2437 { "login", 2, 2, 0, login_machine },
2438 { "bind", 3, 4, 0, bind_mount },
2439 { "copy-to", 3, 4, 0, copy_files },
2440 { "copy-from", 3, 4, 0, copy_files },
2441 { "remove", 2, VERB_ANY, 0, remove_image },
2442 { "rename", 3, 3, 0, rename_image },
2443 { "clone", 3, 3, 0, clone_image },
2444 { "read-only", 2, 3, 0, read_only_image },
2445 { "start", 2, VERB_ANY, 0, start_machine },
2446 { "enable", 2, VERB_ANY, 0, enable_machine },
2447 { "disable", 2, VERB_ANY, 0, enable_machine },
2448 { "pull-tar", 2, 3, 0, pull_tar },
2449 { "pull-raw", 2, 3, 0, pull_raw },
2450 { "pull-dkr", 2, 3, 0, pull_dkr },
2451 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2452 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
56159e0d 2453 {}
1ee306e1
LP
2454 };
2455
56159e0d 2456 return dispatch_verb(argc, argv, verbs, bus);
1ee306e1
LP
2457}
2458
2459int main(int argc, char*argv[]) {
24996861 2460 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
84f6181c 2461 int r;
1ee306e1
LP
2462
2463 setlocale(LC_ALL, "");
2464 log_parse_environment();
2465 log_open();
2466
2467 r = parse_argv(argc, argv);
84f6181c 2468 if (r <= 0)
1ee306e1 2469 goto finish;
1ee306e1 2470
d21ed1ea 2471 r = bus_open_transport(arg_transport, arg_host, false, &bus);
a1da8583 2472 if (r < 0) {
da927ba9 2473 log_error_errno(r, "Failed to create bus connection: %m");
a1da8583
TG
2474 goto finish;
2475 }
1ee306e1 2476
56159e0d 2477 r = machinectl_main(argc, argv, bus);
1ee306e1
LP
2478
2479finish:
1ee306e1 2480 pager_close();
acf97e21 2481 polkit_agent_close();
1ee306e1 2482
84f6181c
LP
2483 strv_free(arg_property);
2484
2485 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1ee306e1 2486}