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