chiark / gitweb /
machine: properly distuingish created and registered machines
[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
227         if (!isempty(service)) {
228                 m->service = strdup(service);
229                 if (!m->service) {
230                         r = -ENOMEM;
231                         goto fail;
232                 }
233         }
234
235         if (!isempty(root_directory)) {
236                 m->root_directory = strdup(root_directory);
237                 if (!m->root_directory) {
238                         r = -ENOMEM;
239                         goto fail;
240                 }
241         }
242
243         *_m = m;
244
245         return 1;
246
247 fail:
248         machine_add_to_gc_queue(m);
249         return r;
250 }
251
252 static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
253         Manager *manager = userdata;
254         Machine *m = NULL;
255         int r;
256
257         r = method_create_or_register_machine(manager, message, &m, error);
258         if (r < 0)
259                 return r;
260
261         r = sd_bus_message_enter_container(message, 'a', "(sv)");
262         if (r < 0)
263                 goto fail;
264
265         r = machine_start(m, message, error);
266         if (r < 0)
267                 goto fail;
268
269         m->create_message = sd_bus_message_ref(message);
270         return 1;
271
272 fail:
273         machine_add_to_gc_queue(m);
274         return r;
275 }
276
277 static int method_register_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
278         Manager *manager = userdata;
279         _cleanup_free_ char *p = NULL;
280         Machine *m = NULL;
281         int r;
282
283         r = method_create_or_register_machine(manager, message, &m, error);
284         if (r < 0)
285                 return r;
286
287         r = cg_pid_get_unit(m->leader, &m->unit);
288         if (r < 0) {
289                 r = sd_bus_error_set_errnof(error, r, "Failed to determine unit of process "PID_FMT" : %s", m->leader, strerror(-r));
290                 goto fail;
291         }
292
293         m->registered = true;
294
295         r = machine_start(m, NULL, error);
296         if (r < 0)
297                 goto fail;
298
299         p = machine_bus_path(m);
300         if (!p) {
301                 r = -ENOMEM;
302                 goto fail;
303         }
304
305         return sd_bus_reply_method_return(message, "o", p);
306
307 fail:
308         machine_add_to_gc_queue(m);
309         return r;
310 }
311
312 static int method_terminate_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
313         Manager *m = userdata;
314         Machine *machine;
315         const char *name;
316         int r;
317
318         assert(bus);
319         assert(message);
320         assert(m);
321
322         r = sd_bus_message_read(message, "s", &name);
323         if (r < 0)
324                 return sd_bus_error_set_errno(error, r);
325
326         machine = hashmap_get(m->machines, name);
327         if (!machine)
328                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
329
330         return bus_machine_method_terminate(bus, message, machine, error);
331 }
332
333 static int method_kill_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
334         Manager *m = userdata;
335         Machine *machine;
336         const char *name;
337         int r;
338
339         assert(bus);
340         assert(message);
341         assert(m);
342
343         r = sd_bus_message_read(message, "s", &name);
344         if (r < 0)
345                 return sd_bus_error_set_errno(error, r);
346
347         machine = hashmap_get(m->machines, name);
348         if (!machine)
349                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
350
351         return bus_machine_method_kill(bus, message, machine, error);
352 }
353
354 static int method_get_machine_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
355         Manager *m = userdata;
356         Machine *machine;
357         const char *name;
358         int r;
359
360         assert(bus);
361         assert(message);
362         assert(m);
363
364         r = sd_bus_message_read(message, "s", &name);
365         if (r < 0)
366                 return sd_bus_error_set_errno(error, r);
367
368         machine = hashmap_get(m->machines, name);
369         if (!machine)
370                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
371
372         return bus_machine_method_get_addresses(bus, message, machine, error);
373 }
374
375 static int method_get_machine_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
376         Manager *m = userdata;
377         Machine *machine;
378         const char *name;
379         int r;
380
381         assert(bus);
382         assert(message);
383         assert(m);
384
385         r = sd_bus_message_read(message, "s", &name);
386         if (r < 0)
387                 return sd_bus_error_set_errno(error, r);
388
389         machine = hashmap_get(m->machines, name);
390         if (!machine)
391                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
392
393         return bus_machine_method_get_os_release(bus, message, machine, error);
394 }
395
396 const sd_bus_vtable manager_vtable[] = {
397         SD_BUS_VTABLE_START(0),
398         SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED),
399         SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
400         SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED),
401         SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0),
402         SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0),
403         SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
404         SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
405         SD_BUS_METHOD("GetMachineAddresses", "s", "a(yay)", method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
406         SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
407         SD_BUS_SIGNAL("MachineNew", "so", 0),
408         SD_BUS_SIGNAL("MachineRemoved", "so", 0),
409         SD_BUS_VTABLE_END
410 };
411
412 int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
413         const char *path, *result, *unit;
414         Manager *m = userdata;
415         Machine *machine;
416         uint32_t id;
417         int r;
418
419         assert(bus);
420         assert(message);
421         assert(m);
422
423         r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result);
424         if (r < 0) {
425                 bus_log_parse_error(r);
426                 return r;
427         }
428
429         machine = hashmap_get(m->machine_units, unit);
430         if (!machine)
431                 return 0;
432
433         if (streq_ptr(path, machine->scope_job)) {
434                 free(machine->scope_job);
435                 machine->scope_job = NULL;
436
437                 if (machine->started) {
438                         if (streq(result, "done"))
439                                 machine_send_create_reply(machine, NULL);
440                         else {
441                                 _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
442
443                                 sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
444
445                                 machine_send_create_reply(machine, &e);
446                         }
447                 } else
448                         machine_save(machine);
449         }
450
451         machine_add_to_gc_queue(machine);
452         return 0;
453 }
454
455 int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
456         _cleanup_free_ char *unit = NULL;
457         Manager *m = userdata;
458         Machine *machine;
459         const char *path;
460         int r;
461
462         assert(bus);
463         assert(message);
464         assert(m);
465
466         path = sd_bus_message_get_path(message);
467         if (!path)
468                 return 0;
469
470         r = unit_name_from_dbus_path(path, &unit);
471         if (r < 0)
472                 return r;
473
474         machine = hashmap_get(m->machine_units, unit);
475         if (machine)
476                 machine_add_to_gc_queue(machine);
477
478         return 0;
479 }
480
481 int match_unit_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
482         const char *path, *unit;
483         Manager *m = userdata;
484         Machine *machine;
485         int r;
486
487         assert(bus);
488         assert(message);
489         assert(m);
490
491         r = sd_bus_message_read(message, "so", &unit, &path);
492         if (r < 0) {
493                 bus_log_parse_error(r);
494                 return r;
495         }
496
497         machine = hashmap_get(m->machine_units, unit);
498         if (machine)
499                 machine_add_to_gc_queue(machine);
500
501         return 0;
502 }
503
504 int match_reloading(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
505         Manager *m = userdata;
506         Machine *machine;
507         Iterator i;
508         int b, r;
509
510         assert(bus);
511
512         r = sd_bus_message_read(message, "b", &b);
513         if (r < 0) {
514                 bus_log_parse_error(r);
515                 return r;
516         }
517         if (b)
518                 return 0;
519
520         /* systemd finished reloading, let's recheck all our machines */
521         log_debug("System manager has been reloaded, rechecking machines...");
522
523         HASHMAP_FOREACH(machine, m->machines, i)
524                 machine_add_to_gc_queue(machine);
525
526         return 0;
527 }
528
529 int manager_start_scope(
530                 Manager *manager,
531                 const char *scope,
532                 pid_t pid,
533                 const char *slice,
534                 const char *description,
535                 sd_bus_message *more_properties,
536                 sd_bus_error *error,
537                 char **job) {
538
539         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
540         int r;
541
542         assert(manager);
543         assert(scope);
544         assert(pid > 1);
545
546         r = sd_bus_message_new_method_call(
547                         manager->bus,
548                         &m,
549                         "org.freedesktop.systemd1",
550                         "/org/freedesktop/systemd1",
551                         "org.freedesktop.systemd1.Manager",
552                         "StartTransientUnit");
553         if (r < 0)
554                 return r;
555
556         r = sd_bus_message_append(m, "ss", strempty(scope), "fail");
557         if (r < 0)
558                 return r;
559
560         r = sd_bus_message_open_container(m, 'a', "(sv)");
561         if (r < 0)
562                 return r;
563
564         if (!isempty(slice)) {
565                 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
566                 if (r < 0)
567                         return r;
568         }
569
570         if (!isempty(description)) {
571                 r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
572                 if (r < 0)
573                         return r;
574         }
575
576         r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
577         if (r < 0)
578                 return r;
579
580         if (more_properties) {
581                 r = sd_bus_message_copy(m, more_properties, true);
582                 if (r < 0)
583                         return r;
584         }
585
586         r = sd_bus_message_close_container(m);
587         if (r < 0)
588                 return r;
589
590         r = sd_bus_message_append(m, "a(sa(sv))", 0);
591         if (r < 0)
592                 return r;
593
594         r = sd_bus_call(manager->bus, m, 0, error, &reply);
595         if (r < 0)
596                 return r;
597
598         if (job) {
599                 const char *j;
600                 char *copy;
601
602                 r = sd_bus_message_read(reply, "o", &j);
603                 if (r < 0)
604                         return r;
605
606                 copy = strdup(j);
607                 if (!copy)
608                         return -ENOMEM;
609
610                 *job = copy;
611         }
612
613         return 1;
614 }
615
616 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
617         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
618         int r;
619
620         assert(manager);
621         assert(unit);
622
623         r = sd_bus_call_method(
624                         manager->bus,
625                         "org.freedesktop.systemd1",
626                         "/org/freedesktop/systemd1",
627                         "org.freedesktop.systemd1.Manager",
628                         "StopUnit",
629                         error,
630                         &reply,
631                         "ss", unit, "fail");
632         if (r < 0) {
633                 if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
634                     sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
635
636                         if (job)
637                                 *job = NULL;
638
639                         sd_bus_error_free(error);
640                         return 0;
641                 }
642
643                 return r;
644         }
645
646         if (job) {
647                 const char *j;
648                 char *copy;
649
650                 r = sd_bus_message_read(reply, "o", &j);
651                 if (r < 0)
652                         return r;
653
654                 copy = strdup(j);
655                 if (!copy)
656                         return -ENOMEM;
657
658                 *job = copy;
659         }
660
661         return 1;
662 }
663
664 int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_error *error) {
665         assert(manager);
666         assert(unit);
667
668         return sd_bus_call_method(
669                         manager->bus,
670                         "org.freedesktop.systemd1",
671                         "/org/freedesktop/systemd1",
672                         "org.freedesktop.systemd1.Manager",
673                         "KillUnit",
674                         error,
675                         NULL,
676                         "ssi", unit, "all", signo);
677 }
678
679 int manager_unit_is_active(Manager *manager, const char *unit) {
680         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
681         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
682         _cleanup_free_ char *path = NULL;
683         const char *state;
684         int r;
685
686         assert(manager);
687         assert(unit);
688
689         path = unit_dbus_path_from_name(unit);
690         if (!path)
691                 return -ENOMEM;
692
693         r = sd_bus_get_property(
694                         manager->bus,
695                         "org.freedesktop.systemd1",
696                         path,
697                         "org.freedesktop.systemd1.Unit",
698                         "ActiveState",
699                         &error,
700                         &reply,
701                         "s");
702         if (r < 0) {
703                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
704                     sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
705                         return true;
706
707                 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
708                     sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED))
709                         return false;
710
711                 return r;
712         }
713
714         r = sd_bus_message_read(reply, "s", &state);
715         if (r < 0)
716                 return -EINVAL;
717
718         return !streq(state, "inactive") && !streq(state, "failed");
719 }
720
721 int manager_job_is_active(Manager *manager, const char *path) {
722         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
723         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
724         int r;
725
726         assert(manager);
727         assert(path);
728
729         r = sd_bus_get_property(
730                         manager->bus,
731                         "org.freedesktop.systemd1",
732                         path,
733                         "org.freedesktop.systemd1.Job",
734                         "State",
735                         &error,
736                         &reply,
737                         "s");
738         if (r < 0) {
739                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
740                     sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
741                         return true;
742
743                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
744                         return false;
745
746                 return r;
747         }
748
749         /* We don't actually care about the state really. The fact
750          * that we could read the job state is enough for us */
751
752         return true;
753 }
754
755 int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
756         _cleanup_free_ char *unit = NULL;
757         Machine *mm;
758         int r;
759
760         assert(m);
761         assert(pid >= 1);
762         assert(machine);
763
764         r = cg_pid_get_unit(pid, &unit);
765         if (r < 0)
766                 mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
767         else
768                 mm = hashmap_get(m->machine_units, unit);
769
770         if (!mm)
771                 return 0;
772
773         *machine = mm;
774         return 1;
775 }
776
777 int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
778         Machine *machine;
779
780         assert(m);
781         assert(name);
782
783         machine = hashmap_get(m->machines, name);
784         if (!machine) {
785                 machine = machine_new(m, name);
786                 if (!machine)
787                         return -ENOMEM;
788         }
789
790         if (_machine)
791                 *_machine = machine;
792
793         return 0;
794 }