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