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