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