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