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