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