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