]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machinectl.c
Merge pull request #180 from ronnychevalier/rc/coverity_cid_1304686
[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 sd_bus *bus = userdata;
1077 bool copy_from;
1078 int r;
1079
1080 assert(bus);
1081
1082 polkit_agent_open_if_enabled();
1083
1084 copy_from = streq(argv[0], "copy-from");
1085
1086 r = sd_bus_call_method(
1087 bus,
1088 "org.freedesktop.machine1",
1089 "/org/freedesktop/machine1",
1090 "org.freedesktop.machine1.Manager",
1091 copy_from ? "CopyFromMachine" : "CopyToMachine",
1092 &error,
1093 NULL,
1094 "sss",
1095 argv[1],
1096 argv[2],
1097 argv[3]);
1098 if (r < 0) {
1099 log_error("Failed to copy: %s", bus_error_message(&error, -r));
1100 return r;
1101 }
1102
1103 return 0;
1104 }
1105
1106 static int bind_mount(int argc, char *argv[], void *userdata) {
1107 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1108 sd_bus *bus = userdata;
1109 int r;
1110
1111 assert(bus);
1112
1113 polkit_agent_open_if_enabled();
1114
1115 r = sd_bus_call_method(
1116 bus,
1117 "org.freedesktop.machine1",
1118 "/org/freedesktop/machine1",
1119 "org.freedesktop.machine1.Manager",
1120 "BindMountMachine",
1121 &error,
1122 NULL,
1123 "sssbb",
1124 argv[1],
1125 argv[2],
1126 argv[3],
1127 arg_read_only,
1128 arg_mkdir);
1129 if (r < 0) {
1130 log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
1131 return r;
1132 }
1133
1134 return 0;
1135 }
1136
1137 static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1138 PTYForward ** forward = (PTYForward**) userdata;
1139 int r;
1140
1141 assert(m);
1142 assert(forward);
1143
1144 if (*forward) {
1145 /* If the forwarder is already initialized, tell it to
1146 * exit on the next vhangup(), so that we still flush
1147 * out what might be queued and exit then. */
1148
1149 r = pty_forward_set_ignore_vhangup(*forward, false);
1150 if (r >= 0)
1151 return 0;
1152
1153 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
1154 }
1155
1156 /* On error, or when the forwarder is not initialized yet, quit immediately */
1157 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
1158 return 0;
1159 }
1160
1161 static int login_machine(int argc, char *argv[], void *userdata) {
1162 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1163 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1164 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1165 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1166 _cleanup_event_unref_ sd_event *event = NULL;
1167 int master = -1, r, ret = 0;
1168 sd_bus *bus = userdata;
1169 const char *pty, *match;
1170 char last_char = 0;
1171 bool machine_died;
1172
1173 assert(bus);
1174
1175 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1176 arg_transport != BUS_TRANSPORT_MACHINE) {
1177 log_error("Login only supported on local machines.");
1178 return -EOPNOTSUPP;
1179 }
1180
1181 polkit_agent_open_if_enabled();
1182
1183 r = sd_event_default(&event);
1184 if (r < 0)
1185 return log_error_errno(r, "Failed to get event loop: %m");
1186
1187 r = sd_bus_attach_event(bus, event, 0);
1188 if (r < 0)
1189 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1190
1191 match = strjoina("type='signal',"
1192 "sender='org.freedesktop.machine1',"
1193 "path='/org/freedesktop/machine1',",
1194 "interface='org.freedesktop.machine1.Manager',"
1195 "member='MachineRemoved',"
1196 "arg0='",
1197 argv[1],
1198 "'");
1199
1200 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1201 if (r < 0)
1202 return log_error_errno(r, "Failed to add machine removal match: %m");
1203
1204 r = sd_bus_call_method(
1205 bus,
1206 "org.freedesktop.machine1",
1207 "/org/freedesktop/machine1",
1208 "org.freedesktop.machine1.Manager",
1209 "OpenMachineLogin",
1210 &error,
1211 &reply,
1212 "s", argv[1]);
1213 if (r < 0) {
1214 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1215 return r;
1216 }
1217
1218 r = sd_bus_message_read(reply, "hs", &master, &pty);
1219 if (r < 0)
1220 return bus_log_parse_error(r);
1221
1222 sigprocmask_many(SIG_BLOCK, SIGWINCH, SIGTERM, SIGINT, -1);
1223
1224 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1225
1226 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1227 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1228
1229 r = pty_forward_new(event, master, true, false, &forward);
1230 if (r < 0)
1231 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1232
1233 r = sd_event_loop(event);
1234 if (r < 0)
1235 return log_error_errno(r, "Failed to run event loop: %m");
1236
1237 pty_forward_get_last_char(forward, &last_char);
1238 machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
1239
1240 forward = pty_forward_free(forward);
1241
1242 if (last_char != '\n')
1243 fputc('\n', stdout);
1244
1245 if (machine_died)
1246 log_info("Machine %s terminated.", argv[1]);
1247 else
1248 log_info("Connection to machine %s terminated.", argv[1]);
1249
1250 sd_event_get_exit_code(event, &ret);
1251 return ret;
1252 }
1253
1254 static int remove_image(int argc, char *argv[], void *userdata) {
1255 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1256 sd_bus *bus = userdata;
1257 int r, i;
1258
1259 assert(bus);
1260
1261 polkit_agent_open_if_enabled();
1262
1263 for (i = 1; i < argc; i++) {
1264 r = sd_bus_call_method(
1265 bus,
1266 "org.freedesktop.machine1",
1267 "/org/freedesktop/machine1",
1268 "org.freedesktop.machine1.Manager",
1269 "RemoveImage",
1270 &error,
1271 NULL,
1272 "s", argv[i]);
1273 if (r < 0) {
1274 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1275 return r;
1276 }
1277 }
1278
1279 return 0;
1280 }
1281
1282 static int rename_image(int argc, char *argv[], void *userdata) {
1283 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1284 sd_bus *bus = userdata;
1285 int r;
1286
1287 polkit_agent_open_if_enabled();
1288
1289 r = sd_bus_call_method(
1290 bus,
1291 "org.freedesktop.machine1",
1292 "/org/freedesktop/machine1",
1293 "org.freedesktop.machine1.Manager",
1294 "RenameImage",
1295 &error,
1296 NULL,
1297 "ss", argv[1], argv[2]);
1298 if (r < 0) {
1299 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1300 return r;
1301 }
1302
1303 return 0;
1304 }
1305
1306 static int clone_image(int argc, char *argv[], void *userdata) {
1307 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1308 sd_bus *bus = userdata;
1309 int r;
1310
1311 polkit_agent_open_if_enabled();
1312
1313 r = sd_bus_call_method(
1314 bus,
1315 "org.freedesktop.machine1",
1316 "/org/freedesktop/machine1",
1317 "org.freedesktop.machine1.Manager",
1318 "CloneImage",
1319 &error,
1320 NULL,
1321 "ssb", argv[1], argv[2], arg_read_only);
1322 if (r < 0) {
1323 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1324 return r;
1325 }
1326
1327 return 0;
1328 }
1329
1330 static int read_only_image(int argc, char *argv[], void *userdata) {
1331 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1332 sd_bus *bus = userdata;
1333 int b = true, r;
1334
1335 if (argc > 2) {
1336 b = parse_boolean(argv[2]);
1337 if (b < 0) {
1338 log_error("Failed to parse boolean argument: %s", argv[2]);
1339 return -EINVAL;
1340 }
1341 }
1342
1343 polkit_agent_open_if_enabled();
1344
1345 r = sd_bus_call_method(
1346 bus,
1347 "org.freedesktop.machine1",
1348 "/org/freedesktop/machine1",
1349 "org.freedesktop.machine1.Manager",
1350 "MarkImageReadOnly",
1351 &error,
1352 NULL,
1353 "sb", argv[1], b);
1354 if (r < 0) {
1355 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1356 return r;
1357 }
1358
1359 return 0;
1360 }
1361
1362 static int make_service_name(const char *name, char **ret) {
1363 _cleanup_free_ char *e = NULL;
1364 int r;
1365
1366 assert(name);
1367 assert(ret);
1368
1369 if (!machine_name_is_valid(name)) {
1370 log_error("Invalid machine name %s.", name);
1371 return -EINVAL;
1372 }
1373
1374 e = unit_name_escape(name);
1375 if (!e)
1376 return log_oom();
1377
1378 r = unit_name_build("systemd-nspawn", e, ".service", ret);
1379 if (r < 0)
1380 return log_error_errno(r, "Failed to build unit name: %m");
1381
1382 return 0;
1383 }
1384
1385 static int start_machine(int argc, char *argv[], void *userdata) {
1386 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1387 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1388 sd_bus *bus = userdata;
1389 int r, i;
1390
1391 assert(bus);
1392
1393 polkit_agent_open_if_enabled();
1394
1395 r = bus_wait_for_jobs_new(bus, &w);
1396 if (r < 0)
1397 return log_oom();
1398
1399 for (i = 1; i < argc; i++) {
1400 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1401 _cleanup_free_ char *unit = NULL;
1402 const char *object;
1403
1404 r = make_service_name(argv[i], &unit);
1405 if (r < 0)
1406 return r;
1407
1408 r = sd_bus_call_method(
1409 bus,
1410 "org.freedesktop.systemd1",
1411 "/org/freedesktop/systemd1",
1412 "org.freedesktop.systemd1.Manager",
1413 "StartUnit",
1414 &error,
1415 &reply,
1416 "ss", unit, "fail");
1417 if (r < 0) {
1418 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1419 return r;
1420 }
1421
1422 r = sd_bus_message_read(reply, "o", &object);
1423 if (r < 0)
1424 return bus_log_parse_error(r);
1425
1426 r = bus_wait_for_jobs_add(w, object);
1427 if (r < 0)
1428 return log_oom();
1429 }
1430
1431 r = bus_wait_for_jobs(w, arg_quiet);
1432 if (r < 0)
1433 return r;
1434
1435 return 0;
1436 }
1437
1438 static int enable_machine(int argc, char *argv[], void *userdata) {
1439 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1440 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1441 int carries_install_info = 0;
1442 const char *method = NULL;
1443 sd_bus *bus = userdata;
1444 int r, i;
1445
1446 assert(bus);
1447
1448 polkit_agent_open_if_enabled();
1449
1450 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1451
1452 r = sd_bus_message_new_method_call(
1453 bus,
1454 &m,
1455 "org.freedesktop.systemd1",
1456 "/org/freedesktop/systemd1",
1457 "org.freedesktop.systemd1.Manager",
1458 method);
1459 if (r < 0)
1460 return bus_log_create_error(r);
1461
1462 r = sd_bus_message_open_container(m, 'a', "s");
1463 if (r < 0)
1464 return bus_log_create_error(r);
1465
1466 for (i = 1; i < argc; i++) {
1467 _cleanup_free_ char *unit = NULL;
1468
1469 r = make_service_name(argv[i], &unit);
1470 if (r < 0)
1471 return r;
1472
1473 r = sd_bus_message_append(m, "s", unit);
1474 if (r < 0)
1475 return bus_log_create_error(r);
1476 }
1477
1478 r = sd_bus_message_close_container(m);
1479 if (r < 0)
1480 return bus_log_create_error(r);
1481
1482 if (streq(argv[0], "enable"))
1483 r = sd_bus_message_append(m, "bb", false, false);
1484 else
1485 r = sd_bus_message_append(m, "b", false);
1486 if (r < 0)
1487 return bus_log_create_error(r);
1488
1489 r = sd_bus_call(bus, m, 0, &error, &reply);
1490 if (r < 0) {
1491 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1492 return r;
1493 }
1494
1495 if (streq(argv[0], "enable")) {
1496 r = sd_bus_message_read(reply, "b", carries_install_info);
1497 if (r < 0)
1498 return bus_log_parse_error(r);
1499 }
1500
1501 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
1502 if (r < 0)
1503 return r;
1504
1505 r = sd_bus_call_method(
1506 bus,
1507 "org.freedesktop.systemd1",
1508 "/org/freedesktop/systemd1",
1509 "org.freedesktop.systemd1.Manager",
1510 "Reload",
1511 &error,
1512 NULL,
1513 NULL);
1514 if (r < 0) {
1515 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1516 return r;
1517 }
1518
1519 return 0;
1520 }
1521
1522 static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1523 const char **our_path = userdata, *line;
1524 unsigned priority;
1525 int r;
1526
1527 assert(m);
1528 assert(our_path);
1529
1530 r = sd_bus_message_read(m, "us", &priority, &line);
1531 if (r < 0) {
1532 bus_log_parse_error(r);
1533 return 0;
1534 }
1535
1536 if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
1537 return 0;
1538
1539 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1540 return 0;
1541
1542 log_full(priority, "%s", line);
1543 return 0;
1544 }
1545
1546 static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1547 const char **our_path = userdata, *path, *result;
1548 uint32_t id;
1549 int r;
1550
1551 assert(m);
1552 assert(our_path);
1553
1554 r = sd_bus_message_read(m, "uos", &id, &path, &result);
1555 if (r < 0) {
1556 bus_log_parse_error(r);
1557 return 0;
1558 }
1559
1560 if (!streq_ptr(*our_path, path))
1561 return 0;
1562
1563 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done"));
1564 return 0;
1565 }
1566
1567 static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1568 assert(s);
1569 assert(si);
1570
1571 if (!arg_quiet)
1572 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
1573
1574 sd_event_exit(sd_event_source_get_event(s), EINTR);
1575 return 0;
1576 }
1577
1578 static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
1579 _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1580 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1581 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1582 _cleanup_event_unref_ sd_event* event = NULL;
1583 const char *path = NULL;
1584 uint32_t id;
1585 int r;
1586
1587 assert(bus);
1588 assert(m);
1589
1590 polkit_agent_open_if_enabled();
1591
1592 r = sd_event_default(&event);
1593 if (r < 0)
1594 return log_error_errno(r, "Failed to get event loop: %m");
1595
1596 r = sd_bus_attach_event(bus, event, 0);
1597 if (r < 0)
1598 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1599
1600 r = sd_bus_add_match(
1601 bus,
1602 &slot_job_removed,
1603 "type='signal',"
1604 "sender='org.freedesktop.import1',"
1605 "interface='org.freedesktop.import1.Manager',"
1606 "member='TransferRemoved',"
1607 "path='/org/freedesktop/import1'",
1608 match_transfer_removed, &path);
1609 if (r < 0)
1610 return log_error_errno(r, "Failed to install match: %m");
1611
1612 r = sd_bus_add_match(
1613 bus,
1614 &slot_log_message,
1615 "type='signal',"
1616 "sender='org.freedesktop.import1',"
1617 "interface='org.freedesktop.import1.Transfer',"
1618 "member='LogMessage'",
1619 match_log_message, &path);
1620 if (r < 0)
1621 return log_error_errno(r, "Failed to install match: %m");
1622
1623 r = sd_bus_call(bus, m, 0, &error, &reply);
1624 if (r < 0) {
1625 log_error("Failed transfer image: %s", bus_error_message(&error, -r));
1626 return r;
1627 }
1628
1629 r = sd_bus_message_read(reply, "uo", &id, &path);
1630 if (r < 0)
1631 return bus_log_parse_error(r);
1632
1633 sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
1634
1635 if (!arg_quiet)
1636 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
1637
1638 sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
1639 sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
1640
1641 r = sd_event_loop(event);
1642 if (r < 0)
1643 return log_error_errno(r, "Failed to run event loop: %m");
1644
1645 return -r;
1646 }
1647
1648 static int import_tar(int argc, char *argv[], void *userdata) {
1649 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1650 _cleanup_free_ char *ll = NULL;
1651 _cleanup_close_ int fd = -1;
1652 const char *local = NULL, *path = NULL;
1653 sd_bus *bus = userdata;
1654 int r;
1655
1656 assert(bus);
1657
1658 if (argc >= 2)
1659 path = argv[1];
1660 if (isempty(path) || streq(path, "-"))
1661 path = NULL;
1662
1663 if (argc >= 3)
1664 local = argv[2];
1665 else if (path)
1666 local = basename(path);
1667 if (isempty(local) || streq(local, "-"))
1668 local = NULL;
1669
1670 if (!local) {
1671 log_error("Need either path or local name.");
1672 return -EINVAL;
1673 }
1674
1675 r = tar_strip_suffixes(local, &ll);
1676 if (r < 0)
1677 return log_oom();
1678
1679 local = ll;
1680
1681 if (!machine_name_is_valid(local)) {
1682 log_error("Local name %s is not a suitable machine name.", local);
1683 return -EINVAL;
1684 }
1685
1686 if (path) {
1687 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1688 if (fd < 0)
1689 return log_error_errno(errno, "Failed to open %s: %m", path);
1690 }
1691
1692 r = sd_bus_message_new_method_call(
1693 bus,
1694 &m,
1695 "org.freedesktop.import1",
1696 "/org/freedesktop/import1",
1697 "org.freedesktop.import1.Manager",
1698 "ImportTar");
1699 if (r < 0)
1700 return bus_log_create_error(r);
1701
1702 r = sd_bus_message_append(
1703 m,
1704 "hsbb",
1705 fd >= 0 ? fd : STDIN_FILENO,
1706 local,
1707 arg_force,
1708 arg_read_only);
1709 if (r < 0)
1710 return bus_log_create_error(r);
1711
1712 return transfer_image_common(bus, m);
1713 }
1714
1715 static int import_raw(int argc, char *argv[], void *userdata) {
1716 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1717 _cleanup_free_ char *ll = NULL;
1718 _cleanup_close_ int fd = -1;
1719 const char *local = NULL, *path = NULL;
1720 sd_bus *bus = userdata;
1721 int r;
1722
1723 assert(bus);
1724
1725 if (argc >= 2)
1726 path = argv[1];
1727 if (isempty(path) || streq(path, "-"))
1728 path = NULL;
1729
1730 if (argc >= 3)
1731 local = argv[2];
1732 else if (path)
1733 local = basename(path);
1734 if (isempty(local) || streq(local, "-"))
1735 local = NULL;
1736
1737 if (!local) {
1738 log_error("Need either path or local name.");
1739 return -EINVAL;
1740 }
1741
1742 r = raw_strip_suffixes(local, &ll);
1743 if (r < 0)
1744 return log_oom();
1745
1746 local = ll;
1747
1748 if (!machine_name_is_valid(local)) {
1749 log_error("Local name %s is not a suitable machine name.", local);
1750 return -EINVAL;
1751 }
1752
1753 if (path) {
1754 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1755 if (fd < 0)
1756 return log_error_errno(errno, "Failed to open %s: %m", path);
1757 }
1758
1759 r = sd_bus_message_new_method_call(
1760 bus,
1761 &m,
1762 "org.freedesktop.import1",
1763 "/org/freedesktop/import1",
1764 "org.freedesktop.import1.Manager",
1765 "ImportRaw");
1766 if (r < 0)
1767 return bus_log_create_error(r);
1768
1769 r = sd_bus_message_append(
1770 m,
1771 "hsbb",
1772 fd >= 0 ? fd : STDIN_FILENO,
1773 local,
1774 arg_force,
1775 arg_read_only);
1776 if (r < 0)
1777 return bus_log_create_error(r);
1778
1779 return transfer_image_common(bus, m);
1780 }
1781
1782 static void determine_compression_from_filename(const char *p) {
1783 if (arg_format)
1784 return;
1785
1786 if (!p)
1787 return;
1788
1789 if (endswith(p, ".xz"))
1790 arg_format = "xz";
1791 else if (endswith(p, ".gz"))
1792 arg_format = "gzip";
1793 else if (endswith(p, ".bz2"))
1794 arg_format = "bzip2";
1795 }
1796
1797 static int export_tar(int argc, char *argv[], void *userdata) {
1798 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1799 _cleanup_close_ int fd = -1;
1800 const char *local = NULL, *path = NULL;
1801 sd_bus *bus = userdata;
1802 int r;
1803
1804 assert(bus);
1805
1806 local = argv[1];
1807 if (!machine_name_is_valid(local)) {
1808 log_error("Machine name %s is not valid.", local);
1809 return -EINVAL;
1810 }
1811
1812 if (argc >= 3)
1813 path = argv[2];
1814 if (isempty(path) || streq(path, "-"))
1815 path = NULL;
1816
1817 if (path) {
1818 determine_compression_from_filename(path);
1819
1820 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
1821 if (fd < 0)
1822 return log_error_errno(errno, "Failed to open %s: %m", path);
1823 }
1824
1825 r = sd_bus_message_new_method_call(
1826 bus,
1827 &m,
1828 "org.freedesktop.import1",
1829 "/org/freedesktop/import1",
1830 "org.freedesktop.import1.Manager",
1831 "ExportTar");
1832 if (r < 0)
1833 return bus_log_create_error(r);
1834
1835 r = sd_bus_message_append(
1836 m,
1837 "shs",
1838 local,
1839 fd >= 0 ? fd : STDOUT_FILENO,
1840 arg_format);
1841 if (r < 0)
1842 return bus_log_create_error(r);
1843
1844 return transfer_image_common(bus, m);
1845 }
1846
1847 static int export_raw(int argc, char *argv[], void *userdata) {
1848 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1849 _cleanup_close_ int fd = -1;
1850 const char *local = NULL, *path = NULL;
1851 sd_bus *bus = userdata;
1852 int r;
1853
1854 assert(bus);
1855
1856 local = argv[1];
1857 if (!machine_name_is_valid(local)) {
1858 log_error("Machine name %s is not valid.", local);
1859 return -EINVAL;
1860 }
1861
1862 if (argc >= 3)
1863 path = argv[2];
1864 if (isempty(path) || streq(path, "-"))
1865 path = NULL;
1866
1867 if (path) {
1868 determine_compression_from_filename(path);
1869
1870 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
1871 if (fd < 0)
1872 return log_error_errno(errno, "Failed to open %s: %m", path);
1873 }
1874
1875 r = sd_bus_message_new_method_call(
1876 bus,
1877 &m,
1878 "org.freedesktop.import1",
1879 "/org/freedesktop/import1",
1880 "org.freedesktop.import1.Manager",
1881 "ExportRaw");
1882 if (r < 0)
1883 return bus_log_create_error(r);
1884
1885 r = sd_bus_message_append(
1886 m,
1887 "shs",
1888 local,
1889 fd >= 0 ? fd : STDOUT_FILENO,
1890 arg_format);
1891 if (r < 0)
1892 return bus_log_create_error(r);
1893
1894 return transfer_image_common(bus, m);
1895 }
1896
1897 static int pull_tar(int argc, char *argv[], void *userdata) {
1898 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1899 _cleanup_free_ char *l = NULL, *ll = NULL;
1900 const char *local, *remote;
1901 sd_bus *bus = userdata;
1902 int r;
1903
1904 assert(bus);
1905
1906 remote = argv[1];
1907 if (!http_url_is_valid(remote)) {
1908 log_error("URL '%s' is not valid.", remote);
1909 return -EINVAL;
1910 }
1911
1912 if (argc >= 3)
1913 local = argv[2];
1914 else {
1915 r = import_url_last_component(remote, &l);
1916 if (r < 0)
1917 return log_error_errno(r, "Failed to get final component of URL: %m");
1918
1919 local = l;
1920 }
1921
1922 if (isempty(local) || streq(local, "-"))
1923 local = NULL;
1924
1925 if (local) {
1926 r = tar_strip_suffixes(local, &ll);
1927 if (r < 0)
1928 return log_oom();
1929
1930 local = ll;
1931
1932 if (!machine_name_is_valid(local)) {
1933 log_error("Local name %s is not a suitable machine name.", local);
1934 return -EINVAL;
1935 }
1936 }
1937
1938 r = sd_bus_message_new_method_call(
1939 bus,
1940 &m,
1941 "org.freedesktop.import1",
1942 "/org/freedesktop/import1",
1943 "org.freedesktop.import1.Manager",
1944 "PullTar");
1945 if (r < 0)
1946 return bus_log_create_error(r);
1947
1948 r = sd_bus_message_append(
1949 m,
1950 "sssb",
1951 remote,
1952 local,
1953 import_verify_to_string(arg_verify),
1954 arg_force);
1955 if (r < 0)
1956 return bus_log_create_error(r);
1957
1958 return transfer_image_common(bus, m);
1959 }
1960
1961 static int pull_raw(int argc, char *argv[], void *userdata) {
1962 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1963 _cleanup_free_ char *l = NULL, *ll = NULL;
1964 const char *local, *remote;
1965 sd_bus *bus = userdata;
1966 int r;
1967
1968 assert(bus);
1969
1970 remote = argv[1];
1971 if (!http_url_is_valid(remote)) {
1972 log_error("URL '%s' is not valid.", remote);
1973 return -EINVAL;
1974 }
1975
1976 if (argc >= 3)
1977 local = argv[2];
1978 else {
1979 r = import_url_last_component(remote, &l);
1980 if (r < 0)
1981 return log_error_errno(r, "Failed to get final component of URL: %m");
1982
1983 local = l;
1984 }
1985
1986 if (isempty(local) || streq(local, "-"))
1987 local = NULL;
1988
1989 if (local) {
1990 r = raw_strip_suffixes(local, &ll);
1991 if (r < 0)
1992 return log_oom();
1993
1994 local = ll;
1995
1996 if (!machine_name_is_valid(local)) {
1997 log_error("Local name %s is not a suitable machine name.", local);
1998 return -EINVAL;
1999 }
2000 }
2001
2002 r = sd_bus_message_new_method_call(
2003 bus,
2004 &m,
2005 "org.freedesktop.import1",
2006 "/org/freedesktop/import1",
2007 "org.freedesktop.import1.Manager",
2008 "PullRaw");
2009 if (r < 0)
2010 return bus_log_create_error(r);
2011
2012 r = sd_bus_message_append(
2013 m,
2014 "sssb",
2015 remote,
2016 local,
2017 import_verify_to_string(arg_verify),
2018 arg_force);
2019 if (r < 0)
2020 return bus_log_create_error(r);
2021
2022 return transfer_image_common(bus, m);
2023 }
2024
2025 static int pull_dkr(int argc, char *argv[], void *userdata) {
2026 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2027 const char *local, *remote, *tag;
2028 sd_bus *bus = userdata;
2029 int r;
2030
2031 if (arg_verify != IMPORT_VERIFY_NO) {
2032 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2033 return -EINVAL;
2034 }
2035
2036 remote = argv[1];
2037 tag = strchr(remote, ':');
2038 if (tag) {
2039 remote = strndupa(remote, tag - remote);
2040 tag++;
2041 }
2042
2043 if (!dkr_name_is_valid(remote)) {
2044 log_error("DKR name '%s' is invalid.", remote);
2045 return -EINVAL;
2046 }
2047 if (tag && !dkr_tag_is_valid(tag)) {
2048 log_error("DKR tag '%s' is invalid.", remote);
2049 return -EINVAL;
2050 }
2051
2052 if (argc >= 3)
2053 local = argv[2];
2054 else {
2055 local = strchr(remote, '/');
2056 if (local)
2057 local++;
2058 else
2059 local = remote;
2060 }
2061
2062 if (isempty(local) || streq(local, "-"))
2063 local = NULL;
2064
2065 if (local) {
2066 if (!machine_name_is_valid(local)) {
2067 log_error("Local name %s is not a suitable machine name.", local);
2068 return -EINVAL;
2069 }
2070 }
2071
2072 r = sd_bus_message_new_method_call(
2073 bus,
2074 &m,
2075 "org.freedesktop.import1",
2076 "/org/freedesktop/import1",
2077 "org.freedesktop.import1.Manager",
2078 "PullDkr");
2079 if (r < 0)
2080 return bus_log_create_error(r);
2081
2082 r = sd_bus_message_append(
2083 m,
2084 "sssssb",
2085 arg_dkr_index_url,
2086 remote,
2087 tag,
2088 local,
2089 import_verify_to_string(arg_verify),
2090 arg_force);
2091 if (r < 0)
2092 return bus_log_create_error(r);
2093
2094 return transfer_image_common(bus, m);
2095 }
2096
2097 typedef struct TransferInfo {
2098 uint32_t id;
2099 const char *type;
2100 const char *remote;
2101 const char *local;
2102 double progress;
2103 } TransferInfo;
2104
2105 static int compare_transfer_info(const void *a, const void *b) {
2106 const TransferInfo *x = a, *y = b;
2107
2108 return strcmp(x->local, y->local);
2109 }
2110
2111 static int list_transfers(int argc, char *argv[], void *userdata) {
2112 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
2113 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
2114 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2115 _cleanup_free_ TransferInfo *transfers = NULL;
2116 size_t n_transfers = 0, n_allocated = 0, j;
2117 const char *type, *remote, *local, *object;
2118 sd_bus *bus = userdata;
2119 uint32_t id, max_id = 0;
2120 double progress;
2121 int r;
2122
2123 pager_open_if_enabled();
2124
2125 r = sd_bus_call_method(
2126 bus,
2127 "org.freedesktop.import1",
2128 "/org/freedesktop/import1",
2129 "org.freedesktop.import1.Manager",
2130 "ListTransfers",
2131 &error,
2132 &reply,
2133 NULL);
2134 if (r < 0) {
2135 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2136 return r;
2137 }
2138
2139 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
2140 if (r < 0)
2141 return bus_log_parse_error(r);
2142
2143 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
2144 size_t l;
2145
2146 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2147 return log_oom();
2148
2149 transfers[n_transfers].id = id;
2150 transfers[n_transfers].type = type;
2151 transfers[n_transfers].remote = remote;
2152 transfers[n_transfers].local = local;
2153 transfers[n_transfers].progress = progress;
2154
2155 l = strlen(type);
2156 if (l > max_type)
2157 max_type = l;
2158
2159 l = strlen(remote);
2160 if (l > max_remote)
2161 max_remote = l;
2162
2163 l = strlen(local);
2164 if (l > max_local)
2165 max_local = l;
2166
2167 if (id > max_id)
2168 max_id = id;
2169
2170 n_transfers ++;
2171 }
2172 if (r < 0)
2173 return bus_log_parse_error(r);
2174
2175 r = sd_bus_message_exit_container(reply);
2176 if (r < 0)
2177 return bus_log_parse_error(r);
2178
2179 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2180
2181 if (arg_legend)
2182 printf("%-*s %-*s %-*s %-*s %-*s\n",
2183 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
2184 (int) 7, "PERCENT",
2185 (int) max_type, "TYPE",
2186 (int) max_local, "LOCAL",
2187 (int) max_remote, "REMOTE");
2188
2189 for (j = 0; j < n_transfers; j++)
2190 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
2191 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
2192 (int) 6, (unsigned) (transfers[j].progress * 100),
2193 (int) max_type, transfers[j].type,
2194 (int) max_local, transfers[j].local,
2195 (int) max_remote, transfers[j].remote);
2196
2197 if (arg_legend)
2198 printf("\n%zu transfers listed.\n", n_transfers);
2199
2200 return 0;
2201 }
2202
2203 static int cancel_transfer(int argc, char *argv[], void *userdata) {
2204 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2205 sd_bus *bus = userdata;
2206 int r, i;
2207
2208 assert(bus);
2209
2210 polkit_agent_open_if_enabled();
2211
2212 for (i = 1; i < argc; i++) {
2213 uint32_t id;
2214
2215 r = safe_atou32(argv[i], &id);
2216 if (r < 0)
2217 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2218
2219 r = sd_bus_call_method(
2220 bus,
2221 "org.freedesktop.import1",
2222 "/org/freedesktop/import1",
2223 "org.freedesktop.import1.Manager",
2224 "CancelTransfer",
2225 &error,
2226 NULL,
2227 "u", id);
2228 if (r < 0) {
2229 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2230 return r;
2231 }
2232 }
2233
2234 return 0;
2235 }
2236
2237 static int set_limit(int argc, char *argv[], void *userdata) {
2238 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2239 sd_bus *bus = userdata;
2240 uint64_t limit;
2241 int r;
2242
2243 if (streq(argv[argc-1], "-"))
2244 limit = (uint64_t) -1;
2245 else {
2246 off_t off;
2247
2248 r = parse_size(argv[argc-1], 1024, &off);
2249 if (r < 0)
2250 return log_error("Failed to parse size: %s", argv[argc-1]);
2251
2252 limit = (uint64_t) off;
2253 }
2254
2255 if (argc > 2)
2256 /* With two arguments changes the quota limit of the
2257 * specified image */
2258 r = sd_bus_call_method(
2259 bus,
2260 "org.freedesktop.machine1",
2261 "/org/freedesktop/machine1",
2262 "org.freedesktop.machine1.Manager",
2263 "SetImageLimit",
2264 &error,
2265 NULL,
2266 "st", argv[1], limit);
2267 else
2268 /* With one argument changes the pool quota limit */
2269 r = sd_bus_call_method(
2270 bus,
2271 "org.freedesktop.machine1",
2272 "/org/freedesktop/machine1",
2273 "org.freedesktop.machine1.Manager",
2274 "SetPoolLimit",
2275 &error,
2276 NULL,
2277 "t", limit);
2278
2279 if (r < 0) {
2280 log_error("Could not set limit: %s", bus_error_message(&error, -r));
2281 return r;
2282 }
2283
2284 return 0;
2285 }
2286
2287 static int help(int argc, char *argv[], void *userdata) {
2288
2289 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2290 "Send control commands to or query the virtual machine and container\n"
2291 "registration manager.\n\n"
2292 " -h --help Show this help\n"
2293 " --version Show package version\n"
2294 " --no-pager Do not pipe output into a pager\n"
2295 " --no-legend Do not show the headers and footers\n"
2296 " --no-ask-password Do not ask for system passwords\n"
2297 " -H --host=[USER@]HOST Operate on remote host\n"
2298 " -M --machine=CONTAINER Operate on local container\n"
2299 " -p --property=NAME Show only properties by this name\n"
2300 " -q --quiet Suppress output\n"
2301 " -a --all Show all properties, including empty ones\n"
2302 " -l --full Do not ellipsize output\n"
2303 " --kill-who=WHO Who to send signal to\n"
2304 " -s --signal=SIGNAL Which signal to send\n"
2305 " --read-only Create read-only bind mount\n"
2306 " --mkdir Create directory before bind mounting, if missing\n"
2307 " -n --lines=INTEGER Number of journal entries to show\n"
2308 " -o --output=STRING Change journal output mode (short,\n"
2309 " short-monotonic, verbose, export, json,\n"
2310 " json-pretty, json-sse, cat)\n"
2311 " --verify=MODE Verification mode for downloaded images (no,\n"
2312 " checksum, signature)\n"
2313 " --force Download image even if already exists\n"
2314 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2315 " downloads\n\n"
2316 "Machine Commands:\n"
2317 " list List running VMs and containers\n"
2318 " status NAME... Show VM/container details\n"
2319 " show NAME... Show properties of one or more VMs/containers\n"
2320 " start NAME... Start container as a service\n"
2321 " login NAME Get a login prompt on a container\n"
2322 " enable NAME... Enable automatic container start at boot\n"
2323 " disable NAME... Disable automatic container start at boot\n"
2324 " poweroff NAME... Power off one or more containers\n"
2325 " reboot NAME... Reboot one or more containers\n"
2326 " terminate NAME... Terminate one or more VMs/containers\n"
2327 " kill NAME... Send signal to processes of a VM/container\n"
2328 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2329 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2330 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2331 "Image Commands:\n"
2332 " list-images Show available container and VM images\n"
2333 " image-status NAME... Show image details\n"
2334 " show-image NAME... Show properties of image\n"
2335 " clone NAME NAME Clone an image\n"
2336 " rename NAME NAME Rename an image\n"
2337 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2338 " remove NAME... Remove an image\n"
2339 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2340 "Image Transfer Commands:\n"
2341 " pull-tar URL [NAME] Download a TAR container image\n"
2342 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2343 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2344 " import-tar FILE [NAME] Import a local TAR container image\n"
2345 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2346 " export-tar NAME [FILE] Export a TAR container image locally\n"
2347 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2348 " list-transfers Show list of downloads in progress\n"
2349 " cancel-transfer Cancel a download\n"
2350 , program_invocation_short_name);
2351
2352 return 0;
2353 }
2354
2355 static int parse_argv(int argc, char *argv[]) {
2356
2357 enum {
2358 ARG_VERSION = 0x100,
2359 ARG_NO_PAGER,
2360 ARG_NO_LEGEND,
2361 ARG_KILL_WHO,
2362 ARG_READ_ONLY,
2363 ARG_MKDIR,
2364 ARG_NO_ASK_PASSWORD,
2365 ARG_VERIFY,
2366 ARG_FORCE,
2367 ARG_DKR_INDEX_URL,
2368 ARG_FORMAT,
2369 };
2370
2371 static const struct option options[] = {
2372 { "help", no_argument, NULL, 'h' },
2373 { "version", no_argument, NULL, ARG_VERSION },
2374 { "property", required_argument, NULL, 'p' },
2375 { "all", no_argument, NULL, 'a' },
2376 { "full", no_argument, NULL, 'l' },
2377 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2378 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2379 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2380 { "signal", required_argument, NULL, 's' },
2381 { "host", required_argument, NULL, 'H' },
2382 { "machine", required_argument, NULL, 'M' },
2383 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2384 { "mkdir", no_argument, NULL, ARG_MKDIR },
2385 { "quiet", no_argument, NULL, 'q' },
2386 { "lines", required_argument, NULL, 'n' },
2387 { "output", required_argument, NULL, 'o' },
2388 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2389 { "verify", required_argument, NULL, ARG_VERIFY },
2390 { "force", no_argument, NULL, ARG_FORCE },
2391 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
2392 { "format", required_argument, NULL, ARG_FORMAT },
2393 {}
2394 };
2395
2396 int c, r;
2397
2398 assert(argc >= 0);
2399 assert(argv);
2400
2401 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2402
2403 switch (c) {
2404
2405 case 'h':
2406 return help(0, NULL, NULL);
2407
2408 case ARG_VERSION:
2409 puts(PACKAGE_STRING);
2410 puts(SYSTEMD_FEATURES);
2411 return 0;
2412
2413 case 'p':
2414 r = strv_extend(&arg_property, optarg);
2415 if (r < 0)
2416 return log_oom();
2417
2418 /* If the user asked for a particular
2419 * property, show it to him, even if it is
2420 * empty. */
2421 arg_all = true;
2422 break;
2423
2424 case 'a':
2425 arg_all = true;
2426 break;
2427
2428 case 'l':
2429 arg_full = true;
2430 break;
2431
2432 case 'n':
2433 if (safe_atou(optarg, &arg_lines) < 0) {
2434 log_error("Failed to parse lines '%s'", optarg);
2435 return -EINVAL;
2436 }
2437 break;
2438
2439 case 'o':
2440 arg_output = output_mode_from_string(optarg);
2441 if (arg_output < 0) {
2442 log_error("Unknown output '%s'.", optarg);
2443 return -EINVAL;
2444 }
2445 break;
2446
2447 case ARG_NO_PAGER:
2448 arg_no_pager = true;
2449 break;
2450
2451 case ARG_NO_LEGEND:
2452 arg_legend = false;
2453 break;
2454
2455 case ARG_KILL_WHO:
2456 arg_kill_who = optarg;
2457 break;
2458
2459 case 's':
2460 arg_signal = signal_from_string_try_harder(optarg);
2461 if (arg_signal < 0) {
2462 log_error("Failed to parse signal string %s.", optarg);
2463 return -EINVAL;
2464 }
2465 break;
2466
2467 case ARG_NO_ASK_PASSWORD:
2468 arg_ask_password = false;
2469 break;
2470
2471 case 'H':
2472 arg_transport = BUS_TRANSPORT_REMOTE;
2473 arg_host = optarg;
2474 break;
2475
2476 case 'M':
2477 arg_transport = BUS_TRANSPORT_MACHINE;
2478 arg_host = optarg;
2479 break;
2480
2481 case ARG_READ_ONLY:
2482 arg_read_only = true;
2483 break;
2484
2485 case ARG_MKDIR:
2486 arg_mkdir = true;
2487 break;
2488
2489 case 'q':
2490 arg_quiet = true;
2491 break;
2492
2493 case ARG_VERIFY:
2494 arg_verify = import_verify_from_string(optarg);
2495 if (arg_verify < 0) {
2496 log_error("Failed to parse --verify= setting: %s", optarg);
2497 return -EINVAL;
2498 }
2499 break;
2500
2501 case ARG_FORCE:
2502 arg_force = true;
2503 break;
2504
2505 case ARG_DKR_INDEX_URL:
2506 if (!http_url_is_valid(optarg)) {
2507 log_error("Index URL is invalid: %s", optarg);
2508 return -EINVAL;
2509 }
2510
2511 arg_dkr_index_url = optarg;
2512 break;
2513
2514 case ARG_FORMAT:
2515 if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
2516 log_error("Unknown format: %s", optarg);
2517 return -EINVAL;
2518 }
2519
2520 arg_format = optarg;
2521 break;
2522
2523 case '?':
2524 return -EINVAL;
2525
2526 default:
2527 assert_not_reached("Unhandled option");
2528 }
2529
2530 return 1;
2531 }
2532
2533 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2534
2535 static const Verb verbs[] = {
2536 { "help", VERB_ANY, VERB_ANY, 0, help },
2537 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2538 { "list-images", VERB_ANY, 1, 0, list_images },
2539 { "status", 2, VERB_ANY, 0, show_machine },
2540 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
2541 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2542 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2543 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2544 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2545 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2546 { "kill", 2, VERB_ANY, 0, kill_machine },
2547 { "login", 2, 2, 0, login_machine },
2548 { "bind", 3, 4, 0, bind_mount },
2549 { "copy-to", 3, 4, 0, copy_files },
2550 { "copy-from", 3, 4, 0, copy_files },
2551 { "remove", 2, VERB_ANY, 0, remove_image },
2552 { "rename", 3, 3, 0, rename_image },
2553 { "clone", 3, 3, 0, clone_image },
2554 { "read-only", 2, 3, 0, read_only_image },
2555 { "start", 2, VERB_ANY, 0, start_machine },
2556 { "enable", 2, VERB_ANY, 0, enable_machine },
2557 { "disable", 2, VERB_ANY, 0, enable_machine },
2558 { "import-tar", 2, 3, 0, import_tar },
2559 { "import-raw", 2, 3, 0, import_raw },
2560 { "export-tar", 2, 3, 0, export_tar },
2561 { "export-raw", 2, 3, 0, export_raw },
2562 { "pull-tar", 2, 3, 0, pull_tar },
2563 { "pull-raw", 2, 3, 0, pull_raw },
2564 { "pull-dkr", 2, 3, 0, pull_dkr },
2565 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2566 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
2567 { "set-limit", 2, 3, 0, set_limit },
2568 {}
2569 };
2570
2571 return dispatch_verb(argc, argv, verbs, bus);
2572 }
2573
2574 int main(int argc, char*argv[]) {
2575 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
2576 int r;
2577
2578 setlocale(LC_ALL, "");
2579 log_parse_environment();
2580 log_open();
2581
2582 r = parse_argv(argc, argv);
2583 if (r <= 0)
2584 goto finish;
2585
2586 r = bus_open_transport(arg_transport, arg_host, false, &bus);
2587 if (r < 0) {
2588 log_error_errno(r, "Failed to create bus connection: %m");
2589 goto finish;
2590 }
2591
2592 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2593
2594 r = machinectl_main(argc, argv, bus);
2595
2596 finish:
2597 pager_close();
2598 polkit_agent_close();
2599
2600 strv_free(arg_property);
2601
2602 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2603 }