]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machinectl.c
treewide: fix multiple typos
[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 73static bool arg_force = false;
6e18cc9f 74static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
3d7415f4 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;
c7b7d449 1358 char last_char = 0;
0ec5543c 1359 bool machine_died;
04d39279
LP
1360
1361 assert(bus);
04d39279 1362
bf441e3d 1363 if (arg_transport != BUS_TRANSPORT_LOCAL &&
de33fc62 1364 arg_transport != BUS_TRANSPORT_MACHINE) {
923d8fd3 1365 log_error("Login only supported on local machines.");
04d39279
LP
1366 return -ENOTSUP;
1367 }
1368
acf97e21
LP
1369 polkit_agent_open_if_enabled();
1370
023fb90b 1371 r = sd_event_default(&event);
f647962d
MS
1372 if (r < 0)
1373 return log_error_errno(r, "Failed to get event loop: %m");
023fb90b
LP
1374
1375 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
1376 if (r < 0)
1377 return log_error_errno(r, "Failed to attach bus to event loop: %m");
023fb90b 1378
0ec5543c
LP
1379 match = strappenda("type='signal',"
1380 "sender='org.freedesktop.machine1',"
1381 "path='/org/freedesktop/machine1',",
1382 "interface='org.freedesktop.machine1.Manager',"
1383 "member='MachineRemoved',"
1384 "arg0='",
1385 argv[1],
1386 "'");
1387
1388 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1389 if (r < 0)
1390 return log_error_errno(r, "Failed to add machine removal match: %m");
1391
d04c1fb8
LP
1392 r = sd_bus_message_new_method_call(bus,
1393 &m,
1394 "org.freedesktop.machine1",
1395 "/org/freedesktop/machine1",
1396 "org.freedesktop.machine1.Manager",
1397 "OpenMachineLogin");
1398 if (r < 0)
1399 return bus_log_create_error(r);
1400
acf97e21 1401 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
d04c1fb8
LP
1402 if (r < 0)
1403 return bus_log_create_error(r);
1404
1405 r = sd_bus_message_append(m, "s", argv[1]);
1406 if (r < 0)
1407 return bus_log_create_error(r);
1408
1409 r = sd_bus_call(bus, m, 0, &error, &reply);
40205d70
LP
1410 if (r < 0) {
1411 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
785890ac 1412 return r;
40205d70 1413 }
04d39279 1414
40205d70
LP
1415 r = sd_bus_message_read(reply, "hs", &master, &pty);
1416 if (r < 0)
ee451d76 1417 return bus_log_parse_error(r);
04d39279 1418
813c65c3 1419 sigprocmask_many(SIG_BLOCK, SIGWINCH, SIGTERM, SIGINT, -1);
04d39279 1420
0ec5543c 1421 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
04d39279 1422
023fb90b
LP
1423 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1424 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1425
9b15b784 1426 r = pty_forward_new(event, master, true, &forward);
f647962d
MS
1427 if (r < 0)
1428 return log_error_errno(r, "Failed to create PTY forwarder: %m");
023fb90b
LP
1429
1430 r = sd_event_loop(event);
f647962d
MS
1431 if (r < 0)
1432 return log_error_errno(r, "Failed to run event loop: %m");
04d39279 1433
0ec5543c 1434 pty_forward_get_last_char(forward, &last_char);
da054c37 1435 machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
c7b7d449 1436
023fb90b
LP
1437 forward = pty_forward_free(forward);
1438
c7b7d449
LP
1439 if (last_char != '\n')
1440 fputc('\n', stdout);
04d39279 1441
0ec5543c
LP
1442 if (machine_died)
1443 log_info("Machine %s terminated.", argv[1]);
1444 else
1445 log_info("Connection to machine %s terminated.", argv[1]);
04d39279 1446
023fb90b
LP
1447 sd_event_get_exit_code(event, &ret);
1448 return ret;
04d39279
LP
1449}
1450
08682124
LP
1451static int remove_image(int argc, char *argv[], void *userdata) {
1452 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1453 sd_bus *bus = userdata;
ebd93cb6 1454 int r, i;
08682124
LP
1455
1456 assert(bus);
1457
acf97e21
LP
1458 polkit_agent_open_if_enabled();
1459
08682124 1460 for (i = 1; i < argc; i++) {
08682124
LP
1461 r = sd_bus_call_method(
1462 bus,
1463 "org.freedesktop.machine1",
1464 "/org/freedesktop/machine1",
1465 "org.freedesktop.machine1.Manager",
1466 "RemoveImage",
1467 &error,
1468 NULL,
1469 "s", argv[i]);
1470 if (r < 0) {
1471 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1472 return r;
1473 }
1474 }
1475
1476 return 0;
1477}
1478
ebd93cb6
LP
1479static int rename_image(int argc, char *argv[], void *userdata) {
1480 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1481 sd_bus *bus = userdata;
1482 int r;
1483
acf97e21
LP
1484 polkit_agent_open_if_enabled();
1485
ebd93cb6
LP
1486 r = sd_bus_call_method(
1487 bus,
1488 "org.freedesktop.machine1",
1489 "/org/freedesktop/machine1",
1490 "org.freedesktop.machine1.Manager",
1491 "RenameImage",
1492 &error,
1493 NULL,
1494 "ss", argv[1], argv[2]);
1495 if (r < 0) {
1496 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1497 return r;
1498 }
1499
1500 return 0;
1501}
1502
1503static int clone_image(int argc, char *argv[], void *userdata) {
1504 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1505 sd_bus *bus = userdata;
1506 int r;
1507
acf97e21
LP
1508 polkit_agent_open_if_enabled();
1509
ebd93cb6
LP
1510 r = sd_bus_call_method(
1511 bus,
1512 "org.freedesktop.machine1",
1513 "/org/freedesktop/machine1",
1514 "org.freedesktop.machine1.Manager",
1515 "CloneImage",
1516 &error,
1517 NULL,
1518 "ssb", argv[1], argv[2], arg_read_only);
1519 if (r < 0) {
1520 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1521 return r;
1522 }
1523
1524 return 0;
1525}
1526
1527static int read_only_image(int argc, char *argv[], void *userdata) {
1528 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1529 sd_bus *bus = userdata;
1530 int b = true, r;
1531
1532 if (argc > 2) {
1533 b = parse_boolean(argv[2]);
1534 if (b < 0) {
1535 log_error("Failed to parse boolean argument: %s", argv[2]);
1536 return -EINVAL;
1537 }
1538 }
1539
acf97e21
LP
1540 polkit_agent_open_if_enabled();
1541
ebd93cb6
LP
1542 r = sd_bus_call_method(
1543 bus,
1544 "org.freedesktop.machine1",
1545 "/org/freedesktop/machine1",
1546 "org.freedesktop.machine1.Manager",
1547 "MarkImageReadOnly",
1548 &error,
1549 NULL,
1550 "sb", argv[1], b);
1551 if (r < 0) {
1552 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1553 return r;
1554 }
1555
1556 return 0;
1557}
1558
ebd011d9
LP
1559static int start_machine(int argc, char *argv[], void *userdata) {
1560 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1561 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1562 sd_bus *bus = userdata;
1563 int r, i;
1564
1565 assert(bus);
1566
acf97e21
LP
1567 polkit_agent_open_if_enabled();
1568
ebd011d9
LP
1569 r = bus_wait_for_jobs_new(bus, &w);
1570 if (r < 0)
1571 return log_oom();
1572
1573 for (i = 1; i < argc; i++) {
1574 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1575 _cleanup_free_ char *e = NULL, *unit = NULL;
1576 const char *object;
1577
1578 if (!machine_name_is_valid(argv[i])) {
1579 log_error("Invalid machine name %s.", argv[i]);
1580 return -EINVAL;
1581 }
1582
1583 e = unit_name_escape(argv[i]);
1584 if (!e)
1585 return log_oom();
1586
1587 unit = unit_name_build("systemd-nspawn", e, ".service");
1588 if (!unit)
1589 return log_oom();
1590
1591 r = sd_bus_message_new_method_call(
1592 bus,
1593 &m,
1594 "org.freedesktop.systemd1",
1595 "/org/freedesktop/systemd1",
1596 "org.freedesktop.systemd1.Manager",
1597 "StartUnit");
1598 if (r < 0)
1599 return bus_log_create_error(r);
1600
acf97e21 1601 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
ebd011d9
LP
1602 if (r < 0)
1603 return bus_log_create_error(r);
1604
1605 r = sd_bus_message_append(m, "ss", unit, "fail");
1606 if (r < 0)
1607 return bus_log_create_error(r);
1608
1609 r = sd_bus_call(bus, m, 0, &error, &reply);
1610 if (r < 0) {
1611 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1612 return r;
1613 }
1614
1615 r = sd_bus_message_read(reply, "o", &object);
1616 if (r < 0)
1617 return bus_log_parse_error(r);
1618
1619 r = bus_wait_for_jobs_add(w, object);
1620 if (r < 0)
1621 return log_oom();
1622 }
1623
d8f52ed2 1624 r = bus_wait_for_jobs(w, arg_quiet);
ebd011d9
LP
1625 if (r < 0)
1626 return r;
1627
1628 return 0;
1629}
1630
d8f52ed2
LP
1631static int enable_machine(int argc, char *argv[], void *userdata) {
1632 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1633 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1634 int carries_install_info = 0;
1635 const char *method = NULL;
1636 sd_bus *bus = userdata;
1637 int r, i;
1638
1639 assert(bus);
1640
acf97e21
LP
1641 polkit_agent_open_if_enabled();
1642
d8f52ed2
LP
1643 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1644
1645 r = sd_bus_message_new_method_call(
1646 bus,
1647 &m,
1648 "org.freedesktop.systemd1",
1649 "/org/freedesktop/systemd1",
1650 "org.freedesktop.systemd1.Manager",
1651 method);
1652 if (r < 0)
1653 return bus_log_create_error(r);
1654
acf97e21 1655 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
d8f52ed2
LP
1656 if (r < 0)
1657 return bus_log_create_error(r);
1658
1659 r = sd_bus_message_open_container(m, 'a', "s");
1660 if (r < 0)
1661 return bus_log_create_error(r);
1662
1663 for (i = 1; i < argc; i++) {
1664 _cleanup_free_ char *e = NULL, *unit = NULL;
1665
1666 if (!machine_name_is_valid(argv[i])) {
1667 log_error("Invalid machine name %s.", argv[i]);
1668 return -EINVAL;
1669 }
1670
1671 e = unit_name_escape(argv[i]);
1672 if (!e)
1673 return log_oom();
1674
1675 unit = unit_name_build("systemd-nspawn", e, ".service");
1676 if (!unit)
1677 return log_oom();
1678
1679 r = sd_bus_message_append(m, "s", unit);
1680 if (r < 0)
1681 return bus_log_create_error(r);
1682 }
1683
1684 r = sd_bus_message_close_container(m);
1685 if (r < 0)
1686 return bus_log_create_error(r);
1687
1688 if (streq(argv[0], "enable"))
1689 r = sd_bus_message_append(m, "bb", false, false);
1690 else
1691 r = sd_bus_message_append(m, "b", false);
1692 if (r < 0)
1693 return bus_log_create_error(r);
1694
1695 r = sd_bus_call(bus, m, 0, &error, &reply);
1696 if (r < 0) {
1697 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1698 return r;
1699 }
1700
1701 if (streq(argv[0], "enable")) {
1702 r = sd_bus_message_read(reply, "b", carries_install_info);
1703 if (r < 0)
1704 return bus_log_parse_error(r);
1705 }
1706
1707 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1708 if (r < 0)
1709 return r;
1710
1711 m = sd_bus_message_unref(m);
1712
1713 r = sd_bus_message_new_method_call(
1714 bus,
1715 &m,
1716 "org.freedesktop.systemd1",
1717 "/org/freedesktop/systemd1",
1718 "org.freedesktop.systemd1.Manager",
1719 "Reload");
1720 if (r < 0)
1721 return bus_log_create_error(r);
1722
acf97e21 1723 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
d8f52ed2
LP
1724 if (r < 0)
1725 return bus_log_create_error(r);
1726
1727 r = sd_bus_call(bus, m, 0, &error, NULL);
1728 if (r < 0) {
1729 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1730 return r;
1731 }
1732
1733 return 0;
1734}
1735
3d7415f4 1736static int match_log_message(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
6adf7b5e 1737 const char **our_path = userdata, *line;
3d7415f4
LP
1738 unsigned priority;
1739 int r;
1740
1741 assert(bus);
1742 assert(m);
6adf7b5e 1743 assert(our_path);
3d7415f4
LP
1744
1745 r = sd_bus_message_read(m, "us", &priority, &line);
1746 if (r < 0) {
1747 bus_log_parse_error(r);
1748 return 0;
1749 }
1750
6adf7b5e 1751 if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
3d7415f4
LP
1752 return 0;
1753
1754 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1755 return 0;
1756
1757 log_full(priority, "%s", line);
1758 return 0;
1759}
1760
1761static int match_transfer_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
6adf7b5e 1762 const char **our_path = userdata, *path, *result;
3d7415f4
LP
1763 uint32_t id;
1764 int r;
1765
1766 assert(bus);
1767 assert(m);
6adf7b5e 1768 assert(our_path);
3d7415f4
LP
1769
1770 r = sd_bus_message_read(m, "uos", &id, &path, &result);
1771 if (r < 0) {
1772 bus_log_parse_error(r);
1773 return 0;
1774 }
1775
6adf7b5e 1776 if (!streq_ptr(*our_path, path))
3d7415f4
LP
1777 return 0;
1778
6adf7b5e
LP
1779 sd_event_exit(sd_bus_get_event(bus), !streq_ptr(result, "done"));
1780 return 0;
1781}
1782
1783static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1784 assert(s);
1785 assert(si);
1786
1787 if (!arg_quiet)
cc98b302 1788 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
6adf7b5e
LP
1789
1790 sd_event_exit(sd_event_source_get_event(s), EINTR);
3d7415f4
LP
1791 return 0;
1792}
1793
1794static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
1795 _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1796 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1797 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
6adf7b5e
LP
1798 _cleanup_event_unref_ sd_event* event = NULL;
1799 const char *path = NULL;
3d7415f4
LP
1800 uint32_t id;
1801 int r;
1802
1803 assert(bus);
1804 assert(m);
1805
1806 polkit_agent_open_if_enabled();
1807
6adf7b5e
LP
1808 r = sd_event_default(&event);
1809 if (r < 0)
1810 return log_error_errno(r, "Failed to get event loop: %m");
1811
1812 r = sd_bus_attach_event(bus, event, 0);
1813 if (r < 0)
1814 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1815
3d7415f4
LP
1816 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1817 if (r < 0)
1818 return bus_log_create_error(r);
1819
1820 r = sd_bus_add_match(
1821 bus,
1822 &slot_job_removed,
1823 "type='signal',"
1824 "sender='org.freedesktop.import1',"
1825 "interface='org.freedesktop.import1.Manager',"
1826 "member='TransferRemoved',"
1827 "path='/org/freedesktop/import1'",
6adf7b5e 1828 match_transfer_removed, &path);
3d7415f4
LP
1829 if (r < 0)
1830 return log_error_errno(r, "Failed to install match: %m");
1831
1832 r = sd_bus_add_match(
1833 bus,
1834 &slot_log_message,
1835 "type='signal',"
1836 "sender='org.freedesktop.import1',"
1837 "interface='org.freedesktop.import1.Transfer',"
1838 "member='LogMessage'",
6adf7b5e 1839 match_log_message, &path);
3d7415f4
LP
1840 if (r < 0)
1841 return log_error_errno(r, "Failed to install match: %m");
1842
1843 r = sd_bus_call(bus, m, 0, &error, &reply);
1844 if (r < 0) {
1845 log_error("Failed pull image: %s", bus_error_message(&error, -r));
1846 return r;
1847 }
1848
6adf7b5e 1849 r = sd_bus_message_read(reply, "uo", &id, &path);
3d7415f4
LP
1850 if (r < 0)
1851 return bus_log_parse_error(r);
1852
6adf7b5e 1853 sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
3d7415f4 1854
6adf7b5e
LP
1855 if (!arg_quiet)
1856 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
3d7415f4 1857
6adf7b5e
LP
1858 sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
1859 sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
1860
1861 r = sd_event_loop(event);
1862 if (r < 0)
1863 return log_error_errno(r, "Failed to run event loop: %m");
3d7415f4 1864
6adf7b5e 1865 return -r;
3d7415f4
LP
1866}
1867
1868static int pull_tar(int argc, char *argv[], void *userdata) {
1869 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1870 _cleanup_free_ char *l = NULL, *ll = NULL;
1871 const char *local, *remote;
1872 sd_bus *bus = userdata;
1873 int r;
1874
1875 assert(bus);
1876
1877 remote = argv[1];
1878 if (!http_url_is_valid(remote)) {
1879 log_error("URL '%s' is not valid.", remote);
1880 return -EINVAL;
1881 }
1882
1883 if (argc >= 3)
1884 local = argv[2];
1885 else {
1886 r = import_url_last_component(remote, &l);
1887 if (r < 0)
1888 return log_error_errno(r, "Failed to get final component of URL: %m");
1889
1890 local = l;
1891 }
1892
1893 if (isempty(local) || streq(local, "-"))
1894 local = NULL;
1895
1896 if (local) {
1897 r = tar_strip_suffixes(local, &ll);
1898 if (r < 0)
1899 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1900
1901 local = ll;
1902
1903 if (!machine_name_is_valid(local)) {
1904 log_error("Local name %s is not a suitable machine name.", local);
1905 return -EINVAL;
1906 }
1907 }
1908
1909 r = sd_bus_message_new_method_call(
1910 bus,
1911 &m,
1912 "org.freedesktop.import1",
1913 "/org/freedesktop/import1",
1914 "org.freedesktop.import1.Manager",
1915 "PullTar");
1916 if (r < 0)
1917 return bus_log_create_error(r);
1918
1919 r = sd_bus_message_append(
1920 m,
1921 "sssb",
1922 remote,
1923 local,
6e18cc9f 1924 import_verify_to_string(arg_verify),
3d7415f4
LP
1925 arg_force);
1926 if (r < 0)
1927 return bus_log_create_error(r);
1928
1929 return pull_image_common(bus, m);
1930}
1931
1932static int pull_raw(int argc, char *argv[], void *userdata) {
1933 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1934 _cleanup_free_ char *l = NULL, *ll = NULL;
1935 const char *local, *remote;
1936 sd_bus *bus = userdata;
1937 int r;
1938
1939 assert(bus);
1940
1941 remote = argv[1];
1942 if (!http_url_is_valid(remote)) {
1943 log_error("URL '%s' is not valid.", remote);
1944 return -EINVAL;
1945 }
1946
1947 if (argc >= 3)
1948 local = argv[2];
1949 else {
1950 r = import_url_last_component(remote, &l);
1951 if (r < 0)
1952 return log_error_errno(r, "Failed to get final component of URL: %m");
1953
1954 local = l;
1955 }
1956
1957 if (isempty(local) || streq(local, "-"))
1958 local = NULL;
1959
1960 if (local) {
1961 r = raw_strip_suffixes(local, &ll);
1962 if (r < 0)
1963 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1964
1965 local = ll;
1966
1967 if (!machine_name_is_valid(local)) {
1968 log_error("Local name %s is not a suitable machine name.", local);
1969 return -EINVAL;
1970 }
1971 }
1972
1973 r = sd_bus_message_new_method_call(
1974 bus,
1975 &m,
1976 "org.freedesktop.import1",
1977 "/org/freedesktop/import1",
1978 "org.freedesktop.import1.Manager",
1979 "PullRaw");
1980 if (r < 0)
1981 return bus_log_create_error(r);
1982
1983 r = sd_bus_message_append(
1984 m,
1985 "sssb",
1986 remote,
1987 local,
6e18cc9f 1988 import_verify_to_string(arg_verify),
3d7415f4
LP
1989 arg_force);
1990 if (r < 0)
1991 return bus_log_create_error(r);
1992
1993 return pull_image_common(bus, m);
1994}
1995
1996static int pull_dkr(int argc, char *argv[], void *userdata) {
1997 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1998 _cleanup_free_ char *l = NULL, *ll = NULL;
1999 const char *local, *remote, *tag;
2000 sd_bus *bus = userdata;
2001 int r;
2002
56e6c2ab 2003 if (arg_verify != IMPORT_VERIFY_NO) {
3d7415f4
LP
2004 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2005 return -EINVAL;
2006 }
2007
2008 remote = argv[1];
2009 tag = strchr(remote, ':');
2010 if (tag) {
2011 remote = strndupa(remote, tag - remote);
2012 tag++;
2013 }
2014
2015 if (!dkr_name_is_valid(remote)) {
2016 log_error("DKR name '%s' is invalid.", remote);
2017 return -EINVAL;
2018 }
2019 if (tag && !dkr_tag_is_valid(tag)) {
2020 log_error("DKR tag '%s' is invalid.", remote);
2021 return -EINVAL;
2022 }
2023
2024 if (argc >= 3)
2025 local = argv[2];
2026 else {
2027 local = strchr(remote, '/');
2028 if (local)
2029 local++;
2030 else
2031 local = remote;
2032 }
2033
2034 if (isempty(local) || streq(local, "-"))
2035 local = NULL;
2036
2037 if (local) {
2038 if (!machine_name_is_valid(local)) {
2039 log_error("Local name %s is not a suitable machine name.", local);
2040 return -EINVAL;
2041 }
2042 }
2043
2044 r = sd_bus_message_new_method_call(
2045 bus,
2046 &m,
2047 "org.freedesktop.import1",
2048 "/org/freedesktop/import1",
2049 "org.freedesktop.import1.Manager",
2050 "PullDkr");
2051 if (r < 0)
2052 return bus_log_create_error(r);
2053
2054 r = sd_bus_message_append(
2055 m,
2056 "sssssb",
2057 arg_dkr_index_url,
2058 remote,
2059 tag,
2060 local,
6e18cc9f 2061 import_verify_to_string(arg_verify),
3d7415f4
LP
2062 arg_force);
2063 if (r < 0)
2064 return bus_log_create_error(r);
2065
2066 return pull_image_common(bus, m);
2067}
2068
2069typedef struct TransferInfo {
2070 uint32_t id;
2071 const char *type;
2072 const char *remote;
2073 const char *local;
7079cfef 2074 double progress;
3d7415f4
LP
2075} TransferInfo;
2076
2077static int compare_transfer_info(const void *a, const void *b) {
2078 const TransferInfo *x = a, *y = b;
2079
2080 return strcmp(x->local, y->local);
2081}
2082
2083static int list_transfers(int argc, char *argv[], void *userdata) {
2084 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
2085 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
2086 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2087 _cleanup_free_ TransferInfo *transfers = NULL;
2088 size_t n_transfers = 0, n_allocated = 0, j;
2089 const char *type, *remote, *local, *object;
2090 sd_bus *bus = userdata;
2091 uint32_t id, max_id = 0;
7079cfef 2092 double progress;
3d7415f4
LP
2093 int r;
2094
2095 pager_open_if_enabled();
2096
2097 r = sd_bus_call_method(
2098 bus,
2099 "org.freedesktop.import1",
2100 "/org/freedesktop/import1",
2101 "org.freedesktop.import1.Manager",
2102 "ListTransfers",
2103 &error,
2104 &reply,
2105 NULL);
2106 if (r < 0) {
2107 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2108 return r;
2109 }
2110
7079cfef 2111 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
3d7415f4
LP
2112 if (r < 0)
2113 return bus_log_parse_error(r);
2114
7079cfef 2115 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
3d7415f4
LP
2116 size_t l;
2117
2118 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2119 return log_oom();
2120
2121 transfers[n_transfers].id = id;
2122 transfers[n_transfers].type = type;
2123 transfers[n_transfers].remote = remote;
2124 transfers[n_transfers].local = local;
7079cfef 2125 transfers[n_transfers].progress = progress;
3d7415f4
LP
2126
2127 l = strlen(type);
2128 if (l > max_type)
2129 max_type = l;
2130
2131 l = strlen(remote);
2132 if (l > max_remote)
2133 max_remote = l;
2134
2135 l = strlen(local);
2136 if (l > max_local)
2137 max_local = l;
2138
2139 if (id > max_id)
2140 max_id = id;
2141
2142 n_transfers ++;
2143 }
2144 if (r < 0)
2145 return bus_log_parse_error(r);
2146
2147 r = sd_bus_message_exit_container(reply);
2148 if (r < 0)
2149 return bus_log_parse_error(r);
2150
2151 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2152
2153 if (arg_legend)
7079cfef 2154 printf("%-*s %-*s %-*s %-*s %-*s\n",
3d7415f4 2155 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
7079cfef 2156 (int) 7, "PERCENT",
3d7415f4
LP
2157 (int) max_type, "TYPE",
2158 (int) max_local, "LOCAL",
2159 (int) max_remote, "REMOTE");
2160
2161 for (j = 0; j < n_transfers; j++)
7079cfef 2162 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
3d7415f4 2163 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
7079cfef 2164 (int) 6, (unsigned) (transfers[j].progress * 100),
3d7415f4
LP
2165 (int) max_type, transfers[j].type,
2166 (int) max_local, transfers[j].local,
2167 (int) max_remote, transfers[j].remote);
2168
2169 if (arg_legend)
2170 printf("\n%zu transfers listed.\n", n_transfers);
2171
2172 return 0;
2173}
2174
2175static int cancel_transfer(int argc, char *argv[], void *userdata) {
2176 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2177 sd_bus *bus = userdata;
2178 int r, i;
2179
2180 assert(bus);
2181
2182 polkit_agent_open_if_enabled();
2183
2184 for (i = 1; i < argc; i++) {
2185 uint32_t id;
2186
2187 r = safe_atou32(argv[i], &id);
2188 if (r < 0)
2189 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2190
2191 r = sd_bus_call_method(
2192 bus,
2193 "org.freedesktop.import1",
2194 "/org/freedesktop/import1",
2195 "org.freedesktop.import1.Manager",
2196 "CancelTransfer",
2197 &error,
2198 NULL,
2199 "u", id);
2200 if (r < 0) {
2201 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2202 return r;
2203 }
2204 }
2205
2206 return 0;
2207}
2208
56159e0d
LP
2209static int help(int argc, char *argv[], void *userdata) {
2210
1ee306e1 2211 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
f2cbe59e
LP
2212 "Send control commands to or query the virtual machine and container\n"
2213 "registration manager.\n\n"
2214 " -h --help Show this help\n"
2215 " --version Show package version\n"
2216 " --no-pager Do not pipe output into a pager\n"
2217 " --no-legend Do not show the headers and footers\n"
acf97e21 2218 " --no-ask-password Do not ask for system passwords\n"
f2cbe59e
LP
2219 " -H --host=[USER@]HOST Operate on remote host\n"
2220 " -M --machine=CONTAINER Operate on local container\n"
2221 " -p --property=NAME Show only properties by this name\n"
d8f52ed2 2222 " -q --quiet Suppress output\n"
f2cbe59e
LP
2223 " -a --all Show all properties, including empty ones\n"
2224 " -l --full Do not ellipsize output\n"
2225 " --kill-who=WHO Who to send signal to\n"
2226 " -s --signal=SIGNAL Which signal to send\n"
2227 " --read-only Create read-only bind mount\n"
8b0cc9a3
LP
2228 " --mkdir Create directory before bind mounting, if missing\n"
2229 " -n --lines=INTEGER Number of journal entries to show\n"
2230 " -o --output=STRING Change journal output mode (short,\n"
2231 " short-monotonic, verbose, export, json,\n"
3d7415f4 2232 " json-pretty, json-sse, cat)\n"
7f444afa
LP
2233 " --verify=MODE Verification mode for downloaded images (no,\n"
2234 " checksum, signature)\n"
3d7415f4
LP
2235 " --force Download image even if already exists\n"
2236 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2237 " downloads\n\n"
cd61c3bf 2238 "Machine Commands:\n"
f2cbe59e 2239 " list List running VMs and containers\n"
fefdc04b 2240 " status NAME... Show VM/container details\n"
f2cbe59e 2241 " show NAME... Show properties of one or more VMs/containers\n"
ebd011d9 2242 " start NAME... Start container as a service\n"
e45fc5e7 2243 " login NAME Get a login prompt on a container\n"
d8f52ed2
LP
2244 " enable NAME... Enable automatic container start at boot\n"
2245 " disable NAME... Disable automatic container start at boot\n"
f2cbe59e
LP
2246 " poweroff NAME... Power off one or more containers\n"
2247 " reboot NAME... Reboot one or more containers\n"
f2cbe59e 2248 " terminate NAME... Terminate one or more VMs/containers\n"
ebd93cb6 2249 " kill NAME... Send signal to processes of a VM/container\n"
f2cbe59e 2250 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
ebd93cb6
LP
2251 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2252 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
fefdc04b 2253 "Image Commands:\n"
56b921c3 2254 " list-images Show available container and VM images\n"
fefdc04b 2255 " image-status NAME... Show image details\n"
08682124 2256 " show-image NAME... Show properties of image\n"
ebd93cb6
LP
2257 " clone NAME NAME Clone an image\n"
2258 " rename NAME NAME Rename an image\n"
2259 " read-only NAME [BOOL] Mark or unmark image read-only\n"
3d7415f4 2260 " remove NAME... Remove an image\n\n"
b5b38b41
LP
2261 "Image Transfer Commands:\n"
2262 " pull-tar URL [NAME] Download a TAR container image\n"
2263 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2264 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2265 " list-transfers Show list of downloads in progress\n"
3d7415f4 2266 " cancel-transfer Cancel a download\n"
f7621db0 2267 , program_invocation_short_name);
56159e0d
LP
2268
2269 return 0;
1ee306e1
LP
2270}
2271
2272static int parse_argv(int argc, char *argv[]) {
2273
2274 enum {
2275 ARG_VERSION = 0x100,
2276 ARG_NO_PAGER,
e56056e9 2277 ARG_NO_LEGEND,
1ee306e1 2278 ARG_KILL_WHO,
785890ac
LP
2279 ARG_READ_ONLY,
2280 ARG_MKDIR,
acf97e21 2281 ARG_NO_ASK_PASSWORD,
3d7415f4
LP
2282 ARG_VERIFY,
2283 ARG_FORCE,
2284 ARG_DKR_INDEX_URL,
1ee306e1
LP
2285 };
2286
2287 static const struct option options[] = {
2288 { "help", no_argument, NULL, 'h' },
2289 { "version", no_argument, NULL, ARG_VERSION },
2290 { "property", required_argument, NULL, 'p' },
2291 { "all", no_argument, NULL, 'a' },
2292 { "full", no_argument, NULL, 'l' },
2293 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
e56056e9 2294 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1ee306e1
LP
2295 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2296 { "signal", required_argument, NULL, 's' },
2297 { "host", required_argument, NULL, 'H' },
a7893c6b 2298 { "machine", required_argument, NULL, 'M' },
785890ac
LP
2299 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2300 { "mkdir", no_argument, NULL, ARG_MKDIR },
d8f52ed2 2301 { "quiet", no_argument, NULL, 'q' },
8b0cc9a3
LP
2302 { "lines", required_argument, NULL, 'n' },
2303 { "output", required_argument, NULL, 'o' },
acf97e21 2304 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
3d7415f4
LP
2305 { "verify", required_argument, NULL, ARG_VERIFY },
2306 { "force", no_argument, NULL, ARG_FORCE },
2307 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
eb9da376 2308 {}
1ee306e1
LP
2309 };
2310
a7893c6b 2311 int c, r;
1ee306e1
LP
2312
2313 assert(argc >= 0);
2314 assert(argv);
2315
8b0cc9a3 2316 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
1ee306e1
LP
2317
2318 switch (c) {
2319
2320 case 'h':
56159e0d 2321 return help(0, NULL, NULL);
1ee306e1
LP
2322
2323 case ARG_VERSION:
2324 puts(PACKAGE_STRING);
2325 puts(SYSTEMD_FEATURES);
2326 return 0;
2327
a7893c6b
LP
2328 case 'p':
2329 r = strv_extend(&arg_property, optarg);
2330 if (r < 0)
2331 return log_oom();
1ee306e1
LP
2332
2333 /* If the user asked for a particular
2334 * property, show it to him, even if it is
2335 * empty. */
2336 arg_all = true;
2337 break;
1ee306e1
LP
2338
2339 case 'a':
2340 arg_all = true;
2341 break;
2342
2343 case 'l':
2344 arg_full = true;
2345 break;
2346
8b0cc9a3
LP
2347 case 'n':
2348 if (safe_atou(optarg, &arg_lines) < 0) {
2349 log_error("Failed to parse lines '%s'", optarg);
2350 return -EINVAL;
2351 }
2352 break;
2353
2354 case 'o':
2355 arg_output = output_mode_from_string(optarg);
2356 if (arg_output < 0) {
2357 log_error("Unknown output '%s'.", optarg);
2358 return -EINVAL;
2359 }
2360 break;
2361
1ee306e1
LP
2362 case ARG_NO_PAGER:
2363 arg_no_pager = true;
2364 break;
2365
e56056e9
TA
2366 case ARG_NO_LEGEND:
2367 arg_legend = false;
2368 break;
2369
1ee306e1
LP
2370 case ARG_KILL_WHO:
2371 arg_kill_who = optarg;
2372 break;
2373
2374 case 's':
2375 arg_signal = signal_from_string_try_harder(optarg);
2376 if (arg_signal < 0) {
2377 log_error("Failed to parse signal string %s.", optarg);
2378 return -EINVAL;
2379 }
2380 break;
2381
acf97e21
LP
2382 case ARG_NO_ASK_PASSWORD:
2383 arg_ask_password = false;
2384 break;
2385
1ee306e1 2386 case 'H':
d21ed1ea 2387 arg_transport = BUS_TRANSPORT_REMOTE;
a7893c6b
LP
2388 arg_host = optarg;
2389 break;
2390
2391 case 'M':
de33fc62 2392 arg_transport = BUS_TRANSPORT_MACHINE;
a7893c6b 2393 arg_host = optarg;
1ee306e1
LP
2394 break;
2395
785890ac
LP
2396 case ARG_READ_ONLY:
2397 arg_read_only = true;
2398 break;
2399
2400 case ARG_MKDIR:
2401 arg_mkdir = true;
2402 break;
2403
d8f52ed2
LP
2404 case 'q':
2405 arg_quiet = true;
2406 break;
2407
3d7415f4 2408 case ARG_VERIFY:
6e18cc9f
LP
2409 arg_verify = import_verify_from_string(optarg);
2410 if (arg_verify < 0) {
2411 log_error("Failed to parse --verify= setting: %s", optarg);
2412 return -EINVAL;
2413 }
3d7415f4
LP
2414 break;
2415
2416 case ARG_FORCE:
2417 arg_force = true;
2418 break;
2419
2420 case ARG_DKR_INDEX_URL:
2421 if (!http_url_is_valid(optarg)) {
2422 log_error("Index URL is invalid: %s", optarg);
2423 return -EINVAL;
2424 }
2425
2426 arg_dkr_index_url = optarg;
2427 break;
2428
1ee306e1
LP
2429 case '?':
2430 return -EINVAL;
2431
2432 default:
eb9da376 2433 assert_not_reached("Unhandled option");
1ee306e1 2434 }
1ee306e1
LP
2435
2436 return 1;
2437}
2438
56159e0d
LP
2439static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2440
2441 static const Verb verbs[] = {
3d7415f4
LP
2442 { "help", VERB_ANY, VERB_ANY, 0, help },
2443 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2444 { "list-images", VERB_ANY, 1, 0, list_images },
2445 { "status", 2, VERB_ANY, 0, show_machine },
2446 { "image-status", 2, VERB_ANY, 0, show_image },
2447 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2448 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2449 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2450 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2451 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2452 { "kill", 2, VERB_ANY, 0, kill_machine },
2453 { "login", 2, 2, 0, login_machine },
2454 { "bind", 3, 4, 0, bind_mount },
2455 { "copy-to", 3, 4, 0, copy_files },
2456 { "copy-from", 3, 4, 0, copy_files },
2457 { "remove", 2, VERB_ANY, 0, remove_image },
2458 { "rename", 3, 3, 0, rename_image },
2459 { "clone", 3, 3, 0, clone_image },
2460 { "read-only", 2, 3, 0, read_only_image },
2461 { "start", 2, VERB_ANY, 0, start_machine },
2462 { "enable", 2, VERB_ANY, 0, enable_machine },
2463 { "disable", 2, VERB_ANY, 0, enable_machine },
2464 { "pull-tar", 2, 3, 0, pull_tar },
2465 { "pull-raw", 2, 3, 0, pull_raw },
2466 { "pull-dkr", 2, 3, 0, pull_dkr },
2467 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2468 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
56159e0d 2469 {}
1ee306e1
LP
2470 };
2471
56159e0d 2472 return dispatch_verb(argc, argv, verbs, bus);
1ee306e1
LP
2473}
2474
2475int main(int argc, char*argv[]) {
24996861 2476 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
84f6181c 2477 int r;
1ee306e1
LP
2478
2479 setlocale(LC_ALL, "");
2480 log_parse_environment();
2481 log_open();
2482
2483 r = parse_argv(argc, argv);
84f6181c 2484 if (r <= 0)
1ee306e1 2485 goto finish;
1ee306e1 2486
d21ed1ea 2487 r = bus_open_transport(arg_transport, arg_host, false, &bus);
a1da8583 2488 if (r < 0) {
da927ba9 2489 log_error_errno(r, "Failed to create bus connection: %m");
a1da8583
TG
2490 goto finish;
2491 }
1ee306e1 2492
56159e0d 2493 r = machinectl_main(argc, argv, bus);
1ee306e1
LP
2494
2495finish:
1ee306e1 2496 pager_close();
acf97e21 2497 polkit_agent_close();
1ee306e1 2498
84f6181c
LP
2499 strv_free(arg_property);
2500
2501 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1ee306e1 2502}