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