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