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