]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machinectl.c
fc194929a1956376f58f2276995daf2dc180d165
[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 (void) 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 (void) 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 const char *name;
643 sd_id128_t id;
644 const char *class;
645 const char *service;
646 const char *unit;
647 const 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->netif);
657 zero(*info);
658 }
659 }
660
661 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
662 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
663 char since2[FORMAT_TIMESTAMP_MAX], *s2;
664 int ifi = -1;
665
666 assert(bus);
667 assert(i);
668
669 fputs(strna(i->name), stdout);
670
671 if (!sd_id128_is_null(i->id))
672 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
673 else
674 putchar('\n');
675
676 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
677 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
678
679 if (s1)
680 printf("\t Since: %s; %s\n", s2, s1);
681 else if (s2)
682 printf("\t Since: %s\n", s2);
683
684 if (i->leader > 0) {
685 _cleanup_free_ char *t = NULL;
686
687 printf("\t Leader: %u", (unsigned) i->leader);
688
689 get_process_comm(i->leader, &t);
690 if (t)
691 printf(" (%s)", t);
692
693 putchar('\n');
694 }
695
696 if (i->service) {
697 printf("\t Service: %s", i->service);
698
699 if (i->class)
700 printf("; class %s", i->class);
701
702 putchar('\n');
703 } else if (i->class)
704 printf("\t Class: %s\n", i->class);
705
706 if (i->root_directory)
707 printf("\t Root: %s\n", i->root_directory);
708
709 if (i->n_netif > 0) {
710 unsigned c;
711
712 fputs("\t Iface:", stdout);
713
714 for (c = 0; c < i->n_netif; c++) {
715 char name[IF_NAMESIZE+1] = "";
716
717 if (if_indextoname(i->netif[c], name)) {
718 fputc(' ', stdout);
719 fputs(name, stdout);
720
721 if (ifi < 0)
722 ifi = i->netif[c];
723 else
724 ifi = 0;
725 } else
726 printf(" %i", i->netif[c]);
727 }
728
729 fputc('\n', stdout);
730 }
731
732 if (print_addresses(bus, i->name, ifi,
733 "\t Address: ",
734 "\n\t ",
735 ALL_IP_ADDRESSES) > 0)
736 fputc('\n', stdout);
737
738 print_os_release(bus, "GetMachineOSRelease", i->name, "\t OS: ");
739
740 print_uid_shift(bus, i->name);
741
742 if (i->unit) {
743 printf("\t Unit: %s\n", i->unit);
744 show_unit_cgroup(bus, i->unit, i->leader);
745
746 if (arg_transport == BUS_TRANSPORT_LOCAL)
747
748 show_journal_by_unit(
749 stdout,
750 i->unit,
751 arg_output,
752 0,
753 i->timestamp.monotonic,
754 arg_lines,
755 0,
756 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
757 SD_JOURNAL_LOCAL_ONLY,
758 true,
759 NULL);
760 }
761 }
762
763 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
764 MachineStatusInfo *i = userdata;
765 size_t l;
766 const void *v;
767 int r;
768
769 assert_cc(sizeof(int32_t) == sizeof(int));
770 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
771 if (r < 0)
772 return r;
773 if (r == 0)
774 return -EBADMSG;
775
776 i->n_netif = l / sizeof(int32_t);
777 i->netif = memdup(v, l);
778 if (!i->netif)
779 return -ENOMEM;
780
781 return 0;
782 }
783
784 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
785
786 static const struct bus_properties_map map[] = {
787 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
788 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
789 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
790 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
791 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
792 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
793 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
794 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
795 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
796 { "NetworkInterfaces", "ai", map_netif, 0 },
797 {}
798 };
799
800 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
801 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
802 _cleanup_(machine_status_info_clear) MachineStatusInfo info = {};
803 int r;
804
805 assert(verb);
806 assert(bus);
807 assert(path);
808 assert(new_line);
809
810 r = bus_map_all_properties(bus,
811 "org.freedesktop.machine1",
812 path,
813 map,
814 0,
815 &error,
816 &m,
817 &info);
818 if (r < 0)
819 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
820
821 if (*new_line)
822 printf("\n");
823 *new_line = true;
824
825 print_machine_status_info(bus, &info);
826
827 return r;
828 }
829
830 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
831 int r;
832
833 assert(bus);
834 assert(path);
835 assert(new_line);
836
837 if (*new_line)
838 printf("\n");
839
840 *new_line = true;
841
842 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, NULL, arg_property, arg_value, arg_all, NULL);
843 if (r < 0)
844 log_error_errno(r, "Could not get properties: %m");
845
846 return r;
847 }
848
849 static int show_machine(int argc, char *argv[], void *userdata) {
850
851 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
852 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
853 bool properties, new_line = false;
854 sd_bus *bus = userdata;
855 int r = 0, i;
856
857 assert(bus);
858
859 properties = !strstr(argv[0], "status");
860
861 (void) pager_open(arg_no_pager, false);
862
863 if (properties && argc <= 1) {
864
865 /* If no argument is specified, inspect the manager
866 * itself */
867 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
868 if (r < 0)
869 return r;
870 }
871
872 for (i = 1; i < argc; i++) {
873 const char *path = NULL;
874
875 r = sd_bus_call_method(bus,
876 "org.freedesktop.machine1",
877 "/org/freedesktop/machine1",
878 "org.freedesktop.machine1.Manager",
879 "GetMachine",
880 &error,
881 &reply,
882 "s", argv[i]);
883 if (r < 0) {
884 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
885 return r;
886 }
887
888 r = sd_bus_message_read(reply, "o", &path);
889 if (r < 0)
890 return bus_log_parse_error(r);
891
892 if (properties)
893 r = show_machine_properties(bus, path, &new_line);
894 else
895 r = show_machine_info(argv[0], bus, path, &new_line);
896 }
897
898 return r;
899 }
900
901 static int print_image_hostname(sd_bus *bus, const char *name) {
902 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
903 const char *hn;
904 int r;
905
906 r = sd_bus_call_method(
907 bus,
908 "org.freedesktop.machine1",
909 "/org/freedesktop/machine1",
910 "org.freedesktop.machine1.Manager",
911 "GetImageHostname",
912 NULL, &reply, "s", name);
913 if (r < 0)
914 return r;
915
916 r = sd_bus_message_read(reply, "s", &hn);
917 if (r < 0)
918 return r;
919
920 if (!isempty(hn))
921 printf("\tHostname: %s\n", hn);
922
923 return 0;
924 }
925
926 static int print_image_machine_id(sd_bus *bus, const char *name) {
927 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
928 sd_id128_t id = SD_ID128_NULL;
929 const void *p;
930 size_t size;
931 int r;
932
933 r = sd_bus_call_method(
934 bus,
935 "org.freedesktop.machine1",
936 "/org/freedesktop/machine1",
937 "org.freedesktop.machine1.Manager",
938 "GetImageMachineID",
939 NULL, &reply, "s", name);
940 if (r < 0)
941 return r;
942
943 r = sd_bus_message_read_array(reply, 'y', &p, &size);
944 if (r < 0)
945 return r;
946
947 if (size == sizeof(sd_id128_t))
948 memcpy(&id, p, size);
949
950 if (!sd_id128_is_null(id))
951 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
952
953 return 0;
954 }
955
956 static int print_image_machine_info(sd_bus *bus, const char *name) {
957 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
958 int r;
959
960 r = sd_bus_call_method(
961 bus,
962 "org.freedesktop.machine1",
963 "/org/freedesktop/machine1",
964 "org.freedesktop.machine1.Manager",
965 "GetImageMachineInfo",
966 NULL, &reply, "s", name);
967 if (r < 0)
968 return r;
969
970 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
971 if (r < 0)
972 return r;
973
974 for (;;) {
975 const char *p, *q;
976
977 r = sd_bus_message_read(reply, "{ss}", &p, &q);
978 if (r < 0)
979 return r;
980 if (r == 0)
981 break;
982
983 if (streq(p, "DEPLOYMENT"))
984 printf(" Deployment: %s\n", q);
985 }
986
987 r = sd_bus_message_exit_container(reply);
988 if (r < 0)
989 return r;
990
991 return 0;
992 }
993
994 typedef struct ImageStatusInfo {
995 const char *name;
996 const char *path;
997 const char *type;
998 bool read_only;
999 usec_t crtime;
1000 usec_t mtime;
1001 uint64_t usage;
1002 uint64_t limit;
1003 uint64_t usage_exclusive;
1004 uint64_t limit_exclusive;
1005 } ImageStatusInfo;
1006
1007 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
1008 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
1009 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
1010 char bs[FORMAT_BYTES_MAX], *s3;
1011 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
1012
1013 assert(bus);
1014 assert(i);
1015
1016 if (i->name) {
1017 fputs(i->name, stdout);
1018 putchar('\n');
1019 }
1020
1021 if (i->type)
1022 printf("\t Type: %s\n", i->type);
1023
1024 if (i->path)
1025 printf("\t Path: %s\n", i->path);
1026
1027 (void) print_image_hostname(bus, i->name);
1028 (void) print_image_machine_id(bus, i->name);
1029 (void) print_image_machine_info(bus, i->name);
1030
1031 print_os_release(bus, "GetImageOSRelease", i->name, "\t OS: ");
1032
1033 printf("\t RO: %s%s%s\n",
1034 i->read_only ? ansi_highlight_red() : "",
1035 i->read_only ? "read-only" : "writable",
1036 i->read_only ? ansi_normal() : "");
1037
1038 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
1039 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
1040 if (s1 && s2)
1041 printf("\t Created: %s; %s\n", s2, s1);
1042 else if (s2)
1043 printf("\t Created: %s\n", s2);
1044
1045 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
1046 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
1047 if (s1 && s2)
1048 printf("\tModified: %s; %s\n", s2, s1);
1049 else if (s2)
1050 printf("\tModified: %s\n", s2);
1051
1052 s3 = format_bytes(bs, sizeof(bs), i->usage);
1053 s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
1054 if (s3 && s4)
1055 printf("\t Usage: %s (exclusive: %s)\n", s3, s4);
1056 else if (s3)
1057 printf("\t Usage: %s\n", s3);
1058
1059 s3 = format_bytes(bs, sizeof(bs), i->limit);
1060 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
1061 if (s3 && s4)
1062 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
1063 else if (s3)
1064 printf("\t Limit: %s\n", s3);
1065 }
1066
1067 static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
1068
1069 static const struct bus_properties_map map[] = {
1070 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
1071 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
1072 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
1073 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
1074 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
1075 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
1076 { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
1077 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
1078 { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
1079 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
1080 {}
1081 };
1082
1083 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1084 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1085 ImageStatusInfo info = {};
1086 int r;
1087
1088 assert(bus);
1089 assert(path);
1090 assert(new_line);
1091
1092 r = bus_map_all_properties(bus,
1093 "org.freedesktop.machine1",
1094 path,
1095 map,
1096 BUS_MAP_BOOLEAN_AS_BOOL,
1097 &error,
1098 &m,
1099 &info);
1100 if (r < 0)
1101 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
1102
1103 if (*new_line)
1104 printf("\n");
1105 *new_line = true;
1106
1107 print_image_status_info(bus, &info);
1108
1109 return r;
1110 }
1111
1112 typedef struct PoolStatusInfo {
1113 const char *path;
1114 uint64_t usage;
1115 uint64_t limit;
1116 } PoolStatusInfo;
1117
1118 static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
1119 char bs[FORMAT_BYTES_MAX], *s;
1120
1121 if (i->path)
1122 printf("\t Path: %s\n", i->path);
1123
1124 s = format_bytes(bs, sizeof(bs), i->usage);
1125 if (s)
1126 printf("\t Usage: %s\n", s);
1127
1128 s = format_bytes(bs, sizeof(bs), i->limit);
1129 if (s)
1130 printf("\t Limit: %s\n", s);
1131 }
1132
1133 static int show_pool_info(sd_bus *bus) {
1134
1135 static const struct bus_properties_map map[] = {
1136 { "PoolPath", "s", NULL, offsetof(PoolStatusInfo, path) },
1137 { "PoolUsage", "t", NULL, offsetof(PoolStatusInfo, usage) },
1138 { "PoolLimit", "t", NULL, offsetof(PoolStatusInfo, limit) },
1139 {}
1140 };
1141
1142 PoolStatusInfo info = {
1143 .usage = (uint64_t) -1,
1144 .limit = (uint64_t) -1,
1145 };
1146
1147 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1148 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1149 int r;
1150
1151 assert(bus);
1152
1153 r = bus_map_all_properties(bus,
1154 "org.freedesktop.machine1",
1155 "/org/freedesktop/machine1",
1156 map,
1157 0,
1158 &error,
1159 &m,
1160 &info);
1161 if (r < 0)
1162 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
1163
1164 print_pool_status_info(bus, &info);
1165
1166 return 0;
1167 }
1168
1169
1170 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
1171 int r;
1172
1173 assert(bus);
1174 assert(path);
1175 assert(new_line);
1176
1177 if (*new_line)
1178 printf("\n");
1179
1180 *new_line = true;
1181
1182 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, NULL, arg_property, arg_value, arg_all, NULL);
1183 if (r < 0)
1184 log_error_errno(r, "Could not get properties: %m");
1185
1186 return r;
1187 }
1188
1189 static int show_image(int argc, char *argv[], void *userdata) {
1190
1191 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1192 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1193 bool properties, new_line = false;
1194 sd_bus *bus = userdata;
1195 int r = 0, i;
1196
1197 assert(bus);
1198
1199 properties = !strstr(argv[0], "status");
1200
1201 (void) pager_open(arg_no_pager, false);
1202
1203 if (argc <= 1) {
1204
1205 /* If no argument is specified, inspect the manager
1206 * itself */
1207
1208 if (properties)
1209 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
1210 else
1211 r = show_pool_info(bus);
1212 if (r < 0)
1213 return r;
1214 }
1215
1216 for (i = 1; i < argc; i++) {
1217 const char *path = NULL;
1218
1219 r = sd_bus_call_method(
1220 bus,
1221 "org.freedesktop.machine1",
1222 "/org/freedesktop/machine1",
1223 "org.freedesktop.machine1.Manager",
1224 "GetImage",
1225 &error,
1226 &reply,
1227 "s", argv[i]);
1228 if (r < 0) {
1229 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
1230 return r;
1231 }
1232
1233 r = sd_bus_message_read(reply, "o", &path);
1234 if (r < 0)
1235 return bus_log_parse_error(r);
1236
1237 if (properties)
1238 r = show_image_properties(bus, path, &new_line);
1239 else
1240 r = show_image_info(bus, path, &new_line);
1241 }
1242
1243 return r;
1244 }
1245
1246 static int kill_machine(int argc, char *argv[], void *userdata) {
1247 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1248 sd_bus *bus = userdata;
1249 int r, i;
1250
1251 assert(bus);
1252
1253 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1254
1255 if (!arg_kill_who)
1256 arg_kill_who = "all";
1257
1258 for (i = 1; i < argc; i++) {
1259 r = sd_bus_call_method(
1260 bus,
1261 "org.freedesktop.machine1",
1262 "/org/freedesktop/machine1",
1263 "org.freedesktop.machine1.Manager",
1264 "KillMachine",
1265 &error,
1266 NULL,
1267 "ssi", argv[i], arg_kill_who, arg_signal);
1268 if (r < 0) {
1269 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
1270 return r;
1271 }
1272 }
1273
1274 return 0;
1275 }
1276
1277 static int reboot_machine(int argc, char *argv[], void *userdata) {
1278 arg_kill_who = "leader";
1279 arg_signal = SIGINT; /* sysvinit + systemd */
1280
1281 return kill_machine(argc, argv, userdata);
1282 }
1283
1284 static int poweroff_machine(int argc, char *argv[], void *userdata) {
1285 arg_kill_who = "leader";
1286 arg_signal = SIGRTMIN+4; /* only systemd */
1287
1288 return kill_machine(argc, argv, userdata);
1289 }
1290
1291 static int terminate_machine(int argc, char *argv[], void *userdata) {
1292 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1293 sd_bus *bus = userdata;
1294 int r, i;
1295
1296 assert(bus);
1297
1298 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1299
1300 for (i = 1; i < argc; i++) {
1301 r = sd_bus_call_method(
1302 bus,
1303 "org.freedesktop.machine1",
1304 "/org/freedesktop/machine1",
1305 "org.freedesktop.machine1.Manager",
1306 "TerminateMachine",
1307 &error,
1308 NULL,
1309 "s", argv[i]);
1310 if (r < 0) {
1311 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
1312 return r;
1313 }
1314 }
1315
1316 return 0;
1317 }
1318
1319 static int copy_files(int argc, char *argv[], void *userdata) {
1320 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1321 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1322 _cleanup_free_ char *abs_host_path = NULL;
1323 char *dest, *host_path, *container_path;
1324 sd_bus *bus = userdata;
1325 bool copy_from;
1326 int r;
1327
1328 assert(bus);
1329
1330 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1331
1332 copy_from = streq(argv[0], "copy-from");
1333 dest = argv[3] ?: argv[2];
1334 host_path = copy_from ? dest : argv[2];
1335 container_path = copy_from ? argv[2] : dest;
1336
1337 if (!path_is_absolute(host_path)) {
1338 r = path_make_absolute_cwd(host_path, &abs_host_path);
1339 if (r < 0)
1340 return log_error_errno(r, "Failed to make path absolute: %m");
1341
1342 host_path = abs_host_path;
1343 }
1344
1345 r = sd_bus_message_new_method_call(
1346 bus,
1347 &m,
1348 "org.freedesktop.machine1",
1349 "/org/freedesktop/machine1",
1350 "org.freedesktop.machine1.Manager",
1351 copy_from ? "CopyFromMachine" : "CopyToMachine");
1352 if (r < 0)
1353 return bus_log_create_error(r);
1354
1355 r = sd_bus_message_append(
1356 m,
1357 "sss",
1358 argv[1],
1359 copy_from ? container_path : host_path,
1360 copy_from ? host_path : container_path);
1361 if (r < 0)
1362 return bus_log_create_error(r);
1363
1364 /* This is a slow operation, hence turn off any method call timeouts */
1365 r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
1366 if (r < 0)
1367 return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r));
1368
1369 return 0;
1370 }
1371
1372 static int bind_mount(int argc, char *argv[], void *userdata) {
1373 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1374 sd_bus *bus = userdata;
1375 int r;
1376
1377 assert(bus);
1378
1379 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1380
1381 r = sd_bus_call_method(
1382 bus,
1383 "org.freedesktop.machine1",
1384 "/org/freedesktop/machine1",
1385 "org.freedesktop.machine1.Manager",
1386 "BindMountMachine",
1387 &error,
1388 NULL,
1389 "sssbb",
1390 argv[1],
1391 argv[2],
1392 argv[3],
1393 arg_read_only,
1394 arg_mkdir);
1395 if (r < 0) {
1396 log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
1397 return r;
1398 }
1399
1400 return 0;
1401 }
1402
1403 static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1404 PTYForward ** forward = (PTYForward**) userdata;
1405 int r;
1406
1407 assert(m);
1408 assert(forward);
1409
1410 if (*forward) {
1411 /* If the forwarder is already initialized, tell it to
1412 * exit on the next vhangup(), so that we still flush
1413 * out what might be queued and exit then. */
1414
1415 r = pty_forward_set_ignore_vhangup(*forward, false);
1416 if (r >= 0)
1417 return 0;
1418
1419 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
1420 }
1421
1422 /* On error, or when the forwarder is not initialized yet, quit immediately */
1423 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
1424 return 0;
1425 }
1426
1427 static int process_forward(sd_event *event, PTYForward **forward, int master, PTYForwardFlags flags, const char *name) {
1428 char last_char = 0;
1429 bool machine_died;
1430 int ret = 0, r;
1431
1432 assert(event);
1433 assert(master >= 0);
1434 assert(name);
1435
1436 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
1437
1438 if (!arg_quiet) {
1439 if (streq(name, ".host"))
1440 log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
1441 else
1442 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
1443 }
1444
1445 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1446 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1447
1448 r = pty_forward_new(event, master, flags, forward);
1449 if (r < 0)
1450 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1451
1452 r = sd_event_loop(event);
1453 if (r < 0)
1454 return log_error_errno(r, "Failed to run event loop: %m");
1455
1456 pty_forward_get_last_char(*forward, &last_char);
1457
1458 machine_died =
1459 (flags & PTY_FORWARD_IGNORE_VHANGUP) &&
1460 pty_forward_get_ignore_vhangup(*forward) == 0;
1461
1462 *forward = pty_forward_free(*forward);
1463
1464 if (last_char != '\n')
1465 fputc('\n', stdout);
1466
1467 if (!arg_quiet) {
1468 if (machine_died)
1469 log_info("Machine %s terminated.", name);
1470 else if (streq(name, ".host"))
1471 log_info("Connection to the local host terminated.");
1472 else
1473 log_info("Connection to machine %s terminated.", name);
1474 }
1475
1476 sd_event_get_exit_code(event, &ret);
1477 return ret;
1478 }
1479
1480 static int parse_machine_uid(const char *spec, const char **machine, char **uid) {
1481 /*
1482 * Whatever is specified in the spec takes priority over global arguments.
1483 */
1484 char *_uid = NULL;
1485 const char *_machine = NULL;
1486
1487 if (spec) {
1488 const char *at;
1489
1490 at = strchr(spec, '@');
1491 if (at) {
1492 if (at == spec)
1493 /* Do the same as ssh and refuse "@host". */
1494 return -EINVAL;
1495
1496 _machine = at + 1;
1497 _uid = strndup(spec, at - spec);
1498 if (!_uid)
1499 return -ENOMEM;
1500 } else
1501 _machine = spec;
1502 };
1503
1504 if (arg_uid && !_uid) {
1505 _uid = strdup(arg_uid);
1506 if (!_uid)
1507 return -ENOMEM;
1508 }
1509
1510 *uid = _uid;
1511 *machine = isempty(_machine) ? ".host" : _machine;
1512 return 0;
1513 }
1514
1515 static int login_machine(int argc, char *argv[], void *userdata) {
1516 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1517 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1518 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1519 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
1520 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
1521 int master = -1, r;
1522 sd_bus *bus = userdata;
1523 const char *pty, *match, *machine;
1524
1525 assert(bus);
1526
1527 if (!strv_isempty(arg_setenv) || arg_uid) {
1528 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1529 return -EINVAL;
1530 }
1531
1532 if (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) {
1533 log_error("Login only supported on local machines.");
1534 return -EOPNOTSUPP;
1535 }
1536
1537 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1538
1539 r = sd_event_default(&event);
1540 if (r < 0)
1541 return log_error_errno(r, "Failed to get event loop: %m");
1542
1543 r = sd_bus_attach_event(bus, event, 0);
1544 if (r < 0)
1545 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1546
1547 machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1];
1548
1549 match = strjoina("type='signal',"
1550 "sender='org.freedesktop.machine1',"
1551 "path='/org/freedesktop/machine1',",
1552 "interface='org.freedesktop.machine1.Manager',"
1553 "member='MachineRemoved',"
1554 "arg0='", machine, "'");
1555
1556 r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward);
1557 if (r < 0)
1558 return log_error_errno(r, "Failed to request machine removal match: %m");
1559
1560 r = sd_bus_call_method(
1561 bus,
1562 "org.freedesktop.machine1",
1563 "/org/freedesktop/machine1",
1564 "org.freedesktop.machine1.Manager",
1565 "OpenMachineLogin",
1566 &error,
1567 &reply,
1568 "s", machine);
1569 if (r < 0) {
1570 log_error("Failed to get login PTY: %s", bus_error_message(&error, -r));
1571 return r;
1572 }
1573
1574 r = sd_bus_message_read(reply, "hs", &master, &pty);
1575 if (r < 0)
1576 return bus_log_parse_error(r);
1577
1578 return process_forward(event, &forward, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
1579 }
1580
1581 static int shell_machine(int argc, char *argv[], void *userdata) {
1582 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
1583 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1584 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1585 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
1586 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
1587 int master = -1, r;
1588 sd_bus *bus = userdata;
1589 const char *pty, *match, *machine, *path;
1590 _cleanup_free_ char *uid = NULL;
1591
1592 assert(bus);
1593
1594 if (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) {
1595 log_error("Shell only supported on local machines.");
1596 return -EOPNOTSUPP;
1597 }
1598
1599 /* Pass $TERM to shell session, if not explicitly specified. */
1600 if (!strv_find_prefix(arg_setenv, "TERM=")) {
1601 const char *t;
1602
1603 t = strv_find_prefix(environ, "TERM=");
1604 if (t) {
1605 if (strv_extend(&arg_setenv, t) < 0)
1606 return log_oom();
1607 }
1608 }
1609
1610 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1611
1612 r = sd_event_default(&event);
1613 if (r < 0)
1614 return log_error_errno(r, "Failed to get event loop: %m");
1615
1616 r = sd_bus_attach_event(bus, event, 0);
1617 if (r < 0)
1618 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1619
1620 r = parse_machine_uid(argc >= 2 ? argv[1] : NULL, &machine, &uid);
1621 if (r < 0)
1622 return log_error_errno(r, "Failed to parse machine specification: %m");
1623
1624 match = strjoina("type='signal',"
1625 "sender='org.freedesktop.machine1',"
1626 "path='/org/freedesktop/machine1',",
1627 "interface='org.freedesktop.machine1.Manager',"
1628 "member='MachineRemoved',"
1629 "arg0='", machine, "'");
1630
1631 r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward);
1632 if (r < 0)
1633 return log_error_errno(r, "Failed to request machine removal match: %m");
1634
1635 r = sd_bus_message_new_method_call(
1636 bus,
1637 &m,
1638 "org.freedesktop.machine1",
1639 "/org/freedesktop/machine1",
1640 "org.freedesktop.machine1.Manager",
1641 "OpenMachineShell");
1642 if (r < 0)
1643 return bus_log_create_error(r);
1644
1645 path = argc < 3 || isempty(argv[2]) ? NULL : argv[2];
1646
1647 r = sd_bus_message_append(m, "sss", machine, uid, path);
1648 if (r < 0)
1649 return bus_log_create_error(r);
1650
1651 r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2);
1652 if (r < 0)
1653 return bus_log_create_error(r);
1654
1655 r = sd_bus_message_append_strv(m, arg_setenv);
1656 if (r < 0)
1657 return bus_log_create_error(r);
1658
1659 r = sd_bus_call(bus, m, 0, &error, &reply);
1660 if (r < 0) {
1661 log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r));
1662 return r;
1663 }
1664
1665 r = sd_bus_message_read(reply, "hs", &master, &pty);
1666 if (r < 0)
1667 return bus_log_parse_error(r);
1668
1669 return process_forward(event, &forward, master, 0, machine);
1670 }
1671
1672 static int remove_image(int argc, char *argv[], void *userdata) {
1673 sd_bus *bus = userdata;
1674 int r, i;
1675
1676 assert(bus);
1677
1678 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1679
1680 for (i = 1; i < argc; i++) {
1681 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1682 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1683
1684 r = sd_bus_message_new_method_call(
1685 bus,
1686 &m,
1687 "org.freedesktop.machine1",
1688 "/org/freedesktop/machine1",
1689 "org.freedesktop.machine1.Manager",
1690 "RemoveImage");
1691 if (r < 0)
1692 return bus_log_create_error(r);
1693
1694 r = sd_bus_message_append(m, "s", argv[i]);
1695 if (r < 0)
1696 return bus_log_create_error(r);
1697
1698 /* This is a slow operation, hence turn off any method call timeouts */
1699 r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
1700 if (r < 0)
1701 return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r));
1702 }
1703
1704 return 0;
1705 }
1706
1707 static int rename_image(int argc, char *argv[], void *userdata) {
1708 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1709 sd_bus *bus = userdata;
1710 int r;
1711
1712 assert(bus);
1713
1714 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1715
1716 r = sd_bus_call_method(
1717 bus,
1718 "org.freedesktop.machine1",
1719 "/org/freedesktop/machine1",
1720 "org.freedesktop.machine1.Manager",
1721 "RenameImage",
1722 &error,
1723 NULL,
1724 "ss", argv[1], argv[2]);
1725 if (r < 0) {
1726 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1727 return r;
1728 }
1729
1730 return 0;
1731 }
1732
1733 static int clone_image(int argc, char *argv[], void *userdata) {
1734 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1735 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1736 sd_bus *bus = userdata;
1737 int r;
1738
1739 assert(bus);
1740
1741 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1742
1743 r = sd_bus_message_new_method_call(
1744 bus,
1745 &m,
1746 "org.freedesktop.machine1",
1747 "/org/freedesktop/machine1",
1748 "org.freedesktop.machine1.Manager",
1749 "CloneImage");
1750 if (r < 0)
1751 return bus_log_create_error(r);
1752
1753 r = sd_bus_message_append(m, "ssb", argv[1], argv[2], arg_read_only);
1754 if (r < 0)
1755 return bus_log_create_error(r);
1756
1757 /* This is a slow operation, hence turn off any method call timeouts */
1758 r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
1759 if (r < 0)
1760 return log_error_errno(r, "Could not clone image: %s", bus_error_message(&error, r));
1761
1762 return 0;
1763 }
1764
1765 static int read_only_image(int argc, char *argv[], void *userdata) {
1766 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1767 sd_bus *bus = userdata;
1768 int b = true, r;
1769
1770 assert(bus);
1771
1772 if (argc > 2) {
1773 b = parse_boolean(argv[2]);
1774 if (b < 0) {
1775 log_error("Failed to parse boolean argument: %s", argv[2]);
1776 return -EINVAL;
1777 }
1778 }
1779
1780 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1781
1782 r = sd_bus_call_method(
1783 bus,
1784 "org.freedesktop.machine1",
1785 "/org/freedesktop/machine1",
1786 "org.freedesktop.machine1.Manager",
1787 "MarkImageReadOnly",
1788 &error,
1789 NULL,
1790 "sb", argv[1], b);
1791 if (r < 0) {
1792 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1793 return r;
1794 }
1795
1796 return 0;
1797 }
1798
1799 static int image_exists(sd_bus *bus, const char *name) {
1800 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1801 int r;
1802
1803 assert(bus);
1804 assert(name);
1805
1806 r = sd_bus_call_method(
1807 bus,
1808 "org.freedesktop.machine1",
1809 "/org/freedesktop/machine1",
1810 "org.freedesktop.machine1.Manager",
1811 "GetImage",
1812 &error,
1813 NULL,
1814 "s", name);
1815 if (r < 0) {
1816 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_IMAGE))
1817 return 0;
1818
1819 return log_error_errno(r, "Failed to check whether image %s exists: %s", name, bus_error_message(&error, -r));
1820 }
1821
1822 return 1;
1823 }
1824
1825 static int make_service_name(const char *name, char **ret) {
1826 int r;
1827
1828 assert(name);
1829 assert(ret);
1830
1831 if (!machine_name_is_valid(name)) {
1832 log_error("Invalid machine name %s.", name);
1833 return -EINVAL;
1834 }
1835
1836 r = unit_name_build("systemd-nspawn", name, ".service", ret);
1837 if (r < 0)
1838 return log_error_errno(r, "Failed to build unit name: %m");
1839
1840 return 0;
1841 }
1842
1843 static int start_machine(int argc, char *argv[], void *userdata) {
1844 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1845 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1846 sd_bus *bus = userdata;
1847 int r, i;
1848
1849 assert(bus);
1850
1851 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1852
1853 r = bus_wait_for_jobs_new(bus, &w);
1854 if (r < 0)
1855 return log_oom();
1856
1857 for (i = 1; i < argc; i++) {
1858 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1859 _cleanup_free_ char *unit = NULL;
1860 const char *object;
1861
1862 r = make_service_name(argv[i], &unit);
1863 if (r < 0)
1864 return r;
1865
1866 r = image_exists(bus, argv[i]);
1867 if (r < 0)
1868 return r;
1869 if (r == 0) {
1870 log_error("Machine image '%s' does not exist.", argv[1]);
1871 return -ENXIO;
1872 }
1873
1874 r = sd_bus_call_method(
1875 bus,
1876 "org.freedesktop.systemd1",
1877 "/org/freedesktop/systemd1",
1878 "org.freedesktop.systemd1.Manager",
1879 "StartUnit",
1880 &error,
1881 &reply,
1882 "ss", unit, "fail");
1883 if (r < 0) {
1884 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1885 return r;
1886 }
1887
1888 r = sd_bus_message_read(reply, "o", &object);
1889 if (r < 0)
1890 return bus_log_parse_error(r);
1891
1892 r = bus_wait_for_jobs_add(w, object);
1893 if (r < 0)
1894 return log_oom();
1895 }
1896
1897 r = bus_wait_for_jobs(w, arg_quiet, NULL);
1898 if (r < 0)
1899 return r;
1900
1901 return 0;
1902 }
1903
1904 static int enable_machine(int argc, char *argv[], void *userdata) {
1905 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
1906 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1907 UnitFileChange *changes = NULL;
1908 unsigned n_changes = 0;
1909 int carries_install_info = 0;
1910 const char *method = NULL;
1911 sd_bus *bus = userdata;
1912 int r, i;
1913
1914 assert(bus);
1915
1916 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1917
1918 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1919
1920 r = sd_bus_message_new_method_call(
1921 bus,
1922 &m,
1923 "org.freedesktop.systemd1",
1924 "/org/freedesktop/systemd1",
1925 "org.freedesktop.systemd1.Manager",
1926 method);
1927 if (r < 0)
1928 return bus_log_create_error(r);
1929
1930 r = sd_bus_message_open_container(m, 'a', "s");
1931 if (r < 0)
1932 return bus_log_create_error(r);
1933
1934 for (i = 1; i < argc; i++) {
1935 _cleanup_free_ char *unit = NULL;
1936
1937 r = make_service_name(argv[i], &unit);
1938 if (r < 0)
1939 return r;
1940
1941 r = image_exists(bus, argv[i]);
1942 if (r < 0)
1943 return r;
1944 if (r == 0) {
1945 log_error("Machine image '%s' does not exist.", argv[1]);
1946 return -ENXIO;
1947 }
1948
1949 r = sd_bus_message_append(m, "s", unit);
1950 if (r < 0)
1951 return bus_log_create_error(r);
1952 }
1953
1954 r = sd_bus_message_close_container(m);
1955 if (r < 0)
1956 return bus_log_create_error(r);
1957
1958 if (streq(argv[0], "enable"))
1959 r = sd_bus_message_append(m, "bb", false, false);
1960 else
1961 r = sd_bus_message_append(m, "b", false);
1962 if (r < 0)
1963 return bus_log_create_error(r);
1964
1965 r = sd_bus_call(bus, m, 0, &error, &reply);
1966 if (r < 0) {
1967 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1968 return r;
1969 }
1970
1971 if (streq(argv[0], "enable")) {
1972 r = sd_bus_message_read(reply, "b", carries_install_info);
1973 if (r < 0)
1974 return bus_log_parse_error(r);
1975 }
1976
1977 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes);
1978 if (r < 0)
1979 goto finish;
1980
1981 r = sd_bus_call_method(
1982 bus,
1983 "org.freedesktop.systemd1",
1984 "/org/freedesktop/systemd1",
1985 "org.freedesktop.systemd1.Manager",
1986 "Reload",
1987 &error,
1988 NULL,
1989 NULL);
1990 if (r < 0) {
1991 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1992 goto finish;
1993 }
1994
1995 r = 0;
1996
1997 finish:
1998 unit_file_changes_free(changes, n_changes);
1999
2000 return r;
2001 }
2002
2003 static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) {
2004 const char **our_path = userdata, *line;
2005 unsigned priority;
2006 int r;
2007
2008 assert(m);
2009 assert(our_path);
2010
2011 r = sd_bus_message_read(m, "us", &priority, &line);
2012 if (r < 0) {
2013 bus_log_parse_error(r);
2014 return 0;
2015 }
2016
2017 if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
2018 return 0;
2019
2020 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
2021 return 0;
2022
2023 log_full(priority, "%s", line);
2024 return 0;
2025 }
2026
2027 static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
2028 const char **our_path = userdata, *path, *result;
2029 uint32_t id;
2030 int r;
2031
2032 assert(m);
2033 assert(our_path);
2034
2035 r = sd_bus_message_read(m, "uos", &id, &path, &result);
2036 if (r < 0) {
2037 bus_log_parse_error(r);
2038 return 0;
2039 }
2040
2041 if (!streq_ptr(*our_path, path))
2042 return 0;
2043
2044 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done"));
2045 return 0;
2046 }
2047
2048 static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
2049 assert(s);
2050 assert(si);
2051
2052 if (!arg_quiet)
2053 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
2054
2055 sd_event_exit(sd_event_source_get_event(s), EINTR);
2056 return 0;
2057 }
2058
2059 static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
2060 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
2061 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2062 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2063 _cleanup_(sd_event_unrefp) sd_event* event = NULL;
2064 const char *path = NULL;
2065 uint32_t id;
2066 int r;
2067
2068 assert(bus);
2069 assert(m);
2070
2071 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2072
2073 r = sd_event_default(&event);
2074 if (r < 0)
2075 return log_error_errno(r, "Failed to get event loop: %m");
2076
2077 r = sd_bus_attach_event(bus, event, 0);
2078 if (r < 0)
2079 return log_error_errno(r, "Failed to attach bus to event loop: %m");
2080
2081 r = sd_bus_match_signal_async(
2082 bus,
2083 &slot_job_removed,
2084 "org.freedesktop.import1",
2085 "/org/freedesktop/import1",
2086 "org.freedesktop.import1.Manager",
2087 "TransferRemoved",
2088 match_transfer_removed, NULL, &path);
2089 if (r < 0)
2090 return log_error_errno(r, "Failed to request match: %m");
2091
2092 r = sd_bus_match_signal_async(
2093 bus,
2094 &slot_log_message,
2095 "org.freedesktop.import1",
2096 NULL,
2097 "org.freedesktop.import1.Transfer",
2098 "LogMessage",
2099 match_log_message, NULL, &path);
2100 if (r < 0)
2101 return log_error_errno(r, "Failed to request match: %m");
2102
2103 r = sd_bus_call(bus, m, 0, &error, &reply);
2104 if (r < 0) {
2105 log_error("Failed to transfer image: %s", bus_error_message(&error, -r));
2106 return r;
2107 }
2108
2109 r = sd_bus_message_read(reply, "uo", &id, &path);
2110 if (r < 0)
2111 return bus_log_parse_error(r);
2112
2113 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
2114
2115 if (!arg_quiet)
2116 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
2117
2118 sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
2119 sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
2120
2121 r = sd_event_loop(event);
2122 if (r < 0)
2123 return log_error_errno(r, "Failed to run event loop: %m");
2124
2125 return -r;
2126 }
2127
2128 static int import_tar(int argc, char *argv[], void *userdata) {
2129 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2130 _cleanup_free_ char *ll = NULL;
2131 _cleanup_close_ int fd = -1;
2132 const char *local = NULL, *path = NULL;
2133 sd_bus *bus = userdata;
2134 int r;
2135
2136 assert(bus);
2137
2138 if (argc >= 2)
2139 path = argv[1];
2140 if (isempty(path) || streq(path, "-"))
2141 path = NULL;
2142
2143 if (argc >= 3)
2144 local = argv[2];
2145 else if (path)
2146 local = basename(path);
2147 if (isempty(local) || streq(local, "-"))
2148 local = NULL;
2149
2150 if (!local) {
2151 log_error("Need either path or local name.");
2152 return -EINVAL;
2153 }
2154
2155 r = tar_strip_suffixes(local, &ll);
2156 if (r < 0)
2157 return log_oom();
2158
2159 local = ll;
2160
2161 if (!machine_name_is_valid(local)) {
2162 log_error("Local name %s is not a suitable machine name.", local);
2163 return -EINVAL;
2164 }
2165
2166 if (path) {
2167 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
2168 if (fd < 0)
2169 return log_error_errno(errno, "Failed to open %s: %m", path);
2170 }
2171
2172 r = sd_bus_message_new_method_call(
2173 bus,
2174 &m,
2175 "org.freedesktop.import1",
2176 "/org/freedesktop/import1",
2177 "org.freedesktop.import1.Manager",
2178 "ImportTar");
2179 if (r < 0)
2180 return bus_log_create_error(r);
2181
2182 r = sd_bus_message_append(
2183 m,
2184 "hsbb",
2185 fd >= 0 ? fd : STDIN_FILENO,
2186 local,
2187 arg_force,
2188 arg_read_only);
2189 if (r < 0)
2190 return bus_log_create_error(r);
2191
2192 return transfer_image_common(bus, m);
2193 }
2194
2195 static int import_raw(int argc, char *argv[], void *userdata) {
2196 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2197 _cleanup_free_ char *ll = NULL;
2198 _cleanup_close_ int fd = -1;
2199 const char *local = NULL, *path = NULL;
2200 sd_bus *bus = userdata;
2201 int r;
2202
2203 assert(bus);
2204
2205 if (argc >= 2)
2206 path = argv[1];
2207 if (isempty(path) || streq(path, "-"))
2208 path = NULL;
2209
2210 if (argc >= 3)
2211 local = argv[2];
2212 else if (path)
2213 local = basename(path);
2214 if (isempty(local) || streq(local, "-"))
2215 local = NULL;
2216
2217 if (!local) {
2218 log_error("Need either path or local name.");
2219 return -EINVAL;
2220 }
2221
2222 r = raw_strip_suffixes(local, &ll);
2223 if (r < 0)
2224 return log_oom();
2225
2226 local = ll;
2227
2228 if (!machine_name_is_valid(local)) {
2229 log_error("Local name %s is not a suitable machine name.", local);
2230 return -EINVAL;
2231 }
2232
2233 if (path) {
2234 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
2235 if (fd < 0)
2236 return log_error_errno(errno, "Failed to open %s: %m", path);
2237 }
2238
2239 r = sd_bus_message_new_method_call(
2240 bus,
2241 &m,
2242 "org.freedesktop.import1",
2243 "/org/freedesktop/import1",
2244 "org.freedesktop.import1.Manager",
2245 "ImportRaw");
2246 if (r < 0)
2247 return bus_log_create_error(r);
2248
2249 r = sd_bus_message_append(
2250 m,
2251 "hsbb",
2252 fd >= 0 ? fd : STDIN_FILENO,
2253 local,
2254 arg_force,
2255 arg_read_only);
2256 if (r < 0)
2257 return bus_log_create_error(r);
2258
2259 return transfer_image_common(bus, m);
2260 }
2261
2262 static void determine_compression_from_filename(const char *p) {
2263 if (arg_format)
2264 return;
2265
2266 if (!p)
2267 return;
2268
2269 if (endswith(p, ".xz"))
2270 arg_format = "xz";
2271 else if (endswith(p, ".gz"))
2272 arg_format = "gzip";
2273 else if (endswith(p, ".bz2"))
2274 arg_format = "bzip2";
2275 }
2276
2277 static int export_tar(int argc, char *argv[], void *userdata) {
2278 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2279 _cleanup_close_ int fd = -1;
2280 const char *local = NULL, *path = NULL;
2281 sd_bus *bus = userdata;
2282 int r;
2283
2284 assert(bus);
2285
2286 local = argv[1];
2287 if (!machine_name_is_valid(local)) {
2288 log_error("Machine name %s is not valid.", local);
2289 return -EINVAL;
2290 }
2291
2292 if (argc >= 3)
2293 path = argv[2];
2294 if (isempty(path) || streq(path, "-"))
2295 path = NULL;
2296
2297 if (path) {
2298 determine_compression_from_filename(path);
2299
2300 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
2301 if (fd < 0)
2302 return log_error_errno(errno, "Failed to open %s: %m", path);
2303 }
2304
2305 r = sd_bus_message_new_method_call(
2306 bus,
2307 &m,
2308 "org.freedesktop.import1",
2309 "/org/freedesktop/import1",
2310 "org.freedesktop.import1.Manager",
2311 "ExportTar");
2312 if (r < 0)
2313 return bus_log_create_error(r);
2314
2315 r = sd_bus_message_append(
2316 m,
2317 "shs",
2318 local,
2319 fd >= 0 ? fd : STDOUT_FILENO,
2320 arg_format);
2321 if (r < 0)
2322 return bus_log_create_error(r);
2323
2324 return transfer_image_common(bus, m);
2325 }
2326
2327 static int export_raw(int argc, char *argv[], void *userdata) {
2328 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2329 _cleanup_close_ int fd = -1;
2330 const char *local = NULL, *path = NULL;
2331 sd_bus *bus = userdata;
2332 int r;
2333
2334 assert(bus);
2335
2336 local = argv[1];
2337 if (!machine_name_is_valid(local)) {
2338 log_error("Machine name %s is not valid.", local);
2339 return -EINVAL;
2340 }
2341
2342 if (argc >= 3)
2343 path = argv[2];
2344 if (isempty(path) || streq(path, "-"))
2345 path = NULL;
2346
2347 if (path) {
2348 determine_compression_from_filename(path);
2349
2350 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
2351 if (fd < 0)
2352 return log_error_errno(errno, "Failed to open %s: %m", path);
2353 }
2354
2355 r = sd_bus_message_new_method_call(
2356 bus,
2357 &m,
2358 "org.freedesktop.import1",
2359 "/org/freedesktop/import1",
2360 "org.freedesktop.import1.Manager",
2361 "ExportRaw");
2362 if (r < 0)
2363 return bus_log_create_error(r);
2364
2365 r = sd_bus_message_append(
2366 m,
2367 "shs",
2368 local,
2369 fd >= 0 ? fd : STDOUT_FILENO,
2370 arg_format);
2371 if (r < 0)
2372 return bus_log_create_error(r);
2373
2374 return transfer_image_common(bus, m);
2375 }
2376
2377 static int pull_tar(int argc, char *argv[], void *userdata) {
2378 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2379 _cleanup_free_ char *l = NULL, *ll = NULL;
2380 const char *local, *remote;
2381 sd_bus *bus = userdata;
2382 int r;
2383
2384 assert(bus);
2385
2386 remote = argv[1];
2387 if (!http_url_is_valid(remote)) {
2388 log_error("URL '%s' is not valid.", remote);
2389 return -EINVAL;
2390 }
2391
2392 if (argc >= 3)
2393 local = argv[2];
2394 else {
2395 r = import_url_last_component(remote, &l);
2396 if (r < 0)
2397 return log_error_errno(r, "Failed to get final component of URL: %m");
2398
2399 local = l;
2400 }
2401
2402 if (isempty(local) || streq(local, "-"))
2403 local = NULL;
2404
2405 if (local) {
2406 r = tar_strip_suffixes(local, &ll);
2407 if (r < 0)
2408 return log_oom();
2409
2410 local = ll;
2411
2412 if (!machine_name_is_valid(local)) {
2413 log_error("Local name %s is not a suitable machine name.", local);
2414 return -EINVAL;
2415 }
2416 }
2417
2418 r = sd_bus_message_new_method_call(
2419 bus,
2420 &m,
2421 "org.freedesktop.import1",
2422 "/org/freedesktop/import1",
2423 "org.freedesktop.import1.Manager",
2424 "PullTar");
2425 if (r < 0)
2426 return bus_log_create_error(r);
2427
2428 r = sd_bus_message_append(
2429 m,
2430 "sssb",
2431 remote,
2432 local,
2433 import_verify_to_string(arg_verify),
2434 arg_force);
2435 if (r < 0)
2436 return bus_log_create_error(r);
2437
2438 return transfer_image_common(bus, m);
2439 }
2440
2441 static int pull_raw(int argc, char *argv[], void *userdata) {
2442 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2443 _cleanup_free_ char *l = NULL, *ll = NULL;
2444 const char *local, *remote;
2445 sd_bus *bus = userdata;
2446 int r;
2447
2448 assert(bus);
2449
2450 remote = argv[1];
2451 if (!http_url_is_valid(remote)) {
2452 log_error("URL '%s' is not valid.", remote);
2453 return -EINVAL;
2454 }
2455
2456 if (argc >= 3)
2457 local = argv[2];
2458 else {
2459 r = import_url_last_component(remote, &l);
2460 if (r < 0)
2461 return log_error_errno(r, "Failed to get final component of URL: %m");
2462
2463 local = l;
2464 }
2465
2466 if (isempty(local) || streq(local, "-"))
2467 local = NULL;
2468
2469 if (local) {
2470 r = raw_strip_suffixes(local, &ll);
2471 if (r < 0)
2472 return log_oom();
2473
2474 local = ll;
2475
2476 if (!machine_name_is_valid(local)) {
2477 log_error("Local name %s is not a suitable machine name.", local);
2478 return -EINVAL;
2479 }
2480 }
2481
2482 r = sd_bus_message_new_method_call(
2483 bus,
2484 &m,
2485 "org.freedesktop.import1",
2486 "/org/freedesktop/import1",
2487 "org.freedesktop.import1.Manager",
2488 "PullRaw");
2489 if (r < 0)
2490 return bus_log_create_error(r);
2491
2492 r = sd_bus_message_append(
2493 m,
2494 "sssb",
2495 remote,
2496 local,
2497 import_verify_to_string(arg_verify),
2498 arg_force);
2499 if (r < 0)
2500 return bus_log_create_error(r);
2501
2502 return transfer_image_common(bus, m);
2503 }
2504
2505 typedef struct TransferInfo {
2506 uint32_t id;
2507 const char *type;
2508 const char *remote;
2509 const char *local;
2510 double progress;
2511 } TransferInfo;
2512
2513 static int compare_transfer_info(const void *a, const void *b) {
2514 const TransferInfo *x = a, *y = b;
2515
2516 return strcmp(x->local, y->local);
2517 }
2518
2519 static int list_transfers(int argc, char *argv[], void *userdata) {
2520 size_t max_type = STRLEN("TYPE"), max_local = STRLEN("LOCAL"), max_remote = STRLEN("REMOTE");
2521 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2522 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2523 _cleanup_free_ TransferInfo *transfers = NULL;
2524 size_t n_transfers = 0, n_allocated = 0, j;
2525 const char *type, *remote, *local, *object;
2526 sd_bus *bus = userdata;
2527 uint32_t id, max_id = 0;
2528 double progress;
2529 int r;
2530
2531 (void) pager_open(arg_no_pager, false);
2532
2533 r = sd_bus_call_method(bus,
2534 "org.freedesktop.import1",
2535 "/org/freedesktop/import1",
2536 "org.freedesktop.import1.Manager",
2537 "ListTransfers",
2538 &error,
2539 &reply,
2540 NULL);
2541 if (r < 0) {
2542 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2543 return r;
2544 }
2545
2546 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
2547 if (r < 0)
2548 return bus_log_parse_error(r);
2549
2550 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
2551 size_t l;
2552
2553 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2554 return log_oom();
2555
2556 transfers[n_transfers].id = id;
2557 transfers[n_transfers].type = type;
2558 transfers[n_transfers].remote = remote;
2559 transfers[n_transfers].local = local;
2560 transfers[n_transfers].progress = progress;
2561
2562 l = strlen(type);
2563 if (l > max_type)
2564 max_type = l;
2565
2566 l = strlen(remote);
2567 if (l > max_remote)
2568 max_remote = l;
2569
2570 l = strlen(local);
2571 if (l > max_local)
2572 max_local = l;
2573
2574 if (id > max_id)
2575 max_id = id;
2576
2577 n_transfers++;
2578 }
2579 if (r < 0)
2580 return bus_log_parse_error(r);
2581
2582 r = sd_bus_message_exit_container(reply);
2583 if (r < 0)
2584 return bus_log_parse_error(r);
2585
2586 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2587
2588 if (arg_legend && n_transfers > 0)
2589 printf("%-*s %-*s %-*s %-*s %-*s\n",
2590 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
2591 (int) 7, "PERCENT",
2592 (int) max_type, "TYPE",
2593 (int) max_local, "LOCAL",
2594 (int) max_remote, "REMOTE");
2595
2596 for (j = 0; j < n_transfers; j++)
2597 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
2598 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
2599 (int) 6, (unsigned) (transfers[j].progress * 100),
2600 (int) max_type, transfers[j].type,
2601 (int) max_local, transfers[j].local,
2602 (int) max_remote, transfers[j].remote);
2603
2604 if (arg_legend) {
2605 if (n_transfers > 0)
2606 printf("\n%zu transfers listed.\n", n_transfers);
2607 else
2608 printf("No transfers.\n");
2609 }
2610
2611 return 0;
2612 }
2613
2614 static int cancel_transfer(int argc, char *argv[], void *userdata) {
2615 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2616 sd_bus *bus = userdata;
2617 int r, i;
2618
2619 assert(bus);
2620
2621 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2622
2623 for (i = 1; i < argc; i++) {
2624 uint32_t id;
2625
2626 r = safe_atou32(argv[i], &id);
2627 if (r < 0)
2628 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2629
2630 r = sd_bus_call_method(
2631 bus,
2632 "org.freedesktop.import1",
2633 "/org/freedesktop/import1",
2634 "org.freedesktop.import1.Manager",
2635 "CancelTransfer",
2636 &error,
2637 NULL,
2638 "u", id);
2639 if (r < 0) {
2640 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2641 return r;
2642 }
2643 }
2644
2645 return 0;
2646 }
2647
2648 static int set_limit(int argc, char *argv[], void *userdata) {
2649 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2650 sd_bus *bus = userdata;
2651 uint64_t limit;
2652 int r;
2653
2654 if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
2655 limit = (uint64_t) -1;
2656 else {
2657 r = parse_size(argv[argc-1], 1024, &limit);
2658 if (r < 0)
2659 return log_error("Failed to parse size: %s", argv[argc-1]);
2660 }
2661
2662 if (argc > 2)
2663 /* With two arguments changes the quota limit of the
2664 * specified image */
2665 r = sd_bus_call_method(
2666 bus,
2667 "org.freedesktop.machine1",
2668 "/org/freedesktop/machine1",
2669 "org.freedesktop.machine1.Manager",
2670 "SetImageLimit",
2671 &error,
2672 NULL,
2673 "st", argv[1], limit);
2674 else
2675 /* With one argument changes the pool quota limit */
2676 r = sd_bus_call_method(
2677 bus,
2678 "org.freedesktop.machine1",
2679 "/org/freedesktop/machine1",
2680 "org.freedesktop.machine1.Manager",
2681 "SetPoolLimit",
2682 &error,
2683 NULL,
2684 "t", limit);
2685
2686 if (r < 0) {
2687 log_error("Could not set limit: %s", bus_error_message(&error, -r));
2688 return r;
2689 }
2690
2691 return 0;
2692 }
2693
2694 static int clean_images(int argc, char *argv[], void *userdata) {
2695 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
2696 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2697 uint64_t usage, total = 0;
2698 char fb[FORMAT_BYTES_MAX];
2699 sd_bus *bus = userdata;
2700 const char *name;
2701 unsigned c = 0;
2702 int r;
2703
2704 r = sd_bus_message_new_method_call(
2705 bus,
2706 &m,
2707 "org.freedesktop.machine1",
2708 "/org/freedesktop/machine1",
2709 "org.freedesktop.machine1.Manager",
2710 "CleanPool");
2711 if (r < 0)
2712 return bus_log_create_error(r);
2713
2714 r = sd_bus_message_append(m, "s", arg_all ? "all" : "hidden");
2715 if (r < 0)
2716 return bus_log_create_error(r);
2717
2718 /* This is a slow operation, hence permit a longer time for completion. */
2719 r = sd_bus_call(bus, m, USEC_INFINITY, &error, &reply);
2720 if (r < 0)
2721 return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r));
2722
2723 r = sd_bus_message_enter_container(reply, 'a', "(st)");
2724 if (r < 0)
2725 return bus_log_parse_error(r);
2726
2727 while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) {
2728 log_info("Removed image '%s'. Freed exclusive disk space: %s",
2729 name, format_bytes(fb, sizeof(fb), usage));
2730
2731 total += usage;
2732 c++;
2733 }
2734
2735 r = sd_bus_message_exit_container(reply);
2736 if (r < 0)
2737 return bus_log_parse_error(r);
2738
2739 log_info("Removed %u images in total. Total freed exclusive disk space %s.",
2740 c, format_bytes(fb, sizeof(fb), total));
2741
2742 return 0;
2743 }
2744
2745 static int help(int argc, char *argv[], void *userdata) {
2746 (void) pager_open(arg_no_pager, false);
2747
2748 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2749 "Send control commands to or query the virtual machine and container\n"
2750 "registration manager.\n\n"
2751 " -h --help Show this help\n"
2752 " --version Show package version\n"
2753 " --no-pager Do not pipe output into a pager\n"
2754 " --no-legend Do not show the headers and footers\n"
2755 " --no-ask-password Do not ask for system passwords\n"
2756 " -H --host=[USER@]HOST Operate on remote host\n"
2757 " -M --machine=CONTAINER Operate on local container\n"
2758 " -p --property=NAME Show only properties by this name\n"
2759 " -q --quiet Suppress output\n"
2760 " -a --all Show all properties, including empty ones\n"
2761 " --value When showing properties, only print the value\n"
2762 " -l --full Do not ellipsize output\n"
2763 " --kill-who=WHO Who to send signal to\n"
2764 " -s --signal=SIGNAL Which signal to send\n"
2765 " --uid=USER Specify user ID to invoke shell as\n"
2766 " -E --setenv=VAR=VALUE Add an environment variable for shell\n"
2767 " --read-only Create read-only bind mount\n"
2768 " --mkdir Create directory before bind mounting, if missing\n"
2769 " -n --lines=INTEGER Number of journal entries to show\n"
2770 " --max-addresses=INTEGER Number of internet addresses to show at most\n"
2771 " -o --output=STRING Change journal output mode (short, short-precise,\n"
2772 " short-iso, short-iso-precise, short-full,\n"
2773 " short-monotonic, short-unix, verbose, export,\n"
2774 " json, json-pretty, json-sse, cat)\n"
2775 " --verify=MODE Verification mode for downloaded images (no,\n"
2776 " checksum, signature)\n"
2777 " --force Download image even if already exists\n\n"
2778 "Machine Commands:\n"
2779 " list List running VMs and containers\n"
2780 " status NAME... Show VM/container details\n"
2781 " show [NAME...] Show properties of one or more VMs/containers\n"
2782 " start NAME... Start container as a service\n"
2783 " login [NAME] Get a login prompt in a container or on the\n"
2784 " local host\n"
2785 " shell [[USER@]NAME [COMMAND...]]\n"
2786 " Invoke a shell (or other command) in a container\n"
2787 " or on the local host\n"
2788 " enable NAME... Enable automatic container start at boot\n"
2789 " disable NAME... Disable automatic container start at boot\n"
2790 " poweroff NAME... Power off one or more containers\n"
2791 " reboot NAME... Reboot one or more containers\n"
2792 " terminate NAME... Terminate one or more VMs/containers\n"
2793 " kill NAME... Send signal to processes of a VM/container\n"
2794 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2795 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2796 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2797 "Image Commands:\n"
2798 " list-images Show available container and VM images\n"
2799 " image-status [NAME...] Show image details\n"
2800 " show-image [NAME...] Show properties of image\n"
2801 " clone NAME NAME Clone an image\n"
2802 " rename NAME NAME Rename an image\n"
2803 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2804 " remove NAME... Remove an image\n"
2805 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
2806 " clean Remove hidden (or all) images\n\n"
2807 "Image Transfer Commands:\n"
2808 " pull-tar URL [NAME] Download a TAR container image\n"
2809 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2810 " import-tar FILE [NAME] Import a local TAR container image\n"
2811 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2812 " export-tar NAME [FILE] Export a TAR container image locally\n"
2813 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2814 " list-transfers Show list of downloads in progress\n"
2815 " cancel-transfer Cancel a download\n"
2816 , program_invocation_short_name);
2817
2818 return 0;
2819 }
2820
2821 static int parse_argv(int argc, char *argv[]) {
2822
2823 enum {
2824 ARG_VERSION = 0x100,
2825 ARG_NO_PAGER,
2826 ARG_NO_LEGEND,
2827 ARG_VALUE,
2828 ARG_KILL_WHO,
2829 ARG_READ_ONLY,
2830 ARG_MKDIR,
2831 ARG_NO_ASK_PASSWORD,
2832 ARG_VERIFY,
2833 ARG_FORCE,
2834 ARG_FORMAT,
2835 ARG_UID,
2836 ARG_NUMBER_IPS,
2837 };
2838
2839 static const struct option options[] = {
2840 { "help", no_argument, NULL, 'h' },
2841 { "version", no_argument, NULL, ARG_VERSION },
2842 { "property", required_argument, NULL, 'p' },
2843 { "all", no_argument, NULL, 'a' },
2844 { "value", no_argument, NULL, ARG_VALUE },
2845 { "full", no_argument, NULL, 'l' },
2846 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2847 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2848 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2849 { "signal", required_argument, NULL, 's' },
2850 { "host", required_argument, NULL, 'H' },
2851 { "machine", required_argument, NULL, 'M' },
2852 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2853 { "mkdir", no_argument, NULL, ARG_MKDIR },
2854 { "quiet", no_argument, NULL, 'q' },
2855 { "lines", required_argument, NULL, 'n' },
2856 { "output", required_argument, NULL, 'o' },
2857 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2858 { "verify", required_argument, NULL, ARG_VERIFY },
2859 { "force", no_argument, NULL, ARG_FORCE },
2860 { "format", required_argument, NULL, ARG_FORMAT },
2861 { "uid", required_argument, NULL, ARG_UID },
2862 { "setenv", required_argument, NULL, 'E' },
2863 { "max-addresses", required_argument, NULL, ARG_NUMBER_IPS },
2864 {}
2865 };
2866
2867 bool reorder = false;
2868 int c, r, shell = -1;
2869
2870 assert(argc >= 0);
2871 assert(argv);
2872
2873 for (;;) {
2874 static const char option_string[] = "-hp:als:H:M:qn:o:E:";
2875
2876 c = getopt_long(argc, argv, option_string + reorder, options, NULL);
2877 if (c < 0)
2878 break;
2879
2880 switch (c) {
2881
2882 case 1: /* getopt_long() returns 1 if "-" was the first character of the option string, and a
2883 * non-option argument was discovered. */
2884
2885 assert(!reorder);
2886
2887 /* We generally are fine with the fact that getopt_long() reorders the command line, and looks
2888 * for switches after the main verb. However, for "shell" we really don't want that, since we
2889 * want that switches specified after the machine name are passed to the program to execute,
2890 * and not processed by us. To make this possible, we'll first invoke getopt_long() with
2891 * reordering disabled (i.e. with the "-" prefix in the option string), looking for the first
2892 * non-option parameter. If it's the verb "shell" we remember its position and continue
2893 * processing options. In this case, as soon as we hit the next non-option argument we found
2894 * the machine name, and stop further processing. If the first non-option argument is any other
2895 * verb than "shell" we switch to normal reordering mode and continue processing arguments
2896 * normally. */
2897
2898 if (shell >= 0) {
2899 /* If we already found the "shell" verb on the command line, and now found the next
2900 * non-option argument, then this is the machine name and we should stop processing
2901 * further arguments. */
2902 optind --; /* don't process this argument, go one step back */
2903 goto done;
2904 }
2905 if (streq(optarg, "shell"))
2906 /* Remember the position of the "shell" verb, and continue processing normally. */
2907 shell = optind - 1;
2908 else {
2909 int saved_optind;
2910
2911 /* OK, this is some other verb. In this case, turn on reordering again, and continue
2912 * processing normally. */
2913 reorder = true;
2914
2915 /* We changed the option string. getopt_long() only looks at it again if we invoke it
2916 * at least once with a reset option index. Hence, let's reset the option index here,
2917 * then invoke getopt_long() again (ignoring what it has to say, after all we most
2918 * likely already processed it), and the bump the option index so that we read the
2919 * intended argument again. */
2920 saved_optind = optind;
2921 optind = 0;
2922 (void) getopt_long(argc, argv, option_string + reorder, options, NULL);
2923 optind = saved_optind - 1; /* go one step back, process this argument again */
2924 }
2925
2926 break;
2927
2928 case 'h':
2929 return help(0, NULL, NULL);
2930
2931 case ARG_VERSION:
2932 return version();
2933
2934 case 'p':
2935 r = strv_extend(&arg_property, optarg);
2936 if (r < 0)
2937 return log_oom();
2938
2939 /* If the user asked for a particular
2940 * property, show it to him, even if it is
2941 * empty. */
2942 arg_all = true;
2943 break;
2944
2945 case 'a':
2946 arg_all = true;
2947 break;
2948
2949 case ARG_VALUE:
2950 arg_value = true;
2951 break;
2952
2953 case 'l':
2954 arg_full = true;
2955 break;
2956
2957 case 'n':
2958 if (safe_atou(optarg, &arg_lines) < 0) {
2959 log_error("Failed to parse lines '%s'", optarg);
2960 return -EINVAL;
2961 }
2962 break;
2963
2964 case 'o':
2965 arg_output = output_mode_from_string(optarg);
2966 if (arg_output < 0) {
2967 log_error("Unknown output '%s'.", optarg);
2968 return -EINVAL;
2969 }
2970 break;
2971
2972 case ARG_NO_PAGER:
2973 arg_no_pager = true;
2974 break;
2975
2976 case ARG_NO_LEGEND:
2977 arg_legend = false;
2978 break;
2979
2980 case ARG_KILL_WHO:
2981 arg_kill_who = optarg;
2982 break;
2983
2984 case 's':
2985 arg_signal = signal_from_string_try_harder(optarg);
2986 if (arg_signal < 0) {
2987 log_error("Failed to parse signal string %s.", optarg);
2988 return -EINVAL;
2989 }
2990 break;
2991
2992 case ARG_NO_ASK_PASSWORD:
2993 arg_ask_password = false;
2994 break;
2995
2996 case 'H':
2997 arg_transport = BUS_TRANSPORT_REMOTE;
2998 arg_host = optarg;
2999 break;
3000
3001 case 'M':
3002 arg_transport = BUS_TRANSPORT_MACHINE;
3003 arg_host = optarg;
3004 break;
3005
3006 case ARG_READ_ONLY:
3007 arg_read_only = true;
3008 break;
3009
3010 case ARG_MKDIR:
3011 arg_mkdir = true;
3012 break;
3013
3014 case 'q':
3015 arg_quiet = true;
3016 break;
3017
3018 case ARG_VERIFY:
3019 arg_verify = import_verify_from_string(optarg);
3020 if (arg_verify < 0) {
3021 log_error("Failed to parse --verify= setting: %s", optarg);
3022 return -EINVAL;
3023 }
3024 break;
3025
3026 case ARG_FORCE:
3027 arg_force = true;
3028 break;
3029
3030 case ARG_FORMAT:
3031 if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
3032 log_error("Unknown format: %s", optarg);
3033 return -EINVAL;
3034 }
3035
3036 arg_format = optarg;
3037 break;
3038
3039 case ARG_UID:
3040 arg_uid = optarg;
3041 break;
3042
3043 case 'E':
3044 if (!env_assignment_is_valid(optarg)) {
3045 log_error("Environment assignment invalid: %s", optarg);
3046 return -EINVAL;
3047 }
3048
3049 r = strv_extend(&arg_setenv, optarg);
3050 if (r < 0)
3051 return log_oom();
3052 break;
3053
3054 case ARG_NUMBER_IPS:
3055 if (streq(optarg, "all"))
3056 arg_addrs = ALL_IP_ADDRESSES;
3057 else if (safe_atoi(optarg, &arg_addrs) < 0) {
3058 log_error("Invalid number of IPs");
3059 return -EINVAL;
3060 } else if (arg_addrs < 0) {
3061 log_error("Number of IPs cannot be negative");
3062 return -EINVAL;
3063 }
3064 break;
3065
3066 case '?':
3067 return -EINVAL;
3068
3069 default:
3070 assert_not_reached("Unhandled option");
3071 }
3072 }
3073
3074 done:
3075 if (shell >= 0) {
3076 char *t;
3077 int i;
3078
3079 /* We found the "shell" verb while processing the argument list. Since we turned off reordering of the
3080 * argument list initially let's readjust it now, and move the "shell" verb to the back. */
3081
3082 optind -= 1; /* place the option index where the "shell" verb will be placed */
3083
3084 t = argv[shell];
3085 for (i = shell; i < optind; i++)
3086 argv[i] = argv[i+1];
3087 argv[optind] = t;
3088 }
3089
3090 return 1;
3091 }
3092
3093 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
3094
3095 static const Verb verbs[] = {
3096 { "help", VERB_ANY, VERB_ANY, 0, help },
3097 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
3098 { "list-images", VERB_ANY, 1, 0, list_images },
3099 { "status", 2, VERB_ANY, 0, show_machine },
3100 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
3101 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
3102 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
3103 { "terminate", 2, VERB_ANY, 0, terminate_machine },
3104 { "reboot", 2, VERB_ANY, 0, reboot_machine },
3105 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
3106 { "stop", 2, VERB_ANY, 0, poweroff_machine }, /* Convenience alias */
3107 { "kill", 2, VERB_ANY, 0, kill_machine },
3108 { "login", VERB_ANY, 2, 0, login_machine },
3109 { "shell", VERB_ANY, VERB_ANY, 0, shell_machine },
3110 { "bind", 3, 4, 0, bind_mount },
3111 { "copy-to", 3, 4, 0, copy_files },
3112 { "copy-from", 3, 4, 0, copy_files },
3113 { "remove", 2, VERB_ANY, 0, remove_image },
3114 { "rename", 3, 3, 0, rename_image },
3115 { "clone", 3, 3, 0, clone_image },
3116 { "read-only", 2, 3, 0, read_only_image },
3117 { "start", 2, VERB_ANY, 0, start_machine },
3118 { "enable", 2, VERB_ANY, 0, enable_machine },
3119 { "disable", 2, VERB_ANY, 0, enable_machine },
3120 { "import-tar", 2, 3, 0, import_tar },
3121 { "import-raw", 2, 3, 0, import_raw },
3122 { "export-tar", 2, 3, 0, export_tar },
3123 { "export-raw", 2, 3, 0, export_raw },
3124 { "pull-tar", 2, 3, 0, pull_tar },
3125 { "pull-raw", 2, 3, 0, pull_raw },
3126 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
3127 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
3128 { "set-limit", 2, 3, 0, set_limit },
3129 { "clean", VERB_ANY, 1, 0, clean_images },
3130 {}
3131 };
3132
3133 return dispatch_verb(argc, argv, verbs, bus);
3134 }
3135
3136 int main(int argc, char*argv[]) {
3137 sd_bus *bus = NULL;
3138 int r;
3139
3140 setlocale(LC_ALL, "");
3141 log_parse_environment();
3142 log_open();
3143 sigbus_install();
3144
3145 r = parse_argv(argc, argv);
3146 if (r <= 0)
3147 goto finish;
3148
3149 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
3150 if (r < 0) {
3151 log_error_errno(r, "Failed to create bus connection: %m");
3152 goto finish;
3153 }
3154
3155 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
3156
3157 r = machinectl_main(argc, argv, bus);
3158
3159 finish:
3160 /* make sure we terminate the bus connection first, and then close the
3161 * pager, see issue #3543 for the details. */
3162 sd_bus_flush_close_unref(bus);
3163 pager_close();
3164 polkit_agent_close();
3165
3166 strv_free(arg_property);
3167 strv_free(arg_setenv);
3168
3169 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
3170 }