chiark / gitweb /
machinectl: show /etc/os-release information of container in status output
[elogind.git] / src / machine / machined-dbus.c
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>
25 #include <pwd.h>
26 #include <sys/capability.h>
27
28 #include "sd-id128.h"
29 #include "sd-messages.h"
30 #include "strv.h"
31 #include "mkdir.h"
32 #include "path-util.h"
33 #include "special.h"
34 #include "fileio-label.h"
35 #include "label.h"
36 #include "utf8.h"
37 #include "unit-name.h"
38 #include "bus-util.h"
39 #include "bus-errors.h"
40 #include "time-util.h"
41 #include "cgroup-util.h"
42 #include "machined.h"
43
44 static int method_get_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
45         _cleanup_free_ char *p = NULL;
46         Manager *m = userdata;
47         Machine *machine;
48         const char *name;
49         int r;
50
51         assert(bus);
52         assert(message);
53         assert(m);
54
55         r = sd_bus_message_read(message, "s", &name);
56         if (r < 0)
57                 return r;
58
59         machine = hashmap_get(m->machines, name);
60         if (!machine)
61                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
62
63         p = machine_bus_path(machine);
64         if (!p)
65                 return -ENOMEM;
66
67         return sd_bus_reply_method_return(message, "o", p);
68 }
69
70 static int method_get_machine_by_pid(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
71         _cleanup_free_ char *p = NULL;
72         Manager *m = userdata;
73         Machine *machine = NULL;
74         pid_t pid;
75         int r;
76
77         assert(bus);
78         assert(message);
79         assert(m);
80
81         assert_cc(sizeof(pid_t) == sizeof(uint32_t));
82
83         r = sd_bus_message_read(message, "u", &pid);
84         if (r < 0)
85                 return r;
86
87         if (pid == 0) {
88                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
89
90                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
91                 if (r < 0)
92                         return r;
93
94                 r = sd_bus_creds_get_pid(creds, &pid);
95                 if (r < 0)
96                         return r;
97         }
98
99         r = manager_get_machine_by_pid(m, pid, &machine);
100         if (r < 0)
101                 return r;
102         if (!machine)
103                 return sd_bus_error_setf(error, BUS_ERROR_NO_MACHINE_FOR_PID, "PID "PID_FMT" does not belong to any known machine", pid);
104
105         p = machine_bus_path(machine);
106         if (!p)
107                 return -ENOMEM;
108
109         return sd_bus_reply_method_return(message, "o", p);
110 }
111
112 static int method_list_machines(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
113         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
114         Manager *m = userdata;
115         Machine *machine;
116         Iterator i;
117         int r;
118
119         assert(bus);
120         assert(message);
121         assert(m);
122
123         r = sd_bus_message_new_method_return(message, &reply);
124         if (r < 0)
125                 return sd_bus_error_set_errno(error, r);
126
127         r = sd_bus_message_open_container(reply, 'a', "(ssso)");
128         if (r < 0)
129                 return sd_bus_error_set_errno(error, r);
130
131         HASHMAP_FOREACH(machine, m->machines, i) {
132                 _cleanup_free_ char *p = NULL;
133
134                 p = machine_bus_path(machine);
135                 if (!p)
136                         return -ENOMEM;
137
138                 r = sd_bus_message_append(reply, "(ssso)",
139                                           machine->name,
140                                           strempty(machine_class_to_string(machine->class)),
141                                           machine->service,
142                                           p);
143                 if (r < 0)
144                         return sd_bus_error_set_errno(error, r);
145         }
146
147         r = sd_bus_message_close_container(reply);
148         if (r < 0)
149                 return sd_bus_error_set_errno(error, r);
150
151         return sd_bus_send(bus, reply, NULL);
152 }
153
154 static int method_create_or_register_machine(Manager *manager, sd_bus_message *message, Machine **_m, sd_bus_error *error) {
155         const char *name, *service, *class, *root_directory;
156         MachineClass c;
157         uint32_t leader;
158         sd_id128_t id;
159         const void *v;
160         Machine *m;
161         size_t n;
162         int r;
163
164         assert(manager);
165         assert(message);
166         assert(_m);
167
168         r = sd_bus_message_read(message, "s", &name);
169         if (r < 0)
170                 return r;
171         if (!machine_name_is_valid(name))
172                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name");
173
174         r = sd_bus_message_read_array(message, 'y', &v, &n);
175         if (r < 0)
176                 return r;
177         if (n == 0)
178                 id = SD_ID128_NULL;
179         else if (n == 16)
180                 memcpy(&id, v, n);
181         else
182                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine ID parameter");
183
184         r = sd_bus_message_read(message, "ssus", &service, &class, &leader, &root_directory);
185         if (r < 0)
186                 return r;
187
188         if (isempty(class))
189                 c = _MACHINE_CLASS_INVALID;
190         else {
191                 c = machine_class_from_string(class);
192                 if (c < 0)
193                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine class parameter");
194         }
195
196         if (leader == 1)
197                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
198
199         if (!isempty(root_directory) && !path_is_absolute(root_directory))
200                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path");
201
202         if (leader == 0) {
203                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
204
205                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
206                 if (r < 0)
207                         return r;
208
209                 assert_cc(sizeof(uint32_t) == sizeof(pid_t));
210
211                 r = sd_bus_creds_get_pid(creds, (pid_t*) &leader);
212                 if (r < 0)
213                         return r;
214         }
215
216         if (hashmap_get(manager->machines, name))
217                 return sd_bus_error_setf(error, BUS_ERROR_MACHINE_EXISTS, "Machine '%s' already exists", name);
218
219         r = manager_add_machine(manager, name, &m);
220         if (r < 0)
221                 return r;
222
223         m->leader = leader;
224         m->class = c;
225         m->id = id;
226         m->registered = true;
227
228         if (!isempty(service)) {
229                 m->service = strdup(service);
230                 if (!m->service) {
231                         r = -ENOMEM;
232                         goto fail;
233                 }
234         }
235
236         if (!isempty(root_directory)) {
237                 m->root_directory = strdup(root_directory);
238                 if (!m->root_directory) {
239                         r = -ENOMEM;
240                         goto fail;
241                 }
242         }
243
244         *_m = m;
245
246         return 1;
247
248 fail:
249         machine_add_to_gc_queue(m);
250         return r;
251 }
252
253 static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
254         Manager *manager = userdata;
255         Machine *m = NULL;
256         int r;
257
258         r = method_create_or_register_machine(manager, message, &m, error);
259         if (r < 0)
260                 return r;
261
262         r = sd_bus_message_enter_container(message, 'a', "(sv)");
263         if (r < 0)
264                 goto fail;
265
266         r = machine_start(m, message, error);
267         if (r < 0)
268                 goto fail;
269
270         m->create_message = sd_bus_message_ref(message);
271         return 1;
272
273 fail:
274         machine_add_to_gc_queue(m);
275         return r;
276 }
277
278 static int method_register_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
279         Manager *manager = userdata;
280         _cleanup_free_ char *p = NULL;
281         Machine *m = NULL;
282         int r;
283
284         r = method_create_or_register_machine(manager, message, &m, error);
285         if (r < 0)
286                 return r;
287
288         r = cg_pid_get_unit(m->leader, &m->unit);
289         if (r < 0) {
290                 r = sd_bus_error_set_errnof(error, r, "Failed to determine unit of process "PID_FMT" : %s", m->leader, strerror(-r));
291                 goto fail;
292         }
293
294         r = machine_start(m, NULL, error);
295         if (r < 0)
296                 goto fail;
297
298         p = machine_bus_path(m);
299         if (!p) {
300                 r = -ENOMEM;
301                 goto fail;
302         }
303
304         return sd_bus_reply_method_return(message, "o", p);
305
306 fail:
307         machine_add_to_gc_queue(m);
308         return r;
309 }
310
311 static int method_terminate_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
312         Manager *m = userdata;
313         Machine *machine;
314         const char *name;
315         int r;
316
317         assert(bus);
318         assert(message);
319         assert(m);
320
321         r = sd_bus_message_read(message, "s", &name);
322         if (r < 0)
323                 return sd_bus_error_set_errno(error, r);
324
325         machine = hashmap_get(m->machines, name);
326         if (!machine)
327                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
328
329         return bus_machine_method_terminate(bus, message, machine, error);
330 }
331
332 static int method_kill_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
333         Manager *m = userdata;
334         Machine *machine;
335         const char *name;
336         int r;
337
338         assert(bus);
339         assert(message);
340         assert(m);
341
342         r = sd_bus_message_read(message, "s", &name);
343         if (r < 0)
344                 return sd_bus_error_set_errno(error, r);
345
346         machine = hashmap_get(m->machines, name);
347         if (!machine)
348                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
349
350         return bus_machine_method_kill(bus, message, machine, error);
351 }
352
353 static int method_get_machine_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
354         Manager *m = userdata;
355         Machine *machine;
356         const char *name;
357         int r;
358
359         assert(bus);
360         assert(message);
361         assert(m);
362
363         r = sd_bus_message_read(message, "s", &name);
364         if (r < 0)
365                 return sd_bus_error_set_errno(error, r);
366
367         machine = hashmap_get(m->machines, name);
368         if (!machine)
369                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
370
371         return bus_machine_method_get_addresses(bus, message, machine, error);
372 }
373
374 static int method_get_machine_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
375         Manager *m = userdata;
376         Machine *machine;
377         const char *name;
378         int r;
379
380         assert(bus);
381         assert(message);
382         assert(m);
383
384         r = sd_bus_message_read(message, "s", &name);
385         if (r < 0)
386                 return sd_bus_error_set_errno(error, r);
387
388         machine = hashmap_get(m->machines, name);
389         if (!machine)
390                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
391
392         return bus_machine_method_get_os_release(bus, message, machine, error);
393 }
394
395 const sd_bus_vtable manager_vtable[] = {
396         SD_BUS_VTABLE_START(0),
397         SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED),
398         SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
399         SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED),
400         SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0),
401         SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0),
402         SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
403         SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
404         SD_BUS_METHOD("GetMachineAddresses", "s", "a(yay)", method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
405         SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
406         SD_BUS_SIGNAL("MachineNew", "so", 0),
407         SD_BUS_SIGNAL("MachineRemoved", "so", 0),
408         SD_BUS_VTABLE_END
409 };
410
411 int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
412         const char *path, *result, *unit;
413         Manager *m = userdata;
414         Machine *machine;
415         uint32_t id;
416         int r;
417
418         assert(bus);
419         assert(message);
420         assert(m);
421
422         r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result);
423         if (r < 0) {
424                 bus_log_parse_error(r);
425                 return r;
426         }
427
428         machine = hashmap_get(m->machine_units, unit);
429         if (!machine)
430                 return 0;
431
432         if (streq_ptr(path, machine->scope_job)) {
433                 free(machine->scope_job);
434                 machine->scope_job = NULL;
435
436                 if (machine->started) {
437                         if (streq(result, "done"))
438                                 machine_send_create_reply(machine, NULL);
439                         else {
440                                 _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
441
442                                 sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
443
444                                 machine_send_create_reply(machine, &e);
445                         }
446                 } else
447                         machine_save(machine);
448         }
449
450         machine_add_to_gc_queue(machine);
451         return 0;
452 }
453
454 int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
455         _cleanup_free_ char *unit = NULL;
456         Manager *m = userdata;
457         Machine *machine;
458         const char *path;
459         int r;
460
461         assert(bus);
462         assert(message);
463         assert(m);
464
465         path = sd_bus_message_get_path(message);
466         if (!path)
467                 return 0;
468
469         r = unit_name_from_dbus_path(path, &unit);
470         if (r < 0)
471                 return r;
472
473         machine = hashmap_get(m->machine_units, unit);
474         if (machine)
475                 machine_add_to_gc_queue(machine);
476
477         return 0;
478 }
479
480 int match_unit_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
481         const char *path, *unit;
482         Manager *m = userdata;
483         Machine *machine;
484         int r;
485
486         assert(bus);
487         assert(message);
488         assert(m);
489
490         r = sd_bus_message_read(message, "so", &unit, &path);
491         if (r < 0) {
492                 bus_log_parse_error(r);
493                 return r;
494         }
495
496         machine = hashmap_get(m->machine_units, unit);
497         if (machine)
498                 machine_add_to_gc_queue(machine);
499
500         return 0;
501 }
502
503 int match_reloading(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
504         Manager *m = userdata;
505         Machine *machine;
506         Iterator i;
507         int b, r;
508
509         assert(bus);
510
511         r = sd_bus_message_read(message, "b", &b);
512         if (r < 0) {
513                 bus_log_parse_error(r);
514                 return r;
515         }
516         if (b)
517                 return 0;
518
519         /* systemd finished reloading, let's recheck all our machines */
520         log_debug("System manager has been reloaded, rechecking machines...");
521
522         HASHMAP_FOREACH(machine, m->machines, i)
523                 machine_add_to_gc_queue(machine);
524
525         return 0;
526 }
527
528 int manager_start_scope(
529                 Manager *manager,
530                 const char *scope,
531                 pid_t pid,
532                 const char *slice,
533                 const char *description,
534                 sd_bus_message *more_properties,
535                 sd_bus_error *error,
536                 char **job) {
537
538         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
539         int r;
540
541         assert(manager);
542         assert(scope);
543         assert(pid > 1);
544
545         r = sd_bus_message_new_method_call(
546                         manager->bus,
547                         &m,
548                         "org.freedesktop.systemd1",
549                         "/org/freedesktop/systemd1",
550                         "org.freedesktop.systemd1.Manager",
551                         "StartTransientUnit");
552         if (r < 0)
553                 return r;
554
555         r = sd_bus_message_append(m, "ss", strempty(scope), "fail");
556         if (r < 0)
557                 return r;
558
559         r = sd_bus_message_open_container(m, 'a', "(sv)");
560         if (r < 0)
561                 return r;
562
563         if (!isempty(slice)) {
564                 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
565                 if (r < 0)
566                         return r;
567         }
568
569         if (!isempty(description)) {
570                 r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
571                 if (r < 0)
572                         return r;
573         }
574
575         r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
576         if (r < 0)
577                 return r;
578
579         if (more_properties) {
580                 r = sd_bus_message_copy(m, more_properties, true);
581                 if (r < 0)
582                         return r;
583         }
584
585         r = sd_bus_message_close_container(m);
586         if (r < 0)
587                 return r;
588
589         r = sd_bus_message_append(m, "a(sa(sv))", 0);
590         if (r < 0)
591                 return r;
592
593         r = sd_bus_call(manager->bus, m, 0, error, &reply);
594         if (r < 0)
595                 return r;
596
597         if (job) {
598                 const char *j;
599                 char *copy;
600
601                 r = sd_bus_message_read(reply, "o", &j);
602                 if (r < 0)
603                         return r;
604
605                 copy = strdup(j);
606                 if (!copy)
607                         return -ENOMEM;
608
609                 *job = copy;
610         }
611
612         return 1;
613 }
614
615 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
616         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
617         int r;
618
619         assert(manager);
620         assert(unit);
621
622         r = sd_bus_call_method(
623                         manager->bus,
624                         "org.freedesktop.systemd1",
625                         "/org/freedesktop/systemd1",
626                         "org.freedesktop.systemd1.Manager",
627                         "StopUnit",
628                         error,
629                         &reply,
630                         "ss", unit, "fail");
631         if (r < 0) {
632                 if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
633                     sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
634
635                         if (job)
636                                 *job = NULL;
637
638                         sd_bus_error_free(error);
639                         return 0;
640                 }
641
642                 return r;
643         }
644
645         if (job) {
646                 const char *j;
647                 char *copy;
648
649                 r = sd_bus_message_read(reply, "o", &j);
650                 if (r < 0)
651                         return r;
652
653                 copy = strdup(j);
654                 if (!copy)
655                         return -ENOMEM;
656
657                 *job = copy;
658         }
659
660         return 1;
661 }
662
663 int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_error *error) {
664         assert(manager);
665         assert(unit);
666
667         return sd_bus_call_method(
668                         manager->bus,
669                         "org.freedesktop.systemd1",
670                         "/org/freedesktop/systemd1",
671                         "org.freedesktop.systemd1.Manager",
672                         "KillUnit",
673                         error,
674                         NULL,
675                         "ssi", unit, "all", signo);
676 }
677
678 int manager_unit_is_active(Manager *manager, const char *unit) {
679         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
680         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
681         _cleanup_free_ char *path = NULL;
682         const char *state;
683         int r;
684
685         assert(manager);
686         assert(unit);
687
688         path = unit_dbus_path_from_name(unit);
689         if (!path)
690                 return -ENOMEM;
691
692         r = sd_bus_get_property(
693                         manager->bus,
694                         "org.freedesktop.systemd1",
695                         path,
696                         "org.freedesktop.systemd1.Unit",
697                         "ActiveState",
698                         &error,
699                         &reply,
700                         "s");
701         if (r < 0) {
702                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
703                     sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
704                         return true;
705
706                 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
707                     sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED))
708                         return false;
709
710                 return r;
711         }
712
713         r = sd_bus_message_read(reply, "s", &state);
714         if (r < 0)
715                 return -EINVAL;
716
717         return !streq(state, "inactive") && !streq(state, "failed");
718 }
719
720 int manager_job_is_active(Manager *manager, const char *path) {
721         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
722         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
723         int r;
724
725         assert(manager);
726         assert(path);
727
728         r = sd_bus_get_property(
729                         manager->bus,
730                         "org.freedesktop.systemd1",
731                         path,
732                         "org.freedesktop.systemd1.Job",
733                         "State",
734                         &error,
735                         &reply,
736                         "s");
737         if (r < 0) {
738                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
739                     sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
740                         return true;
741
742                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
743                         return false;
744
745                 return r;
746         }
747
748         /* We don't actually care about the state really. The fact
749          * that we could read the job state is enough for us */
750
751         return true;
752 }
753
754 int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
755         _cleanup_free_ char *unit = NULL;
756         Machine *mm;
757         int r;
758
759         assert(m);
760         assert(pid >= 1);
761         assert(machine);
762
763         r = cg_pid_get_unit(pid, &unit);
764         if (r < 0)
765                 mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
766         else
767                 mm = hashmap_get(m->machine_units, unit);
768
769         if (!mm)
770                 return 0;
771
772         *machine = mm;
773         return 1;
774 }
775
776 int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
777         Machine *machine;
778
779         assert(m);
780         assert(name);
781
782         machine = hashmap_get(m->machines, name);
783         if (!machine) {
784                 machine = machine_new(m, name);
785                 if (!machine)
786                         return -ENOMEM;
787         }
788
789         if (_machine)
790                 *_machine = machine;
791
792         return 0;
793 }