]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machined-dbus.c
bootctl: check that partition uuid is valid
[thirdparty/systemd.git] / src / machine / machined-dbus.c
CommitLineData
1ee306e1
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <string.h>
24#include <unistd.h>
1ee306e1 25
c3350683 26#include "sd-id128.h"
1ee306e1 27#include "path-util.h"
1ee306e1 28#include "unit-name.h"
c3350683 29#include "bus-util.h"
96aad8d1 30#include "bus-common-errors.h"
23c80348 31#include "cgroup-util.h"
160e3793 32#include "btrfs-util.h"
003dffde
LP
33#include "machine-image.h"
34#include "image-dbus.h"
c3350683 35#include "machined.h"
003dffde 36#include "machine-dbus.h"
1ee306e1 37
160e3793
LP
38static int property_get_pool_path(
39 sd_bus *bus,
40 const char *path,
41 const char *interface,
42 const char *property,
43 sd_bus_message *reply,
44 void *userdata,
45 sd_bus_error *error) {
46
47 assert(bus);
48 assert(reply);
49
50 return sd_bus_message_append(reply, "s", "/var/lib/machines");
51}
52
53static int property_get_pool_usage(
54 sd_bus *bus,
55 const char *path,
56 const char *interface,
57 const char *property,
58 sd_bus_message *reply,
59 void *userdata,
60 sd_bus_error *error) {
61
62 _cleanup_close_ int fd = -1;
63 uint64_t usage = (uint64_t) -1;
64 struct stat st;
65
66 assert(bus);
67 assert(reply);
68
69 /* We try to read the quota info from /var/lib/machines, as
70 * well as the usage of the loopback file
71 * /var/lib/machines.raw, and pick the larger value. */
72
73 fd = open("/var/lib/machines", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
74 if (fd >= 0) {
75 BtrfsQuotaInfo q;
76
77 if (btrfs_subvol_get_quota_fd(fd, &q) >= 0)
78 usage = q.referred;
79 }
80
81 if (stat("/var/lib/machines.raw", &st) >= 0) {
82 if (usage == (uint64_t) -1 || st.st_blocks * 512ULL > usage)
83 usage = st.st_blocks * 512ULL;
84 }
85
86 return sd_bus_message_append(reply, "t", usage);
87}
88
89static int property_get_pool_limit(
90 sd_bus *bus,
91 const char *path,
92 const char *interface,
93 const char *property,
94 sd_bus_message *reply,
95 void *userdata,
96 sd_bus_error *error) {
97
98 _cleanup_close_ int fd = -1;
99 uint64_t size = (uint64_t) -1;
100 struct stat st;
101
102 assert(bus);
103 assert(reply);
104
105 /* We try to read the quota limit from /var/lib/machines, as
106 * well as the size of the loopback file
107 * /var/lib/machines.raw, and pick the smaller value. */
108
109 fd = open("/var/lib/machines", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
110 if (fd >= 0) {
111 BtrfsQuotaInfo q;
112
113 if (btrfs_subvol_get_quota_fd(fd, &q) >= 0)
114 size = q.referred_max;
115 }
116
117 if (stat("/var/lib/machines.raw", &st) >= 0) {
118 if (size == (uint64_t) -1 || (uint64_t) st.st_size < size)
119 size = st.st_size;
120 }
121
122 return sd_bus_message_append(reply, "t", size);
123}
124
ebcf1f97 125static int method_get_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
c3350683
LP
126 _cleanup_free_ char *p = NULL;
127 Manager *m = userdata;
128 Machine *machine;
129 const char *name;
130 int r;
131
132 assert(bus);
133 assert(message);
134 assert(m);
135
136 r = sd_bus_message_read(message, "s", &name);
137 if (r < 0)
ebcf1f97 138 return r;
c3350683
LP
139
140 machine = hashmap_get(m->machines, name);
141 if (!machine)
ebcf1f97 142 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
c3350683
LP
143
144 p = machine_bus_path(machine);
145 if (!p)
ebcf1f97 146 return -ENOMEM;
c3350683 147
df2d202e 148 return sd_bus_reply_method_return(message, "o", p);
c3350683
LP
149}
150
c2ce6a3d
LP
151static int method_get_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
152 _cleanup_free_ char *p = NULL;
153 Manager *m = userdata;
154 const char *name;
155 int r;
156
157 assert(bus);
158 assert(message);
159 assert(m);
160
161 r = sd_bus_message_read(message, "s", &name);
162 if (r < 0)
163 return r;
164
165 r = image_find(name, NULL);
166 if (r == 0)
167 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
168 if (r < 0)
169 return r;
170
171 p = image_bus_path(name);
172 if (!p)
173 return -ENOMEM;
174
175 return sd_bus_reply_method_return(message, "o", p);
176}
177
ebcf1f97 178static int method_get_machine_by_pid(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
c3350683
LP
179 _cleanup_free_ char *p = NULL;
180 Manager *m = userdata;
181 Machine *machine = NULL;
4e724d9c 182 pid_t pid;
c3350683
LP
183 int r;
184
185 assert(bus);
186 assert(message);
187 assert(m);
188
4e724d9c
LP
189 assert_cc(sizeof(pid_t) == sizeof(uint32_t));
190
c3350683
LP
191 r = sd_bus_message_read(message, "u", &pid);
192 if (r < 0)
ebcf1f97 193 return r;
c3350683 194
4e724d9c 195 if (pid == 0) {
5b12334d
LP
196 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
197
198 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
199 if (r < 0)
200 return r;
201
202 r = sd_bus_creds_get_pid(creds, &pid);
4e724d9c 203 if (r < 0)
ebcf1f97 204 return r;
4e724d9c
LP
205 }
206
c3350683
LP
207 r = manager_get_machine_by_pid(m, pid, &machine);
208 if (r < 0)
ebcf1f97 209 return r;
c3350683 210 if (!machine)
de0671ee 211 return sd_bus_error_setf(error, BUS_ERROR_NO_MACHINE_FOR_PID, "PID "PID_FMT" does not belong to any known machine", pid);
c3350683
LP
212
213 p = machine_bus_path(machine);
214 if (!p)
ebcf1f97 215 return -ENOMEM;
c3350683 216
df2d202e 217 return sd_bus_reply_method_return(message, "o", p);
c3350683
LP
218}
219
ebcf1f97 220static int method_list_machines(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
c3350683
LP
221 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
222 Manager *m = userdata;
223 Machine *machine;
224 Iterator i;
225 int r;
226
227 assert(bus);
228 assert(message);
229 assert(m);
230
df2d202e 231 r = sd_bus_message_new_method_return(message, &reply);
c3350683 232 if (r < 0)
ebcf1f97 233 return sd_bus_error_set_errno(error, r);
c3350683
LP
234
235 r = sd_bus_message_open_container(reply, 'a', "(ssso)");
236 if (r < 0)
ebcf1f97 237 return sd_bus_error_set_errno(error, r);
c3350683
LP
238
239 HASHMAP_FOREACH(machine, m->machines, i) {
240 _cleanup_free_ char *p = NULL;
241
242 p = machine_bus_path(machine);
243 if (!p)
ebcf1f97 244 return -ENOMEM;
c3350683
LP
245
246 r = sd_bus_message_append(reply, "(ssso)",
247 machine->name,
248 strempty(machine_class_to_string(machine->class)),
249 machine->service,
250 p);
251 if (r < 0)
ebcf1f97 252 return sd_bus_error_set_errno(error, r);
c3350683 253 }
1ee306e1 254
c3350683
LP
255 r = sd_bus_message_close_container(reply);
256 if (r < 0)
ebcf1f97 257 return sd_bus_error_set_errno(error, r);
c3350683
LP
258
259 return sd_bus_send(bus, reply, NULL);
260}
261
9b5ed6fe 262static int method_create_or_register_machine(Manager *manager, sd_bus_message *message, bool read_network, Machine **_m, sd_bus_error *error) {
8aec412f 263 const char *name, *service, *class, *root_directory;
9b5ed6fe 264 const int32_t *netif = NULL;
1ee306e1
LP
265 MachineClass c;
266 uint32_t leader;
267 sd_id128_t id;
c3350683 268 const void *v;
1ee306e1 269 Machine *m;
9b5ed6fe 270 size_t n, n_netif = 0;
c3350683 271 int r;
1ee306e1 272
c3350683 273 assert(manager);
89f7c846
LP
274 assert(message);
275 assert(_m);
1ee306e1 276
c3350683
LP
277 r = sd_bus_message_read(message, "s", &name);
278 if (r < 0)
ebcf1f97 279 return r;
7f0d207d 280 if (!machine_name_is_valid(name))
ebcf1f97 281 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name");
1ee306e1 282
c3350683
LP
283 r = sd_bus_message_read_array(message, 'y', &v, &n);
284 if (r < 0)
ebcf1f97 285 return r;
1ee306e1
LP
286 if (n == 0)
287 id = SD_ID128_NULL;
288 else if (n == 16)
289 memcpy(&id, v, n);
290 else
ebcf1f97 291 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine ID parameter");
1ee306e1 292
c3350683
LP
293 r = sd_bus_message_read(message, "ssus", &service, &class, &leader, &root_directory);
294 if (r < 0)
ebcf1f97 295 return r;
1ee306e1 296
9b5ed6fe
LP
297 if (read_network) {
298 size_t i;
299
300 r = sd_bus_message_read_array(message, 'i', (const void**) &netif, &n_netif);
301 if (r < 0)
302 return r;
303
304 n_netif /= sizeof(int32_t);
305
306 for (i = 0; i < n_netif; i++) {
307 if (netif[i] <= 0)
308 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid network interface index %i", netif[i]);
309 }
310 }
311
1ee306e1
LP
312 if (isempty(class))
313 c = _MACHINE_CLASS_INVALID;
314 else {
315 c = machine_class_from_string(class);
316 if (c < 0)
ebcf1f97 317 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine class parameter");
1ee306e1
LP
318 }
319
c3350683 320 if (leader == 1)
ebcf1f97 321 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
1ee306e1 322
c3350683 323 if (!isempty(root_directory) && !path_is_absolute(root_directory))
ebcf1f97 324 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path");
1ee306e1 325
c3350683 326 if (leader == 0) {
5b12334d
LP
327 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
328
329 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
330 if (r < 0)
331 return r;
332
c3350683 333 assert_cc(sizeof(uint32_t) == sizeof(pid_t));
554604b3 334
5b12334d 335 r = sd_bus_creds_get_pid(creds, (pid_t*) &leader);
c3350683 336 if (r < 0)
ebcf1f97 337 return r;
c3350683 338 }
554604b3 339
1ee306e1 340 if (hashmap_get(manager->machines, name))
ebcf1f97 341 return sd_bus_error_setf(error, BUS_ERROR_MACHINE_EXISTS, "Machine '%s' already exists", name);
1ee306e1
LP
342
343 r = manager_add_machine(manager, name, &m);
344 if (r < 0)
ebcf1f97 345 return r;
1ee306e1
LP
346
347 m->leader = leader;
348 m->class = c;
349 m->id = id;
350
351 if (!isempty(service)) {
352 m->service = strdup(service);
353 if (!m->service) {
ebcf1f97 354 r = -ENOMEM;
1ee306e1
LP
355 goto fail;
356 }
357 }
358
359 if (!isempty(root_directory)) {
360 m->root_directory = strdup(root_directory);
361 if (!m->root_directory) {
ebcf1f97 362 r = -ENOMEM;
1ee306e1
LP
363 goto fail;
364 }
365 }
366
9b5ed6fe
LP
367 if (n_netif > 0) {
368 assert_cc(sizeof(int32_t) == sizeof(int));
369 m->netif = memdup(netif, sizeof(int32_t) * n_netif);
370 if (!m->netif) {
371 r = -ENOMEM;
372 goto fail;
373 }
374
375 m->n_netif = n_netif;
376 }
377
89f7c846
LP
378 *_m = m;
379
380 return 1;
381
382fail:
383 machine_add_to_gc_queue(m);
384 return r;
385}
386
9b5ed6fe 387static int method_create_machine_internal(sd_bus *bus, sd_bus_message *message, bool read_network, void *userdata, sd_bus_error *error) {
89f7c846
LP
388 Manager *manager = userdata;
389 Machine *m = NULL;
390 int r;
391
9b5ed6fe 392 r = method_create_or_register_machine(manager, message, read_network, &m, error);
89f7c846
LP
393 if (r < 0)
394 return r;
395
396 r = sd_bus_message_enter_container(message, 'a', "(sv)");
397 if (r < 0)
398 goto fail;
399
ebcf1f97
LP
400 r = machine_start(m, message, error);
401 if (r < 0)
1ee306e1
LP
402 goto fail;
403
c3350683 404 m->create_message = sd_bus_message_ref(message);
c3350683 405 return 1;
1ee306e1
LP
406
407fail:
a3e7f417 408 machine_add_to_gc_queue(m);
89f7c846
LP
409 return r;
410}
1ee306e1 411
9b5ed6fe
LP
412static int method_create_machine_with_network(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
413 return method_create_machine_internal(bus, message, true, userdata, error);
414}
415
416static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
417 return method_create_machine_internal(bus, message, false, userdata, error);
418}
419
420static int method_register_machine_internal(sd_bus *bus, sd_bus_message *message, bool read_network, void *userdata, sd_bus_error *error) {
89f7c846
LP
421 Manager *manager = userdata;
422 _cleanup_free_ char *p = NULL;
423 Machine *m = NULL;
424 int r;
425
9b5ed6fe 426 r = method_create_or_register_machine(manager, message, read_network, &m, error);
89f7c846
LP
427 if (r < 0)
428 return r;
429
430 r = cg_pid_get_unit(m->leader, &m->unit);
431 if (r < 0) {
432 r = sd_bus_error_set_errnof(error, r, "Failed to determine unit of process "PID_FMT" : %s", m->leader, strerror(-r));
433 goto fail;
434 }
435
436 r = machine_start(m, NULL, error);
437 if (r < 0)
438 goto fail;
439
440 p = machine_bus_path(m);
441 if (!p) {
442 r = -ENOMEM;
443 goto fail;
444 }
445
446 return sd_bus_reply_method_return(message, "o", p);
447
448fail:
449 machine_add_to_gc_queue(m);
1ee306e1
LP
450 return r;
451}
452
9b5ed6fe
LP
453static int method_register_machine_with_network(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
454 return method_register_machine_internal(bus, message, true, userdata, error);
455}
456
457static int method_register_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
458 return method_register_machine_internal(bus, message, false, userdata, error);
459}
460
ebcf1f97 461static int method_terminate_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
1ee306e1 462 Manager *m = userdata;
c3350683
LP
463 Machine *machine;
464 const char *name;
1ee306e1
LP
465 int r;
466
c3350683 467 assert(bus);
1ee306e1
LP
468 assert(message);
469 assert(m);
470
c3350683
LP
471 r = sd_bus_message_read(message, "s", &name);
472 if (r < 0)
ebcf1f97 473 return sd_bus_error_set_errno(error, r);
1ee306e1 474
c3350683
LP
475 machine = hashmap_get(m->machines, name);
476 if (!machine)
ebcf1f97 477 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
1ee306e1 478
878cd7e9 479 return bus_machine_method_terminate(bus, message, machine, error);
c3350683 480}
1ee306e1 481
ebcf1f97 482static int method_kill_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
c3350683
LP
483 Manager *m = userdata;
484 Machine *machine;
485 const char *name;
c3350683 486 int r;
1ee306e1 487
c3350683
LP
488 assert(bus);
489 assert(message);
490 assert(m);
1ee306e1 491
878cd7e9 492 r = sd_bus_message_read(message, "s", &name);
c3350683 493 if (r < 0)
ebcf1f97 494 return sd_bus_error_set_errno(error, r);
1ee306e1 495
c3350683
LP
496 machine = hashmap_get(m->machines, name);
497 if (!machine)
ebcf1f97 498 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
1ee306e1 499
878cd7e9
LP
500 return bus_machine_method_kill(bus, message, machine, error);
501}
502
503static int method_get_machine_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
504 Manager *m = userdata;
505 Machine *machine;
506 const char *name;
507 int r;
508
509 assert(bus);
510 assert(message);
511 assert(m);
512
513 r = sd_bus_message_read(message, "s", &name);
c3350683 514 if (r < 0)
ebcf1f97 515 return sd_bus_error_set_errno(error, r);
1ee306e1 516
878cd7e9
LP
517 machine = hashmap_get(m->machines, name);
518 if (!machine)
519 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
520
521 return bus_machine_method_get_addresses(bus, message, machine, error);
c3350683 522}
1ee306e1 523
717603e3
LP
524static int method_get_machine_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
525 Manager *m = userdata;
526 Machine *machine;
527 const char *name;
528 int r;
529
530 assert(bus);
531 assert(message);
532 assert(m);
533
534 r = sd_bus_message_read(message, "s", &name);
535 if (r < 0)
536 return sd_bus_error_set_errno(error, r);
537
538 machine = hashmap_get(m->machines, name);
539 if (!machine)
540 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
541
542 return bus_machine_method_get_os_release(bus, message, machine, error);
543}
544
cd61c3bf
LP
545static int method_list_images(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
546 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
547 _cleanup_(image_hashmap_freep) Hashmap *images = NULL;
548 Manager *m = userdata;
549 Image *image;
550 Iterator i;
551 int r;
552
553 assert(bus);
554 assert(message);
555 assert(m);
556
557 images = hashmap_new(&string_hash_ops);
558 if (!images)
559 return -ENOMEM;
560
561 r = image_discover(images);
562 if (r < 0)
563 return r;
564
565 r = sd_bus_message_new_method_return(message, &reply);
566 if (r < 0)
567 return r;
568
b6b18498 569 r = sd_bus_message_open_container(reply, 'a', "(ssbttto)");
cd61c3bf
LP
570 if (r < 0)
571 return r;
572
573 HASHMAP_FOREACH(image, images, i) {
574 _cleanup_free_ char *p = NULL;
575
576 p = image_bus_path(image->name);
577 if (!p)
578 return -ENOMEM;
579
b6b18498 580 r = sd_bus_message_append(reply, "(ssbttto)",
cd61c3bf
LP
581 image->name,
582 image_type_to_string(image->type),
583 image->read_only,
10f9c755
LP
584 image->crtime,
585 image->mtime,
c19de711 586 image->usage,
cd61c3bf
LP
587 p);
588 if (r < 0)
589 return r;
590 }
591
592 r = sd_bus_message_close_container(reply);
593 if (r < 0)
594 return r;
595
596 return sd_bus_send(bus, reply, NULL);
597}
598
40205d70
LP
599static int method_open_machine_pty(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
600 Manager *m = userdata;
601 Machine *machine;
602 const char *name;
603 int r;
604
605 assert(bus);
606 assert(message);
607 assert(m);
608
609 r = sd_bus_message_read(message, "s", &name);
610 if (r < 0)
611 return sd_bus_error_set_errno(error, r);
612
613 machine = hashmap_get(m->machines, name);
614 if (!machine)
615 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
616
617 return bus_machine_method_open_pty(bus, message, machine, error);
618}
619
5f8cc96a
LP
620static int method_open_machine_login(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
621 Manager *m = userdata;
622 Machine *machine;
623 const char *name;
624 int r;
625
626 assert(bus);
627 assert(message);
628 assert(m);
629
630 r = sd_bus_message_read(message, "s", &name);
631 if (r < 0)
08682124 632 return r;
5f8cc96a
LP
633
634 machine = hashmap_get(m->machines, name);
635 if (!machine)
636 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
637
638 return bus_machine_method_open_login(bus, message, machine, error);
639}
640
90adaa25
LP
641static int method_bind_mount_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
642 Manager *m = userdata;
643 Machine *machine;
644 const char *name;
645 int r;
646
647 assert(bus);
648 assert(message);
649 assert(m);
650
651 r = sd_bus_message_read(message, "s", &name);
652 if (r < 0)
653 return r;
654
655 machine = hashmap_get(m->machines, name);
656 if (!machine)
657 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
658
659 return bus_machine_method_bind_mount(bus, message, machine, error);
660}
661
0370612e
LP
662static int method_copy_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
663 Manager *m = userdata;
664 Machine *machine;
665 const char *name;
666 int r;
667
668 assert(bus);
669 assert(message);
670 assert(m);
671
672 r = sd_bus_message_read(message, "s", &name);
673 if (r < 0)
674 return r;
675
676 machine = hashmap_get(m->machines, name);
677 if (!machine)
678 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
679
680 return bus_machine_method_copy(bus, message, machine, error);
681}
682
08682124
LP
683static int method_remove_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
684 _cleanup_(image_unrefp) Image* i = NULL;
685 const char *name;
686 int r;
687
688 assert(bus);
689 assert(message);
690
691 r = sd_bus_message_read(message, "s", &name);
692 if (r < 0)
693 return r;
694
695 if (!image_name_is_valid(name))
696 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
697
698 r = image_find(name, &i);
699 if (r < 0)
700 return r;
701 if (r == 0)
702 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
703
70244d1d 704 i->userdata = userdata;
1ddb263d 705 return bus_image_method_remove(bus, message, i, error);
08682124
LP
706}
707
ebd93cb6
LP
708static int method_rename_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
709 _cleanup_(image_unrefp) Image* i = NULL;
1ddb263d 710 const char *old_name;
ebd93cb6
LP
711 int r;
712
713 assert(bus);
714 assert(message);
715
1ddb263d 716 r = sd_bus_message_read(message, "s", &old_name);
ebd93cb6
LP
717 if (r < 0)
718 return r;
719
720 if (!image_name_is_valid(old_name))
721 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", old_name);
ebd93cb6
LP
722
723 r = image_find(old_name, &i);
724 if (r < 0)
725 return r;
726 if (r == 0)
727 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name);
728
70244d1d 729 i->userdata = userdata;
1ddb263d 730 return bus_image_method_rename(bus, message, i, error);
ebd93cb6
LP
731}
732
733static int method_clone_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
734 _cleanup_(image_unrefp) Image *i = NULL;
1ddb263d
LP
735 const char *old_name;
736 int r;
ebd93cb6
LP
737
738 assert(bus);
1ddb263d 739 r = sd_bus_message_read(message, "s", &old_name);
ebd93cb6
LP
740 if (r < 0)
741 return r;
742
743 if (!image_name_is_valid(old_name))
744 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", old_name);
ebd93cb6
LP
745
746 r = image_find(old_name, &i);
747 if (r < 0)
748 return r;
749 if (r == 0)
750 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name);
751
70244d1d 752 i->userdata = userdata;
1ddb263d 753 return bus_image_method_clone(bus, message, i, error);
ebd93cb6
LP
754}
755
756static int method_mark_image_read_only(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
757 _cleanup_(image_unrefp) Image *i = NULL;
758 const char *name;
1ddb263d 759 int r;
ebd93cb6
LP
760
761 assert(bus);
1ddb263d 762 r = sd_bus_message_read(message, "s", &name);
ebd93cb6
LP
763 if (r < 0)
764 return r;
765
766 if (!image_name_is_valid(name))
767 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
768
769 r = image_find(name, &i);
770 if (r < 0)
771 return r;
772 if (r == 0)
773 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
774
70244d1d 775 i->userdata = userdata;
1ddb263d 776 return bus_image_method_mark_read_only(bus, message, i, error);
ebd93cb6
LP
777}
778
d6ce17c7
LP
779static int method_set_pool_limit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
780 Manager *m = userdata;
781 uint64_t limit;
782 int r;
783
784 assert(bus);
785 r = sd_bus_message_read(message, "t", &limit);
786 if (r < 0)
787 return r;
788
789 r = bus_verify_polkit_async(
790 message,
791 CAP_SYS_ADMIN,
792 "org.freedesktop.machine1.manage-machines",
793 false,
794 UID_INVALID,
795 &m->polkit_registry,
796 error);
797 if (r < 0)
798 return r;
799 if (r == 0)
800 return 1; /* Will call us back */
801
802 r = btrfs_quota_limit("/var/lib/machines", limit);
803 if (r == -ENOTTY)
804 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
805 else if (r < 0)
806 return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m");
807
808 return sd_bus_reply_method_return(message, NULL);
809}
810
811static int method_set_image_limit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
812 _cleanup_(image_unrefp) Image *i = NULL;
813 const char *name;
814 int r;
815
816 assert(bus);
817 r = sd_bus_message_read(message, "s", &name);
818 if (r < 0)
819 return r;
820
821 if (!image_name_is_valid(name))
822 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
823
824 r = image_find(name, &i);
825 if (r < 0)
826 return r;
827 if (r == 0)
828 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
829
830 i->userdata = userdata;
831 return bus_image_method_set_limit(bus, message, i, error);
832}
833
c3350683
LP
834const sd_bus_vtable manager_vtable[] = {
835 SD_BUS_VTABLE_START(0),
160e3793
LP
836 SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0),
837 SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage, 0, 0),
838 SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit, 0, 0),
adacb957 839 SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED),
c2ce6a3d 840 SD_BUS_METHOD("GetImage", "s", "o", method_get_image, SD_BUS_VTABLE_UNPRIVILEGED),
adacb957
LP
841 SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
842 SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED),
b6b18498 843 SD_BUS_METHOD("ListImages", NULL, "a(ssbttto)", method_list_images, SD_BUS_VTABLE_UNPRIVILEGED),
c3350683 844 SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0),
9b5ed6fe 845 SD_BUS_METHOD("CreateMachineWithNetwork", "sayssusaia(sv)", "o", method_create_machine_with_network, 0),
8d07a7c4 846 SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0),
9b5ed6fe 847 SD_BUS_METHOD("RegisterMachineWithNetwork", "sayssusai", "o", method_register_machine_with_network, 0),
70244d1d
LP
848 SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_UNPRIVILEGED),
849 SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_UNPRIVILEGED),
3a6fb33c 850 SD_BUS_METHOD("GetMachineAddresses", "s", "a(iay)", method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
717603e3 851 SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
40205d70 852 SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0),
d04c1fb8 853 SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED),
70244d1d
LP
854 SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED),
855 SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
856 SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
857 SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED),
858 SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED),
859 SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED),
860 SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
d6ce17c7
LP
861 SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
862 SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),
c3350683
LP
863 SD_BUS_SIGNAL("MachineNew", "so", 0),
864 SD_BUS_SIGNAL("MachineRemoved", "so", 0),
865 SD_BUS_VTABLE_END
866};
1ee306e1 867
ebcf1f97 868int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
c3350683 869 const char *path, *result, *unit;
1ee306e1 870 Manager *m = userdata;
c3350683
LP
871 Machine *machine;
872 uint32_t id;
873 int r;
1ee306e1 874
c3350683 875 assert(bus);
1ee306e1 876 assert(message);
c3350683 877 assert(m);
1ee306e1 878
c3350683
LP
879 r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result);
880 if (r < 0) {
ebcf1f97
LP
881 bus_log_parse_error(r);
882 return r;
c3350683 883 }
6797c324 884
c3350683
LP
885 machine = hashmap_get(m->machine_units, unit);
886 if (!machine)
887 return 0;
6797c324 888
c3350683
LP
889 if (streq_ptr(path, machine->scope_job)) {
890 free(machine->scope_job);
891 machine->scope_job = NULL;
6797c324 892
c3350683
LP
893 if (machine->started) {
894 if (streq(result, "done"))
895 machine_send_create_reply(machine, NULL);
896 else {
ebcf1f97 897 _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
6797c324 898
ebcf1f97 899 sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
6797c324 900
ebcf1f97 901 machine_send_create_reply(machine, &e);
c3350683
LP
902 }
903 } else
904 machine_save(machine);
1ee306e1
LP
905 }
906
c3350683
LP
907 machine_add_to_gc_queue(machine);
908 return 0;
1ee306e1
LP
909}
910
ebcf1f97 911int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
c3350683
LP
912 _cleanup_free_ char *unit = NULL;
913 Manager *m = userdata;
914 Machine *machine;
915 const char *path;
ebcf1f97 916 int r;
554604b3 917
c3350683
LP
918 assert(bus);
919 assert(message);
920 assert(m);
554604b3 921
c3350683
LP
922 path = sd_bus_message_get_path(message);
923 if (!path)
554604b3 924 return 0;
554604b3 925
ebcf1f97 926 r = unit_name_from_dbus_path(path, &unit);
e5f5b5b9
LP
927 if (r == -EINVAL) /* not for a unit */
928 return 0;
ebcf1f97
LP
929 if (r < 0)
930 return r;
554604b3 931
c3350683
LP
932 machine = hashmap_get(m->machine_units, unit);
933 if (machine)
934 machine_add_to_gc_queue(machine);
554604b3 935
c3350683
LP
936 return 0;
937}
554604b3 938
ebcf1f97 939int match_unit_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
c3350683
LP
940 const char *path, *unit;
941 Manager *m = userdata;
942 Machine *machine;
943 int r;
554604b3 944
c3350683
LP
945 assert(bus);
946 assert(message);
947 assert(m);
554604b3 948
c3350683
LP
949 r = sd_bus_message_read(message, "so", &unit, &path);
950 if (r < 0) {
ebcf1f97
LP
951 bus_log_parse_error(r);
952 return r;
554604b3
LP
953 }
954
c3350683
LP
955 machine = hashmap_get(m->machine_units, unit);
956 if (machine)
957 machine_add_to_gc_queue(machine);
554604b3 958
c3350683
LP
959 return 0;
960}
554604b3 961
ebcf1f97 962int match_reloading(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
c3350683 963 Manager *m = userdata;
a658cafa
LP
964 Machine *machine;
965 Iterator i;
c3350683 966 int b, r;
554604b3 967
c3350683 968 assert(bus);
554604b3 969
c3350683
LP
970 r = sd_bus_message_read(message, "b", &b);
971 if (r < 0) {
ebcf1f97
LP
972 bus_log_parse_error(r);
973 return r;
554604b3 974 }
a658cafa
LP
975 if (b)
976 return 0;
554604b3 977
a658cafa
LP
978 /* systemd finished reloading, let's recheck all our machines */
979 log_debug("System manager has been reloaded, rechecking machines...");
554604b3 980
a658cafa
LP
981 HASHMAP_FOREACH(machine, m->machines, i)
982 machine_add_to_gc_queue(machine);
554604b3
LP
983
984 return 0;
985}
986
1ee306e1
LP
987int manager_start_scope(
988 Manager *manager,
989 const char *scope,
990 pid_t pid,
991 const char *slice,
992 const char *description,
c3350683
LP
993 sd_bus_message *more_properties,
994 sd_bus_error *error,
1ee306e1
LP
995 char **job) {
996
c3350683 997 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
554604b3 998 int r;
1ee306e1
LP
999
1000 assert(manager);
1001 assert(scope);
1002 assert(pid > 1);
1003
c3350683
LP
1004 r = sd_bus_message_new_method_call(
1005 manager->bus,
151b9b96 1006 &m,
1ee306e1
LP
1007 "org.freedesktop.systemd1",
1008 "/org/freedesktop/systemd1",
1009 "org.freedesktop.systemd1.Manager",
151b9b96 1010 "StartTransientUnit");
c3350683
LP
1011 if (r < 0)
1012 return r;
1ee306e1 1013
a658cafa 1014 r = sd_bus_message_append(m, "ss", strempty(scope), "fail");
c3350683
LP
1015 if (r < 0)
1016 return r;
1ee306e1 1017
c3350683
LP
1018 r = sd_bus_message_open_container(m, 'a', "(sv)");
1019 if (r < 0)
1020 return r;
1ee306e1
LP
1021
1022 if (!isempty(slice)) {
c3350683
LP
1023 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
1024 if (r < 0)
1025 return r;
1ee306e1
LP
1026 }
1027
1028 if (!isempty(description)) {
c3350683
LP
1029 r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
1030 if (r < 0)
1031 return r;
1ee306e1
LP
1032 }
1033
c3350683
LP
1034 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
1035 if (r < 0)
1036 return r;
554604b3 1037
a931ad47
LP
1038 r = sd_bus_message_append(m, "(sv)", "Delegate", "b", 1);
1039 if (r < 0)
1040 return r;
1041
554604b3 1042 if (more_properties) {
c3350683 1043 r = sd_bus_message_copy(m, more_properties, true);
554604b3
LP
1044 if (r < 0)
1045 return r;
1046 }
1047
c3350683
LP
1048 r = sd_bus_message_close_container(m);
1049 if (r < 0)
1050 return r;
1ee306e1 1051
86b8d289
LP
1052 r = sd_bus_message_append(m, "a(sa(sv))", 0);
1053 if (r < 0)
1054 return r;
1055
c49b30a2 1056 r = sd_bus_call(manager->bus, m, 0, error, &reply);
c3350683
LP
1057 if (r < 0)
1058 return r;
1ee306e1
LP
1059
1060 if (job) {
1061 const char *j;
1062 char *copy;
1063
c3350683
LP
1064 r = sd_bus_message_read(reply, "o", &j);
1065 if (r < 0)
1066 return r;
1ee306e1
LP
1067
1068 copy = strdup(j);
1069 if (!copy)
1070 return -ENOMEM;
1071
1072 *job = copy;
1073 }
1074
c3350683 1075 return 1;
1ee306e1
LP
1076}
1077
c3350683
LP
1078int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
1079 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1ee306e1
LP
1080 int r;
1081
1082 assert(manager);
1083 assert(unit);
1084
c3350683 1085 r = sd_bus_call_method(
1ee306e1
LP
1086 manager->bus,
1087 "org.freedesktop.systemd1",
1088 "/org/freedesktop/systemd1",
1089 "org.freedesktop.systemd1.Manager",
1090 "StopUnit",
1ee306e1 1091 error,
c3350683
LP
1092 &reply,
1093 "ss", unit, "fail");
1ee306e1 1094 if (r < 0) {
c3350683
LP
1095 if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
1096 sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
6797c324
LP
1097
1098 if (job)
1099 *job = NULL;
1100
c3350683 1101 sd_bus_error_free(error);
6797c324
LP
1102 return 0;
1103 }
1104
1ee306e1
LP
1105 return r;
1106 }
1107
1108 if (job) {
1109 const char *j;
1110 char *copy;
1111
c3350683
LP
1112 r = sd_bus_message_read(reply, "o", &j);
1113 if (r < 0)
1114 return r;
1ee306e1
LP
1115
1116 copy = strdup(j);
1117 if (!copy)
1118 return -ENOMEM;
1119
1120 *job = copy;
1121 }
1122
6797c324 1123 return 1;
1ee306e1
LP
1124}
1125
de58a50e 1126int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_error *error) {
1ee306e1
LP
1127 assert(manager);
1128 assert(unit);
1129
a658cafa 1130 return sd_bus_call_method(
1ee306e1
LP
1131 manager->bus,
1132 "org.freedesktop.systemd1",
1133 "/org/freedesktop/systemd1",
1134 "org.freedesktop.systemd1.Manager",
1135 "KillUnit",
1ee306e1 1136 error,
a658cafa 1137 NULL,
de58a50e 1138 "ssi", unit, "all", signo);
1ee306e1
LP
1139}
1140
1141int manager_unit_is_active(Manager *manager, const char *unit) {
c3350683
LP
1142 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1143 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1ee306e1 1144 _cleanup_free_ char *path = NULL;
1ee306e1 1145 const char *state;
1ee306e1
LP
1146 int r;
1147
1148 assert(manager);
1149 assert(unit);
1150
1ee306e1
LP
1151 path = unit_dbus_path_from_name(unit);
1152 if (!path)
1153 return -ENOMEM;
1154
c3350683 1155 r = sd_bus_get_property(
1ee306e1
LP
1156 manager->bus,
1157 "org.freedesktop.systemd1",
1158 path,
c3350683
LP
1159 "org.freedesktop.systemd1.Unit",
1160 "ActiveState",
1ee306e1 1161 &error,
c3350683
LP
1162 &reply,
1163 "s");
1ee306e1 1164 if (r < 0) {
c3350683
LP
1165 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
1166 sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
6797c324 1167 return true;
6797c324 1168
c3350683
LP
1169 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
1170 sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED))
6797c324 1171 return false;
6797c324 1172
1ee306e1
LP
1173 return r;
1174 }
1175
c3350683
LP
1176 r = sd_bus_message_read(reply, "s", &state);
1177 if (r < 0)
1ee306e1 1178 return -EINVAL;
1ee306e1
LP
1179
1180 return !streq(state, "inactive") && !streq(state, "failed");
1181}
bd16acf3 1182
c3350683
LP
1183int manager_job_is_active(Manager *manager, const char *path) {
1184 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1185 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1186 int r;
bd16acf3 1187
c3350683
LP
1188 assert(manager);
1189 assert(path);
bd16acf3 1190
c3350683
LP
1191 r = sd_bus_get_property(
1192 manager->bus,
1193 "org.freedesktop.systemd1",
1194 path,
1195 "org.freedesktop.systemd1.Job",
1196 "State",
1197 &error,
1198 &reply,
1199 "s");
1200 if (r < 0) {
1201 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
1202 sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
1203 return true;
bd16acf3 1204
c3350683
LP
1205 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
1206 return false;
bd16acf3 1207
bd16acf3 1208 return r;
c3350683 1209 }
bd16acf3 1210
c3350683
LP
1211 /* We don't actually care about the state really. The fact
1212 * that we could read the job state is enough for us */
bd16acf3 1213
c3350683 1214 return true;
bd16acf3 1215}
ab49725f
KS
1216
1217int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
1218 _cleanup_free_ char *unit = NULL;
1219 Machine *mm;
1220 int r;
1221
1222 assert(m);
1223 assert(pid >= 1);
1224 assert(machine);
1225
1226 r = cg_pid_get_unit(pid, &unit);
1227 if (r < 0)
1228 mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
1229 else
1230 mm = hashmap_get(m->machine_units, unit);
1231
1232 if (!mm)
1233 return 0;
1234
1235 *machine = mm;
1236 return 1;
1237}
1238
1239int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
1240 Machine *machine;
1241
1242 assert(m);
1243 assert(name);
1244
1245 machine = hashmap_get(m->machines, name);
1246 if (!machine) {
1247 machine = machine_new(m, name);
1248 if (!machine)
1249 return -ENOMEM;
1250 }
1251
1252 if (_machine)
1253 *_machine = machine;
1254
1255 return 0;
1256}