]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machinectl.c
systemctl,loginctl: start polkit agent for all polkit enabled operations
[thirdparty/systemd.git] / src / machine / machinectl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/socket.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <getopt.h>
27 #include <pwd.h>
28 #include <locale.h>
29 #include <fcntl.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <net/if.h>
33 #include <sys/mount.h>
34 #include <libgen.h>
35
36 #include "sd-bus.h"
37 #include "log.h"
38 #include "util.h"
39 #include "macro.h"
40 #include "pager.h"
41 #include "spawn-polkit-agent.h"
42 #include "bus-util.h"
43 #include "bus-error.h"
44 #include "build.h"
45 #include "strv.h"
46 #include "unit-name.h"
47 #include "cgroup-show.h"
48 #include "logs-show.h"
49 #include "cgroup-util.h"
50 #include "ptyfwd.h"
51 #include "event-util.h"
52 #include "path-util.h"
53 #include "mkdir.h"
54 #include "copy.h"
55 #include "verbs.h"
56
57 static char **arg_property = NULL;
58 static bool arg_all = false;
59 static bool arg_full = false;
60 static bool arg_no_pager = false;
61 static bool arg_legend = true;
62 static const char *arg_kill_who = NULL;
63 static int arg_signal = SIGTERM;
64 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
65 static char *arg_host = NULL;
66 static bool arg_read_only = false;
67 static bool arg_mkdir = false;
68 static bool arg_quiet = false;
69 static bool arg_ask_password = true;
70 static unsigned arg_lines = 10;
71 static OutputMode arg_output = OUTPUT_SHORT;
72
73 static void pager_open_if_enabled(void) {
74
75 if (arg_no_pager)
76 return;
77
78 pager_open(false);
79 }
80
81 static void polkit_agent_open_if_enabled(void) {
82
83 /* Open the polkit agent as a child process if necessary */
84
85 if (!arg_ask_password)
86 return;
87
88 if (arg_transport != BUS_TRANSPORT_LOCAL)
89 return;
90
91 polkit_agent_open();
92 }
93
94 static OutputFlags get_output_flags(void) {
95 return
96 arg_all * OUTPUT_SHOW_ALL |
97 arg_full * OUTPUT_FULL_WIDTH |
98 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
99 on_tty() * OUTPUT_COLOR |
100 !arg_quiet * OUTPUT_WARN_CUTOFF;
101 }
102
103 typedef struct MachineInfo {
104 const char *name;
105 const char *class;
106 const char *service;
107 } MachineInfo;
108
109 static int compare_machine_info(const void *a, const void *b) {
110 const MachineInfo *x = a, *y = b;
111
112 return strcmp(x->name, y->name);
113 }
114
115 static int list_machines(int argc, char *argv[], void *userdata) {
116
117 size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
118 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
119 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
120 _cleanup_free_ MachineInfo *machines = NULL;
121 const char *name, *class, *service, *object;
122 size_t n_machines = 0, n_allocated = 0, j;
123 sd_bus *bus = userdata;
124 int r;
125
126 assert(bus);
127
128 pager_open_if_enabled();
129
130 r = sd_bus_call_method(
131 bus,
132 "org.freedesktop.machine1",
133 "/org/freedesktop/machine1",
134 "org.freedesktop.machine1.Manager",
135 "ListMachines",
136 &error,
137 &reply,
138 "");
139 if (r < 0) {
140 log_error("Could not get machines: %s", bus_error_message(&error, -r));
141 return r;
142 }
143
144 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
145 if (r < 0)
146 return bus_log_parse_error(r);
147
148 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
149 size_t l;
150
151 if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
152 return log_oom();
153
154 machines[n_machines].name = name;
155 machines[n_machines].class = class;
156 machines[n_machines].service = service;
157
158 l = strlen(name);
159 if (l > max_name)
160 max_name = l;
161
162 l = strlen(class);
163 if (l > max_class)
164 max_class = l;
165
166 l = strlen(service);
167 if (l > max_service)
168 max_service = l;
169
170 n_machines ++;
171 }
172 if (r < 0)
173 return bus_log_parse_error(r);
174
175 r = sd_bus_message_exit_container(reply);
176 if (r < 0)
177 return bus_log_parse_error(r);
178
179 qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
180
181 if (arg_legend)
182 printf("%-*s %-*s %-*s\n",
183 (int) max_name, "MACHINE",
184 (int) max_class, "CLASS",
185 (int) max_service, "SERVICE");
186
187 for (j = 0; j < n_machines; j++)
188 printf("%-*s %-*s %-*s\n",
189 (int) max_name, machines[j].name,
190 (int) max_class, machines[j].class,
191 (int) max_service, machines[j].service);
192
193 if (arg_legend)
194 printf("\n%zu machines listed.\n", n_machines);
195
196 return 0;
197 }
198
199 typedef struct ImageInfo {
200 const char *name;
201 const char *type;
202 bool read_only;
203 usec_t crtime;
204 usec_t mtime;
205 uint64_t size;
206 } ImageInfo;
207
208 static int compare_image_info(const void *a, const void *b) {
209 const ImageInfo *x = a, *y = b;
210
211 return strcmp(x->name, y->name);
212 }
213
214 static int list_images(int argc, char *argv[], void *userdata) {
215
216 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
217 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("SIZE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
218 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
219 _cleanup_free_ ImageInfo *images = NULL;
220 size_t n_images = 0, n_allocated = 0, j;
221 const char *name, *type, *object;
222 sd_bus *bus = userdata;
223 uint64_t crtime, mtime, size;
224 int read_only, r;
225
226 assert(bus);
227
228 pager_open_if_enabled();
229
230 r = sd_bus_call_method(
231 bus,
232 "org.freedesktop.machine1",
233 "/org/freedesktop/machine1",
234 "org.freedesktop.machine1.Manager",
235 "ListImages",
236 &error,
237 &reply,
238 "");
239 if (r < 0) {
240 log_error("Could not get images: %s", bus_error_message(&error, -r));
241 return r;
242 }
243
244 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
245 if (r < 0)
246 return bus_log_parse_error(r);
247
248 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
249 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
250 size_t l;
251
252 if (name[0] == '.' && !arg_all)
253 continue;
254
255 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
256 return log_oom();
257
258 images[n_images].name = name;
259 images[n_images].type = type;
260 images[n_images].read_only = read_only;
261 images[n_images].crtime = crtime;
262 images[n_images].mtime = mtime;
263 images[n_images].size = size;
264
265 l = strlen(name);
266 if (l > max_name)
267 max_name = l;
268
269 l = strlen(type);
270 if (l > max_type)
271 max_type = l;
272
273 if (crtime != 0) {
274 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
275 if (l > max_crtime)
276 max_crtime = l;
277 }
278
279 if (mtime != 0) {
280 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
281 if (l > max_mtime)
282 max_mtime = l;
283 }
284
285 if (size != (uint64_t) -1) {
286 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
287 if (l > max_size)
288 max_size = l;
289 }
290
291 n_images++;
292 }
293 if (r < 0)
294 return bus_log_parse_error(r);
295
296 r = sd_bus_message_exit_container(reply);
297 if (r < 0)
298 return bus_log_parse_error(r);
299
300 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
301
302 if (arg_legend)
303 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
304 (int) max_name, "NAME",
305 (int) max_type, "TYPE",
306 "RO",
307 (int) max_size, "SIZE",
308 (int) max_crtime, "CREATED",
309 (int) max_mtime, "MODIFIED");
310
311 for (j = 0; j < n_images; j++) {
312 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
313
314 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
315 (int) max_name, images[j].name,
316 (int) max_type, images[j].type,
317 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
318 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
319 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
320 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
321 }
322
323 if (arg_legend)
324 printf("\n%zu images listed.\n", n_images);
325
326 return 0;
327 }
328
329 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
330 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
331 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
332 _cleanup_free_ char *path = NULL;
333 const char *cgroup;
334 int r;
335 unsigned c;
336
337 assert(bus);
338 assert(unit);
339
340 if (arg_transport == BUS_TRANSPORT_REMOTE)
341 return 0;
342
343 path = unit_dbus_path_from_name(unit);
344 if (!path)
345 return log_oom();
346
347 r = sd_bus_get_property(
348 bus,
349 "org.freedesktop.systemd1",
350 path,
351 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
352 "ControlGroup",
353 &error,
354 &reply,
355 "s");
356 if (r < 0) {
357 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
358 return r;
359 }
360
361 r = sd_bus_message_read(reply, "s", &cgroup);
362 if (r < 0)
363 return bus_log_parse_error(r);
364
365 if (isempty(cgroup))
366 return 0;
367
368 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
369 return 0;
370
371 c = columns();
372 if (c > 18)
373 c -= 18;
374 else
375 c = 0;
376
377 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
378 return 0;
379 }
380
381 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
382 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
383 int r;
384
385 assert(bus);
386 assert(name);
387 assert(prefix);
388 assert(prefix2);
389
390 r = sd_bus_call_method(bus,
391 "org.freedesktop.machine1",
392 "/org/freedesktop/machine1",
393 "org.freedesktop.machine1.Manager",
394 "GetMachineAddresses",
395 NULL,
396 &reply,
397 "s", name);
398 if (r < 0)
399 return r;
400
401 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
402 if (r < 0)
403 return bus_log_parse_error(r);
404
405 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
406 int family;
407 const void *a;
408 size_t sz;
409 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
410
411 r = sd_bus_message_read(reply, "i", &family);
412 if (r < 0)
413 return bus_log_parse_error(r);
414
415 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
416 if (r < 0)
417 return bus_log_parse_error(r);
418
419 fputs(prefix, stdout);
420 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
421 if (family == AF_INET6 && ifi > 0)
422 printf("%%%i", ifi);
423 fputc('\n', stdout);
424
425 r = sd_bus_message_exit_container(reply);
426 if (r < 0)
427 return bus_log_parse_error(r);
428
429 if (prefix != prefix2)
430 prefix = prefix2;
431 }
432 if (r < 0)
433 return bus_log_parse_error(r);
434
435 r = sd_bus_message_exit_container(reply);
436 if (r < 0)
437 return bus_log_parse_error(r);
438
439 return 0;
440 }
441
442 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
443 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
444 const char *k, *v, *pretty = NULL;
445 int r;
446
447 assert(bus);
448 assert(name);
449 assert(prefix);
450
451 r = sd_bus_call_method(bus,
452 "org.freedesktop.machine1",
453 "/org/freedesktop/machine1",
454 "org.freedesktop.machine1.Manager",
455 "GetMachineOSRelease",
456 NULL,
457 &reply,
458 "s", name);
459 if (r < 0)
460 return r;
461
462 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
463 if (r < 0)
464 return bus_log_parse_error(r);
465
466 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
467 if (streq(k, "PRETTY_NAME"))
468 pretty = v;
469
470 }
471 if (r < 0)
472 return bus_log_parse_error(r);
473
474 r = sd_bus_message_exit_container(reply);
475 if (r < 0)
476 return bus_log_parse_error(r);
477
478 if (pretty)
479 printf("%s%s\n", prefix, pretty);
480
481 return 0;
482 }
483
484 typedef struct MachineStatusInfo {
485 char *name;
486 sd_id128_t id;
487 char *class;
488 char *service;
489 char *unit;
490 char *root_directory;
491 pid_t leader;
492 struct dual_timestamp timestamp;
493 int *netif;
494 unsigned n_netif;
495 } MachineStatusInfo;
496
497 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
498 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
499 char since2[FORMAT_TIMESTAMP_MAX], *s2;
500 int ifi = -1;
501
502 assert(bus);
503 assert(i);
504
505 fputs(strna(i->name), stdout);
506
507 if (!sd_id128_equal(i->id, SD_ID128_NULL))
508 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
509 else
510 putchar('\n');
511
512 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
513 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
514
515 if (s1)
516 printf("\t Since: %s; %s\n", s2, s1);
517 else if (s2)
518 printf("\t Since: %s\n", s2);
519
520 if (i->leader > 0) {
521 _cleanup_free_ char *t = NULL;
522
523 printf("\t Leader: %u", (unsigned) i->leader);
524
525 get_process_comm(i->leader, &t);
526 if (t)
527 printf(" (%s)", t);
528
529 putchar('\n');
530 }
531
532 if (i->service) {
533 printf("\t Service: %s", i->service);
534
535 if (i->class)
536 printf("; class %s", i->class);
537
538 putchar('\n');
539 } else if (i->class)
540 printf("\t Class: %s\n", i->class);
541
542 if (i->root_directory)
543 printf("\t Root: %s\n", i->root_directory);
544
545 if (i->n_netif > 0) {
546 unsigned c;
547
548 fputs("\t Iface:", stdout);
549
550 for (c = 0; c < i->n_netif; c++) {
551 char name[IF_NAMESIZE+1] = "";
552
553 if (if_indextoname(i->netif[c], name)) {
554 fputc(' ', stdout);
555 fputs(name, stdout);
556
557 if (ifi < 0)
558 ifi = i->netif[c];
559 else
560 ifi = 0;
561 } else
562 printf(" %i", i->netif[c]);
563 }
564
565 fputc('\n', stdout);
566 }
567
568 print_addresses(bus, i->name, ifi,
569 "\t Address: ",
570 "\t ");
571
572 print_os_release(bus, i->name, "\t OS: ");
573
574 if (i->unit) {
575 printf("\t Unit: %s\n", i->unit);
576 show_unit_cgroup(bus, i->unit, i->leader);
577
578 if (arg_transport == BUS_TRANSPORT_LOCAL) {
579
580 show_journal_by_unit(
581 stdout,
582 i->unit,
583 arg_output,
584 0,
585 i->timestamp.monotonic,
586 arg_lines,
587 0,
588 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
589 SD_JOURNAL_LOCAL_ONLY,
590 true,
591 NULL);
592 }
593 }
594 }
595
596 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
597 MachineStatusInfo *i = userdata;
598 size_t l;
599 const void *v;
600 int r;
601
602 assert_cc(sizeof(int32_t) == sizeof(int));
603 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
604 if (r < 0)
605 return r;
606 if (r == 0)
607 return -EBADMSG;
608
609 i->n_netif = l / sizeof(int32_t);
610 i->netif = memdup(v, l);
611 if (!i->netif)
612 return -ENOMEM;
613
614 return 0;
615 }
616
617 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
618
619 static const struct bus_properties_map map[] = {
620 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
621 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
622 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
623 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
624 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
625 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
626 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
627 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
628 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
629 { "NetworkInterfaces", "ai", map_netif, 0 },
630 {}
631 };
632
633 MachineStatusInfo info = {};
634 int r;
635
636 assert(verb);
637 assert(bus);
638 assert(path);
639 assert(new_line);
640
641 r = bus_map_all_properties(bus,
642 "org.freedesktop.machine1",
643 path,
644 map,
645 &info);
646 if (r < 0)
647 return log_error_errno(r, "Could not get properties: %m");
648
649 if (*new_line)
650 printf("\n");
651 *new_line = true;
652
653 print_machine_status_info(bus, &info);
654
655 free(info.name);
656 free(info.class);
657 free(info.service);
658 free(info.unit);
659 free(info.root_directory);
660 free(info.netif);
661
662 return r;
663 }
664
665 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
666 int r;
667
668 assert(bus);
669 assert(path);
670 assert(new_line);
671
672 if (*new_line)
673 printf("\n");
674
675 *new_line = true;
676
677 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
678 if (r < 0)
679 log_error_errno(r, "Could not get properties: %m");
680
681 return r;
682 }
683
684 static int show_machine(int argc, char *argv[], void *userdata) {
685
686 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
687 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
688 bool properties, new_line = false;
689 sd_bus *bus = userdata;
690 int r = 0, i;
691
692 assert(bus);
693
694 properties = !strstr(argv[0], "status");
695
696 pager_open_if_enabled();
697
698 if (properties && argc <= 1) {
699
700 /* If no argument is specified, inspect the manager
701 * itself */
702 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
703 if (r < 0)
704 return r;
705 }
706
707 for (i = 1; i < argc; i++) {
708 const char *path = NULL;
709
710 r = sd_bus_call_method(
711 bus,
712 "org.freedesktop.machine1",
713 "/org/freedesktop/machine1",
714 "org.freedesktop.machine1.Manager",
715 "GetMachine",
716 &error,
717 &reply,
718 "s", argv[i]);
719 if (r < 0) {
720 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
721 return r;
722 }
723
724 r = sd_bus_message_read(reply, "o", &path);
725 if (r < 0)
726 return bus_log_parse_error(r);
727
728 if (properties)
729 r = show_machine_properties(bus, path, &new_line);
730 else
731 r = show_machine_info(argv[0], bus, path, &new_line);
732 }
733
734 return r;
735 }
736
737 typedef struct ImageStatusInfo {
738 char *name;
739 char *path;
740 char *type;
741 int read_only;
742 usec_t crtime;
743 usec_t mtime;
744 uint64_t size;
745 uint64_t limit;
746 uint64_t size_exclusive;
747 uint64_t limit_exclusive;
748 } ImageStatusInfo;
749
750 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
751 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
752 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
753 char bs[FORMAT_BYTES_MAX], *s3;
754 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
755
756 assert(bus);
757 assert(i);
758
759 if (i->name) {
760 fputs(i->name, stdout);
761 putchar('\n');
762 }
763
764 if (i->type)
765 printf("\t Type: %s\n", i->type);
766
767 if (i->path)
768 printf("\t Path: %s\n", i->path);
769
770 printf("\t RO: %s%s%s\n",
771 i->read_only ? ansi_highlight_red() : "",
772 i->read_only ? "read-only" : "writable",
773 i->read_only ? ansi_highlight_off() : "");
774
775 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
776 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
777 if (s1 && s2)
778 printf("\t Created: %s; %s\n", s2, s1);
779 else if (s2)
780 printf("\t Created: %s\n", s2);
781
782 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
783 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
784 if (s1 && s2)
785 printf("\tModified: %s; %s\n", s2, s1);
786 else if (s2)
787 printf("\tModified: %s\n", s2);
788
789 s3 = format_bytes(bs, sizeof(bs), i->size);
790 s4 = i->size_exclusive != i->size ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->size_exclusive) : NULL;
791 if (s3 && s4)
792 printf("\t Size: %s (exclusive: %s)\n", s3, s4);
793 else if (s3)
794 printf("\t Size: %s\n", s3);
795
796 s3 = format_bytes(bs, sizeof(bs), i->limit);
797 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
798 if (s3 && s4)
799 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
800 else if (s3)
801 printf("\t Limit: %s\n", s3);
802 }
803
804 static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
805
806 static const struct bus_properties_map map[] = {
807 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
808 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
809 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
810 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
811 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
812 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
813 { "Size", "t", NULL, offsetof(ImageStatusInfo, size) },
814 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
815 { "SizeExclusive", "t", NULL, offsetof(ImageStatusInfo, size_exclusive) },
816 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
817 {}
818 };
819
820 ImageStatusInfo info = {};
821 int r;
822
823 assert(verb);
824 assert(bus);
825 assert(path);
826 assert(new_line);
827
828 r = bus_map_all_properties(bus,
829 "org.freedesktop.machine1",
830 path,
831 map,
832 &info);
833 if (r < 0)
834 return log_error_errno(r, "Could not get properties: %m");
835
836 if (*new_line)
837 printf("\n");
838 *new_line = true;
839
840 print_image_status_info(bus, &info);
841
842 free(info.name);
843 free(info.path);
844 free(info.type);
845
846 return r;
847 }
848
849 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
850 int r;
851
852 assert(bus);
853 assert(path);
854 assert(new_line);
855
856 if (*new_line)
857 printf("\n");
858
859 *new_line = true;
860
861 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
862 if (r < 0)
863 log_error_errno(r, "Could not get properties: %m");
864
865 return r;
866 }
867
868 static int show_image(int argc, char *argv[], void *userdata) {
869
870 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
871 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
872 bool properties, new_line = false;
873 sd_bus *bus = userdata;
874 int r = 0, i;
875
876 assert(bus);
877
878 properties = !strstr(argv[0], "status");
879
880 pager_open_if_enabled();
881
882 if (properties && argc <= 1) {
883
884 /* If no argument is specified, inspect the manager
885 * itself */
886 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
887 if (r < 0)
888 return r;
889 }
890
891 for (i = 1; i < argc; i++) {
892 const char *path = NULL;
893
894 r = sd_bus_call_method(
895 bus,
896 "org.freedesktop.machine1",
897 "/org/freedesktop/machine1",
898 "org.freedesktop.machine1.Manager",
899 "GetImage",
900 &error,
901 &reply,
902 "s", argv[i]);
903 if (r < 0) {
904 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
905 return r;
906 }
907
908 r = sd_bus_message_read(reply, "o", &path);
909 if (r < 0)
910 return bus_log_parse_error(r);
911
912 if (properties)
913 r = show_image_properties(bus, path, &new_line);
914 else
915 r = show_image_info(argv[0], bus, path, &new_line);
916 }
917
918 return r;
919 }
920
921 static int kill_machine(int argc, char *argv[], void *userdata) {
922 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
923 sd_bus *bus = userdata;
924 int i;
925
926 assert(bus);
927
928 polkit_agent_open_if_enabled();
929
930 if (!arg_kill_who)
931 arg_kill_who = "all";
932
933 for (i = 1; i < argc; i++) {
934 int r;
935
936 r = sd_bus_call_method(
937 bus,
938 "org.freedesktop.machine1",
939 "/org/freedesktop/machine1",
940 "org.freedesktop.machine1.Manager",
941 "KillMachine",
942 &error,
943 NULL,
944 "ssi", argv[i], arg_kill_who, arg_signal);
945 if (r < 0) {
946 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
947 return r;
948 }
949 }
950
951 return 0;
952 }
953
954 static int reboot_machine(int argc, char *argv[], void *userdata) {
955 arg_kill_who = "leader";
956 arg_signal = SIGINT; /* sysvinit + systemd */
957
958 return kill_machine(argc, argv, userdata);
959 }
960
961 static int poweroff_machine(int argc, char *argv[], void *userdata) {
962 arg_kill_who = "leader";
963 arg_signal = SIGRTMIN+4; /* only systemd */
964
965 return kill_machine(argc, argv, userdata);
966 }
967
968 static int terminate_machine(int argc, char *argv[], void *userdata) {
969 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
970 sd_bus *bus = userdata;
971 int i;
972
973 assert(bus);
974
975 polkit_agent_open_if_enabled();
976
977 for (i = 1; i < argc; i++) {
978 int r;
979
980 r = sd_bus_call_method(
981 bus,
982 "org.freedesktop.machine1",
983 "/org/freedesktop/machine1",
984 "org.freedesktop.machine1.Manager",
985 "TerminateMachine",
986 &error,
987 NULL,
988 "s", argv[i]);
989 if (r < 0) {
990 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
991 return r;
992 }
993 }
994
995 return 0;
996 }
997
998 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
999 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1000 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
1001 const char *object;
1002 uint32_t leader;
1003 int r;
1004
1005 assert(bus);
1006 assert(name);
1007 assert(ret);
1008
1009 r = sd_bus_call_method(
1010 bus,
1011 "org.freedesktop.machine1",
1012 "/org/freedesktop/machine1",
1013 "org.freedesktop.machine1.Manager",
1014 "GetMachine",
1015 &error,
1016 &reply,
1017 "s", name);
1018 if (r < 0) {
1019 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
1020 return r;
1021 }
1022
1023 r = sd_bus_message_read(reply, "o", &object);
1024 if (r < 0)
1025 return bus_log_parse_error(r);
1026
1027 r = sd_bus_get_property(
1028 bus,
1029 "org.freedesktop.machine1",
1030 object,
1031 "org.freedesktop.machine1.Machine",
1032 "Leader",
1033 &error,
1034 &reply2,
1035 "u");
1036 if (r < 0)
1037 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
1038
1039 r = sd_bus_message_read(reply2, "u", &leader);
1040 if (r < 0)
1041 return bus_log_parse_error(r);
1042
1043 *ret = leader;
1044 return 0;
1045 }
1046
1047 static int copy_files(int argc, char *argv[], void *userdata) {
1048 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
1049 _cleanup_close_ int hostfd = -1;
1050 sd_bus *bus = userdata;
1051 pid_t child, leader;
1052 bool copy_from;
1053 siginfo_t si;
1054 int r;
1055
1056 assert(bus);
1057
1058 copy_from = streq(argv[0], "copy-from");
1059 dest = argv[3] ?: argv[2];
1060 host_path = strdupa(copy_from ? dest : argv[2]);
1061 container_path = strdupa(copy_from ? argv[2] : dest);
1062
1063 if (!path_is_absolute(container_path)) {
1064 log_error("Container path not absolute.");
1065 return -EINVAL;
1066 }
1067
1068 t = strdup(host_path);
1069 host_basename = basename(t);
1070 host_dirname = dirname(host_path);
1071
1072 t = strdup(container_path);
1073 container_basename = basename(t);
1074 container_dirname = dirname(container_path);
1075
1076 r = machine_get_leader(bus, argv[1], &leader);
1077 if (r < 0)
1078 return r;
1079
1080 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1081 if (r < 0)
1082 return log_error_errno(errno, "Failed to open source directory: %m");
1083
1084 child = fork();
1085 if (child < 0)
1086 return log_error_errno(errno, "Failed to fork(): %m");
1087
1088 if (child == 0) {
1089 int containerfd;
1090 const char *q;
1091 int mntfd;
1092
1093 q = procfs_file_alloca(leader, "ns/mnt");
1094 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1095 if (mntfd < 0) {
1096 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1097 _exit(EXIT_FAILURE);
1098 }
1099
1100 if (setns(mntfd, CLONE_NEWNS) < 0) {
1101 log_error_errno(errno, "Failed to join namespace of leader: %m");
1102 _exit(EXIT_FAILURE);
1103 }
1104
1105 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1106 if (containerfd < 0) {
1107 log_error_errno(errno, "Failed top open destination directory: %m");
1108 _exit(EXIT_FAILURE);
1109 }
1110
1111 if (copy_from)
1112 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
1113 else
1114 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
1115 if (r < 0) {
1116 log_error_errno(errno, "Failed to copy tree: %m");
1117 _exit(EXIT_FAILURE);
1118 }
1119
1120 _exit(EXIT_SUCCESS);
1121 }
1122
1123 r = wait_for_terminate(child, &si);
1124 if (r < 0)
1125 return log_error_errno(r, "Failed to wait for client: %m");
1126 if (si.si_code != CLD_EXITED) {
1127 log_error("Client died abnormally.");
1128 return -EIO;
1129 }
1130 if (si.si_status != EXIT_SUCCESS)
1131 return -EIO;
1132
1133 return 0;
1134 }
1135
1136 static int bind_mount(int argc, char *argv[], void *userdata) {
1137 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
1138 sd_bus *bus = userdata;
1139 pid_t child, leader;
1140 const char *dest;
1141 siginfo_t si;
1142 bool mount_slave_created = false, mount_slave_mounted = false,
1143 mount_tmp_created = false, mount_tmp_mounted = false,
1144 mount_outside_created = false, mount_outside_mounted = false;
1145 int r;
1146
1147 assert(bus);
1148
1149 /* One day, when bind mounting /proc/self/fd/n works across
1150 * namespace boundaries we should rework this logic to make
1151 * use of it... */
1152
1153 dest = argv[3] ?: argv[2];
1154 if (!path_is_absolute(dest)) {
1155 log_error("Destination path not absolute.");
1156 return -EINVAL;
1157 }
1158
1159 p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
1160 if (access(p, F_OK) < 0) {
1161 log_error("Container does not allow propagation of mount points.");
1162 return -ENOTSUP;
1163 }
1164
1165 r = machine_get_leader(bus, argv[1], &leader);
1166 if (r < 0)
1167 return r;
1168
1169 /* Our goal is to install a new bind mount into the container,
1170 possibly read-only. This is irritatingly complex
1171 unfortunately, currently.
1172
1173 First, we start by creating a private playground in /tmp,
1174 that we can mount MS_SLAVE. (Which is necessary, since
1175 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
1176 mounts.) */
1177
1178 if (!mkdtemp(mount_slave))
1179 return log_error_errno(errno, "Failed to create playground: %m");
1180
1181 mount_slave_created = true;
1182
1183 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
1184 r = log_error_errno(errno, "Failed to make bind mount: %m");
1185 goto finish;
1186 }
1187
1188 mount_slave_mounted = true;
1189
1190 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
1191 r = log_error_errno(errno, "Failed to remount slave: %m");
1192 goto finish;
1193 }
1194
1195 /* Second, we mount the source directory to a directory inside
1196 of our MS_SLAVE playground. */
1197 mount_tmp = strappenda(mount_slave, "/mount");
1198 if (mkdir(mount_tmp, 0700) < 0) {
1199 r = log_error_errno(errno, "Failed to create temporary mount: %m");
1200 goto finish;
1201 }
1202
1203 mount_tmp_created = true;
1204
1205 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
1206 r = log_error_errno(errno, "Failed to overmount: %m");
1207 goto finish;
1208 }
1209
1210 mount_tmp_mounted = true;
1211
1212 /* Third, we remount the new bind mount read-only if requested. */
1213 if (arg_read_only)
1214 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
1215 r = log_error_errno(errno, "Failed to mark read-only: %m");
1216 goto finish;
1217 }
1218
1219 /* Fourth, we move the new bind mount into the propagation
1220 * directory. This way it will appear there read-only
1221 * right-away. */
1222
1223 mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
1224 if (!mkdtemp(mount_outside)) {
1225 r = log_error_errno(errno, "Cannot create propagation directory: %m");
1226 goto finish;
1227 }
1228
1229 mount_outside_created = true;
1230
1231 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
1232 r = log_error_errno(errno, "Failed to move: %m");
1233 goto finish;
1234 }
1235
1236 mount_outside_mounted = true;
1237 mount_tmp_mounted = false;
1238
1239 (void) rmdir(mount_tmp);
1240 mount_tmp_created = false;
1241
1242 (void) umount(mount_slave);
1243 mount_slave_mounted = false;
1244
1245 (void) rmdir(mount_slave);
1246 mount_slave_created = false;
1247
1248 child = fork();
1249 if (child < 0) {
1250 r = log_error_errno(errno, "Failed to fork(): %m");
1251 goto finish;
1252 }
1253
1254 if (child == 0) {
1255 const char *mount_inside;
1256 int mntfd;
1257 const char *q;
1258
1259 q = procfs_file_alloca(leader, "ns/mnt");
1260 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1261 if (mntfd < 0) {
1262 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1263 _exit(EXIT_FAILURE);
1264 }
1265
1266 if (setns(mntfd, CLONE_NEWNS) < 0) {
1267 log_error_errno(errno, "Failed to join namespace of leader: %m");
1268 _exit(EXIT_FAILURE);
1269 }
1270
1271 if (arg_mkdir)
1272 mkdir_p(dest, 0755);
1273
1274 /* Fifth, move the mount to the right place inside */
1275 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
1276 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
1277 log_error_errno(errno, "Failed to mount: %m");
1278 _exit(EXIT_FAILURE);
1279 }
1280
1281 _exit(EXIT_SUCCESS);
1282 }
1283
1284 r = wait_for_terminate(child, &si);
1285 if (r < 0) {
1286 log_error_errno(r, "Failed to wait for client: %m");
1287 goto finish;
1288 }
1289 if (si.si_code != CLD_EXITED) {
1290 log_error("Client died abnormally.");
1291 r = -EIO;
1292 goto finish;
1293 }
1294 if (si.si_status != EXIT_SUCCESS) {
1295 r = -EIO;
1296 goto finish;
1297 }
1298
1299 r = 0;
1300
1301 finish:
1302 if (mount_outside_mounted)
1303 umount(mount_outside);
1304 if (mount_outside_created)
1305 rmdir(mount_outside);
1306
1307 if (mount_tmp_mounted)
1308 umount(mount_tmp);
1309 if (mount_tmp_created)
1310 umount(mount_tmp);
1311
1312 if (mount_slave_mounted)
1313 umount(mount_slave);
1314 if (mount_slave_created)
1315 umount(mount_slave);
1316
1317 return r;
1318 }
1319
1320 static int on_machine_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1321 PTYForward ** forward = (PTYForward**) userdata;
1322 int r;
1323
1324 assert(bus);
1325 assert(m);
1326 assert(forward);
1327
1328 if (*forward) {
1329 /* If the forwarder is already initialized, tell it to
1330 * exit on the next vhangup(), so that we still flush
1331 * out what might be queued and exit then. */
1332
1333 r = pty_forward_set_ignore_vhangup(*forward, false);
1334 if (r >= 0)
1335 return 0;
1336
1337 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
1338 }
1339
1340 /* On error, or when the forwarder is not initialized yet, quit immediately */
1341 sd_event_exit(sd_bus_get_event(bus), EXIT_FAILURE);
1342 return 0;
1343 }
1344
1345 static int login_machine(int argc, char *argv[], void *userdata) {
1346 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1347 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1348 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1349 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1350 _cleanup_event_unref_ sd_event *event = NULL;
1351 int master = -1, r, ret = 0;
1352 sd_bus *bus = userdata;
1353 const char *pty, *match;
1354 sigset_t mask;
1355 char last_char = 0;
1356 bool machine_died;
1357
1358 assert(bus);
1359
1360 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1361 arg_transport != BUS_TRANSPORT_MACHINE) {
1362 log_error("Login only supported on local machines.");
1363 return -ENOTSUP;
1364 }
1365
1366 polkit_agent_open_if_enabled();
1367
1368 r = sd_event_default(&event);
1369 if (r < 0)
1370 return log_error_errno(r, "Failed to get event loop: %m");
1371
1372 r = sd_bus_attach_event(bus, event, 0);
1373 if (r < 0)
1374 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1375
1376 match = strappenda("type='signal',"
1377 "sender='org.freedesktop.machine1',"
1378 "path='/org/freedesktop/machine1',",
1379 "interface='org.freedesktop.machine1.Manager',"
1380 "member='MachineRemoved',"
1381 "arg0='",
1382 argv[1],
1383 "'");
1384
1385 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1386 if (r < 0)
1387 return log_error_errno(r, "Failed to add machine removal match: %m");
1388
1389 r = sd_bus_message_new_method_call(bus,
1390 &m,
1391 "org.freedesktop.machine1",
1392 "/org/freedesktop/machine1",
1393 "org.freedesktop.machine1.Manager",
1394 "OpenMachineLogin");
1395 if (r < 0)
1396 return bus_log_create_error(r);
1397
1398 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1399 if (r < 0)
1400 return bus_log_create_error(r);
1401
1402 r = sd_bus_message_append(m, "s", argv[1]);
1403 if (r < 0)
1404 return bus_log_create_error(r);
1405
1406 r = sd_bus_call(bus, m, 0, &error, &reply);
1407 if (r < 0) {
1408 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1409 return r;
1410 }
1411
1412 r = sd_bus_message_read(reply, "hs", &master, &pty);
1413 if (r < 0)
1414 return bus_log_parse_error(r);
1415
1416 assert_se(sigemptyset(&mask) == 0);
1417 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1418 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1419
1420 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1421
1422 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1423 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1424
1425 r = pty_forward_new(event, master, true, &forward);
1426 if (r < 0)
1427 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1428
1429 r = sd_event_loop(event);
1430 if (r < 0)
1431 return log_error_errno(r, "Failed to run event loop: %m");
1432
1433 pty_forward_get_last_char(forward, &last_char);
1434 machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
1435
1436 forward = pty_forward_free(forward);
1437
1438 if (last_char != '\n')
1439 fputc('\n', stdout);
1440
1441 if (machine_died)
1442 log_info("Machine %s terminated.", argv[1]);
1443 else
1444 log_info("Connection to machine %s terminated.", argv[1]);
1445
1446 sd_event_get_exit_code(event, &ret);
1447 return ret;
1448 }
1449
1450 static int remove_image(int argc, char *argv[], void *userdata) {
1451 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1452 sd_bus *bus = userdata;
1453 int r, i;
1454
1455 assert(bus);
1456
1457 polkit_agent_open_if_enabled();
1458
1459 for (i = 1; i < argc; i++) {
1460 r = sd_bus_call_method(
1461 bus,
1462 "org.freedesktop.machine1",
1463 "/org/freedesktop/machine1",
1464 "org.freedesktop.machine1.Manager",
1465 "RemoveImage",
1466 &error,
1467 NULL,
1468 "s", argv[i]);
1469 if (r < 0) {
1470 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1471 return r;
1472 }
1473 }
1474
1475 return 0;
1476 }
1477
1478 static int rename_image(int argc, char *argv[], void *userdata) {
1479 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1480 sd_bus *bus = userdata;
1481 int r;
1482
1483 polkit_agent_open_if_enabled();
1484
1485 r = sd_bus_call_method(
1486 bus,
1487 "org.freedesktop.machine1",
1488 "/org/freedesktop/machine1",
1489 "org.freedesktop.machine1.Manager",
1490 "RenameImage",
1491 &error,
1492 NULL,
1493 "ss", argv[1], argv[2]);
1494 if (r < 0) {
1495 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1496 return r;
1497 }
1498
1499 return 0;
1500 }
1501
1502 static int clone_image(int argc, char *argv[], void *userdata) {
1503 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1504 sd_bus *bus = userdata;
1505 int r;
1506
1507 polkit_agent_open_if_enabled();
1508
1509 r = sd_bus_call_method(
1510 bus,
1511 "org.freedesktop.machine1",
1512 "/org/freedesktop/machine1",
1513 "org.freedesktop.machine1.Manager",
1514 "CloneImage",
1515 &error,
1516 NULL,
1517 "ssb", argv[1], argv[2], arg_read_only);
1518 if (r < 0) {
1519 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1520 return r;
1521 }
1522
1523 return 0;
1524 }
1525
1526 static int read_only_image(int argc, char *argv[], void *userdata) {
1527 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1528 sd_bus *bus = userdata;
1529 int b = true, r;
1530
1531 if (argc > 2) {
1532 b = parse_boolean(argv[2]);
1533 if (b < 0) {
1534 log_error("Failed to parse boolean argument: %s", argv[2]);
1535 return -EINVAL;
1536 }
1537 }
1538
1539 polkit_agent_open_if_enabled();
1540
1541 r = sd_bus_call_method(
1542 bus,
1543 "org.freedesktop.machine1",
1544 "/org/freedesktop/machine1",
1545 "org.freedesktop.machine1.Manager",
1546 "MarkImageReadOnly",
1547 &error,
1548 NULL,
1549 "sb", argv[1], b);
1550 if (r < 0) {
1551 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1552 return r;
1553 }
1554
1555 return 0;
1556 }
1557
1558 static int start_machine(int argc, char *argv[], void *userdata) {
1559 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1560 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1561 sd_bus *bus = userdata;
1562 int r, i;
1563
1564 assert(bus);
1565
1566 polkit_agent_open_if_enabled();
1567
1568 r = bus_wait_for_jobs_new(bus, &w);
1569 if (r < 0)
1570 return log_oom();
1571
1572 for (i = 1; i < argc; i++) {
1573 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1574 _cleanup_free_ char *e = NULL, *unit = NULL;
1575 const char *object;
1576
1577 if (!machine_name_is_valid(argv[i])) {
1578 log_error("Invalid machine name %s.", argv[i]);
1579 return -EINVAL;
1580 }
1581
1582 e = unit_name_escape(argv[i]);
1583 if (!e)
1584 return log_oom();
1585
1586 unit = unit_name_build("systemd-nspawn", e, ".service");
1587 if (!unit)
1588 return log_oom();
1589
1590 r = sd_bus_message_new_method_call(
1591 bus,
1592 &m,
1593 "org.freedesktop.systemd1",
1594 "/org/freedesktop/systemd1",
1595 "org.freedesktop.systemd1.Manager",
1596 "StartUnit");
1597 if (r < 0)
1598 return bus_log_create_error(r);
1599
1600 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1601 if (r < 0)
1602 return bus_log_create_error(r);
1603
1604 r = sd_bus_message_append(m, "ss", unit, "fail");
1605 if (r < 0)
1606 return bus_log_create_error(r);
1607
1608 r = sd_bus_call(bus, m, 0, &error, &reply);
1609 if (r < 0) {
1610 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1611 return r;
1612 }
1613
1614 r = sd_bus_message_read(reply, "o", &object);
1615 if (r < 0)
1616 return bus_log_parse_error(r);
1617
1618 r = bus_wait_for_jobs_add(w, object);
1619 if (r < 0)
1620 return log_oom();
1621 }
1622
1623 r = bus_wait_for_jobs(w, arg_quiet);
1624 if (r < 0)
1625 return r;
1626
1627 return 0;
1628 }
1629
1630 static int enable_machine(int argc, char *argv[], void *userdata) {
1631 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1632 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1633 int carries_install_info = 0;
1634 const char *method = NULL;
1635 sd_bus *bus = userdata;
1636 int r, i;
1637
1638 assert(bus);
1639
1640 polkit_agent_open_if_enabled();
1641
1642 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1643
1644 r = sd_bus_message_new_method_call(
1645 bus,
1646 &m,
1647 "org.freedesktop.systemd1",
1648 "/org/freedesktop/systemd1",
1649 "org.freedesktop.systemd1.Manager",
1650 method);
1651 if (r < 0)
1652 return bus_log_create_error(r);
1653
1654 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1655 if (r < 0)
1656 return bus_log_create_error(r);
1657
1658 r = sd_bus_message_open_container(m, 'a', "s");
1659 if (r < 0)
1660 return bus_log_create_error(r);
1661
1662 for (i = 1; i < argc; i++) {
1663 _cleanup_free_ char *e = NULL, *unit = NULL;
1664
1665 if (!machine_name_is_valid(argv[i])) {
1666 log_error("Invalid machine name %s.", argv[i]);
1667 return -EINVAL;
1668 }
1669
1670 e = unit_name_escape(argv[i]);
1671 if (!e)
1672 return log_oom();
1673
1674 unit = unit_name_build("systemd-nspawn", e, ".service");
1675 if (!unit)
1676 return log_oom();
1677
1678 r = sd_bus_message_append(m, "s", unit);
1679 if (r < 0)
1680 return bus_log_create_error(r);
1681 }
1682
1683 r = sd_bus_message_close_container(m);
1684 if (r < 0)
1685 return bus_log_create_error(r);
1686
1687 if (streq(argv[0], "enable"))
1688 r = sd_bus_message_append(m, "bb", false, false);
1689 else
1690 r = sd_bus_message_append(m, "b", false);
1691 if (r < 0)
1692 return bus_log_create_error(r);
1693
1694 r = sd_bus_call(bus, m, 0, &error, &reply);
1695 if (r < 0) {
1696 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1697 return r;
1698 }
1699
1700 if (streq(argv[0], "enable")) {
1701 r = sd_bus_message_read(reply, "b", carries_install_info);
1702 if (r < 0)
1703 return bus_log_parse_error(r);
1704 }
1705
1706 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1707 if (r < 0)
1708 return r;
1709
1710 m = sd_bus_message_unref(m);
1711
1712 r = sd_bus_message_new_method_call(
1713 bus,
1714 &m,
1715 "org.freedesktop.systemd1",
1716 "/org/freedesktop/systemd1",
1717 "org.freedesktop.systemd1.Manager",
1718 "Reload");
1719 if (r < 0)
1720 return bus_log_create_error(r);
1721
1722 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1723 if (r < 0)
1724 return bus_log_create_error(r);
1725
1726 r = sd_bus_call(bus, m, 0, &error, NULL);
1727 if (r < 0) {
1728 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1729 return r;
1730 }
1731
1732 return 0;
1733 }
1734
1735 static int help(int argc, char *argv[], void *userdata) {
1736
1737 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1738 "Send control commands to or query the virtual machine and container\n"
1739 "registration manager.\n\n"
1740 " -h --help Show this help\n"
1741 " --version Show package version\n"
1742 " --no-pager Do not pipe output into a pager\n"
1743 " --no-legend Do not show the headers and footers\n"
1744 " --no-ask-password Do not ask for system passwords\n"
1745 " -H --host=[USER@]HOST Operate on remote host\n"
1746 " -M --machine=CONTAINER Operate on local container\n"
1747 " -p --property=NAME Show only properties by this name\n"
1748 " -q --quiet Suppress output\n"
1749 " -a --all Show all properties, including empty ones\n"
1750 " -l --full Do not ellipsize output\n"
1751 " --kill-who=WHO Who to send signal to\n"
1752 " -s --signal=SIGNAL Which signal to send\n"
1753 " --read-only Create read-only bind mount\n"
1754 " --mkdir Create directory before bind mounting, if missing\n"
1755 " -n --lines=INTEGER Number of journal entries to show\n"
1756 " -o --output=STRING Change journal output mode (short,\n"
1757 " short-monotonic, verbose, export, json,\n"
1758 " json-pretty, json-sse, cat)\n\n"
1759 "Machine Commands:\n"
1760 " list List running VMs and containers\n"
1761 " status NAME... Show VM/container details\n"
1762 " show NAME... Show properties of one or more VMs/containers\n"
1763 " login NAME Get a login prompt on a container\n"
1764 " start NAME... Start container as a service\n"
1765 " enable NAME... Enable automatic container start at boot\n"
1766 " disable NAME... Disable automatic container start at boot\n"
1767 " poweroff NAME... Power off one or more containers\n"
1768 " reboot NAME... Reboot one or more containers\n"
1769 " terminate NAME... Terminate one or more VMs/containers\n"
1770 " kill NAME... Send signal to processes of a VM/container\n"
1771 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1772 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
1773 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
1774 "Image Commands:\n"
1775 " list-images Show available images\n"
1776 " image-status NAME... Show image details\n"
1777 " show-image NAME... Show properties of image\n"
1778 " clone NAME NAME Clone an image\n"
1779 " rename NAME NAME Rename an image\n"
1780 " read-only NAME [BOOL] Mark or unmark image read-only\n"
1781 " remove NAME... Remove an image\n",
1782 program_invocation_short_name);
1783
1784 return 0;
1785 }
1786
1787 static int parse_argv(int argc, char *argv[]) {
1788
1789 enum {
1790 ARG_VERSION = 0x100,
1791 ARG_NO_PAGER,
1792 ARG_NO_LEGEND,
1793 ARG_KILL_WHO,
1794 ARG_READ_ONLY,
1795 ARG_MKDIR,
1796 ARG_NO_ASK_PASSWORD,
1797 };
1798
1799 static const struct option options[] = {
1800 { "help", no_argument, NULL, 'h' },
1801 { "version", no_argument, NULL, ARG_VERSION },
1802 { "property", required_argument, NULL, 'p' },
1803 { "all", no_argument, NULL, 'a' },
1804 { "full", no_argument, NULL, 'l' },
1805 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1806 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1807 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1808 { "signal", required_argument, NULL, 's' },
1809 { "host", required_argument, NULL, 'H' },
1810 { "machine", required_argument, NULL, 'M' },
1811 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1812 { "mkdir", no_argument, NULL, ARG_MKDIR },
1813 { "quiet", no_argument, NULL, 'q' },
1814 { "lines", required_argument, NULL, 'n' },
1815 { "output", required_argument, NULL, 'o' },
1816 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1817 {}
1818 };
1819
1820 int c, r;
1821
1822 assert(argc >= 0);
1823 assert(argv);
1824
1825 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
1826
1827 switch (c) {
1828
1829 case 'h':
1830 return help(0, NULL, NULL);
1831
1832 case ARG_VERSION:
1833 puts(PACKAGE_STRING);
1834 puts(SYSTEMD_FEATURES);
1835 return 0;
1836
1837 case 'p':
1838 r = strv_extend(&arg_property, optarg);
1839 if (r < 0)
1840 return log_oom();
1841
1842 /* If the user asked for a particular
1843 * property, show it to him, even if it is
1844 * empty. */
1845 arg_all = true;
1846 break;
1847
1848 case 'a':
1849 arg_all = true;
1850 break;
1851
1852 case 'l':
1853 arg_full = true;
1854 break;
1855
1856 case 'n':
1857 if (safe_atou(optarg, &arg_lines) < 0) {
1858 log_error("Failed to parse lines '%s'", optarg);
1859 return -EINVAL;
1860 }
1861 break;
1862
1863 case 'o':
1864 arg_output = output_mode_from_string(optarg);
1865 if (arg_output < 0) {
1866 log_error("Unknown output '%s'.", optarg);
1867 return -EINVAL;
1868 }
1869 break;
1870
1871 case ARG_NO_PAGER:
1872 arg_no_pager = true;
1873 break;
1874
1875 case ARG_NO_LEGEND:
1876 arg_legend = false;
1877 break;
1878
1879 case ARG_KILL_WHO:
1880 arg_kill_who = optarg;
1881 break;
1882
1883 case 's':
1884 arg_signal = signal_from_string_try_harder(optarg);
1885 if (arg_signal < 0) {
1886 log_error("Failed to parse signal string %s.", optarg);
1887 return -EINVAL;
1888 }
1889 break;
1890
1891 case ARG_NO_ASK_PASSWORD:
1892 arg_ask_password = false;
1893 break;
1894
1895 case 'H':
1896 arg_transport = BUS_TRANSPORT_REMOTE;
1897 arg_host = optarg;
1898 break;
1899
1900 case 'M':
1901 arg_transport = BUS_TRANSPORT_MACHINE;
1902 arg_host = optarg;
1903 break;
1904
1905 case ARG_READ_ONLY:
1906 arg_read_only = true;
1907 break;
1908
1909 case ARG_MKDIR:
1910 arg_mkdir = true;
1911 break;
1912
1913 case 'q':
1914 arg_quiet = true;
1915 break;
1916
1917 case '?':
1918 return -EINVAL;
1919
1920 default:
1921 assert_not_reached("Unhandled option");
1922 }
1923
1924 return 1;
1925 }
1926
1927 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1928
1929 static const Verb verbs[] = {
1930 { "help", VERB_ANY, VERB_ANY, 0, help },
1931 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
1932 { "list-images", VERB_ANY, 1, 0, list_images },
1933 { "status", 2, VERB_ANY, 0, show_machine },
1934 { "image-status",2, VERB_ANY, 0, show_image },
1935 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
1936 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
1937 { "terminate", 2, VERB_ANY, 0, terminate_machine },
1938 { "reboot", 2, VERB_ANY, 0, reboot_machine },
1939 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
1940 { "kill", 2, VERB_ANY, 0, kill_machine },
1941 { "login", 2, 2, 0, login_machine },
1942 { "bind", 3, 4, 0, bind_mount },
1943 { "copy-to", 3, 4, 0, copy_files },
1944 { "copy-from", 3, 4, 0, copy_files },
1945 { "remove", 2, VERB_ANY, 0, remove_image },
1946 { "rename", 3, 3, 0, rename_image },
1947 { "clone", 3, 3, 0, clone_image },
1948 { "read-only", 2, 3, 0, read_only_image },
1949 { "start", 2, VERB_ANY, 0, start_machine },
1950 { "enable", 2, VERB_ANY, 0, enable_machine },
1951 { "disable", 2, VERB_ANY, 0, enable_machine },
1952 {}
1953 };
1954
1955 return dispatch_verb(argc, argv, verbs, bus);
1956 }
1957
1958 int main(int argc, char*argv[]) {
1959 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1960 int r;
1961
1962 setlocale(LC_ALL, "");
1963 log_parse_environment();
1964 log_open();
1965
1966 r = parse_argv(argc, argv);
1967 if (r <= 0)
1968 goto finish;
1969
1970 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1971 if (r < 0) {
1972 log_error_errno(r, "Failed to create bus connection: %m");
1973 goto finish;
1974 }
1975
1976 r = machinectl_main(argc, argv, bus);
1977
1978 finish:
1979 pager_close();
1980 polkit_agent_close();
1981
1982 strv_free(arg_property);
1983
1984 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1985 }