]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machine.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / machine / machine.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
9444b1f2
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2011 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
66cb2fde 21#include <errno.h>
9444b1f2
LP
22#include <string.h>
23#include <unistd.h>
9444b1f2 24
c3350683 25#include "sd-messages.h"
fb6becb4 26
b5efdb8a 27#include "alloc-util.h"
66cb2fde
LP
28#include "bus-error.h"
29#include "bus-util.h"
4f5dd394 30#include "escape.h"
cf0fbc49 31#include "extract-word.h"
3ffd4af2 32#include "fd-util.h"
9444b1f2 33#include "fileio.h"
f97b34a6 34#include "format-util.h"
66cb2fde 35#include "hashmap.h"
4f5dd394 36#include "machine-dbus.h"
3ffd4af2 37#include "machine.h"
66cb2fde 38#include "mkdir.h"
6bedfcbb 39#include "parse-util.h"
4a0b58c4 40#include "process-util.h"
9444b1f2 41#include "special.h"
3401419b 42#include "stdio-util.h"
8b43440b 43#include "string-table.h"
66cb2fde 44#include "terminal-util.h"
fb6becb4 45#include "unit-name.h"
66cb2fde 46#include "util.h"
9444b1f2 47
fbe55073 48Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
9444b1f2
LP
49 Machine *m;
50
51 assert(manager);
fbe55073 52 assert(class < _MACHINE_CLASS_MAX);
9444b1f2
LP
53 assert(name);
54
fbe55073
LP
55 /* Passing class == _MACHINE_CLASS_INVALID here is fine. It
56 * means as much as "we don't know yet", and that we'll figure
57 * it out later when loading the state file. */
58
9444b1f2
LP
59 m = new0(Machine, 1);
60 if (!m)
61 return NULL;
62
63 m->name = strdup(name);
64 if (!m->name)
65 goto fail;
66
fbe55073
LP
67 if (class != MACHINE_HOST) {
68 m->state_file = strappend("/run/systemd/machines/", m->name);
69 if (!m->state_file)
70 goto fail;
71 }
72
73 m->class = class;
9444b1f2
LP
74
75 if (hashmap_put(manager->machines, m->name, m) < 0)
76 goto fail;
77
9444b1f2
LP
78 m->manager = manager;
79
80 return m;
81
82fail:
83 free(m->state_file);
84 free(m->name);
6b430fdb 85 return mfree(m);
9444b1f2
LP
86}
87
88void machine_free(Machine *m) {
89 assert(m);
90
0370612e 91 while (m->operations)
795c5d31 92 operation_free(m->operations);
0370612e 93
9444b1f2 94 if (m->in_gc_queue)
71fda00f 95 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2 96
9b420b3c 97 machine_release_unit(m);
9444b1f2 98
fb6becb4
LP
99 free(m->scope_job);
100
9b420b3c 101 (void) hashmap_remove(m->manager->machines, m->name);
9444b1f2 102
fbe55073
LP
103 if (m->manager->host_machine == m)
104 m->manager->host_machine = NULL;
105
d3e84ddb 106 if (m->leader > 0)
4a0b58c4 107 (void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
d3e84ddb 108
c3350683 109 sd_bus_message_unref(m->create_message);
fb6becb4 110
9444b1f2
LP
111 free(m->name);
112 free(m->state_file);
113 free(m->service);
9444b1f2 114 free(m->root_directory);
9b5ed6fe 115 free(m->netif);
9444b1f2
LP
116 free(m);
117}
118
119int machine_save(Machine *m) {
120 _cleanup_free_ char *temp_path = NULL;
121 _cleanup_fclose_ FILE *f = NULL;
122 int r;
123
124 assert(m);
fbe55073
LP
125
126 if (!m->state_file)
127 return 0;
9444b1f2
LP
128
129 if (!m->started)
130 return 0;
131
c31ad024 132 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0, false);
9444b1f2 133 if (r < 0)
dacd6cee 134 goto fail;
9444b1f2
LP
135
136 r = fopen_temporary(m->state_file, &f, &temp_path);
137 if (r < 0)
dacd6cee 138 goto fail;
9444b1f2 139
dacd6cee 140 (void) fchmod(fileno(f), 0644);
9444b1f2
LP
141
142 fprintf(f,
143 "# This is private data. Do not parse.\n"
144 "NAME=%s\n",
145 m->name);
146
ca5405bb
LP
147 if (m->unit) {
148 _cleanup_free_ char *escaped;
149
150 escaped = cescape(m->unit);
151 if (!escaped) {
152 r = -ENOMEM;
dacd6cee 153 goto fail;
ca5405bb
LP
154 }
155
156 fprintf(f, "SCOPE=%s\n", escaped); /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */
157 }
fb6becb4
LP
158
159 if (m->scope_job)
160 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
9444b1f2 161
ca5405bb
LP
162 if (m->service) {
163 _cleanup_free_ char *escaped;
9444b1f2 164
ca5405bb
LP
165 escaped = cescape(m->service);
166 if (!escaped) {
167 r = -ENOMEM;
dacd6cee 168 goto fail;
ca5405bb
LP
169 }
170 fprintf(f, "SERVICE=%s\n", escaped);
171 }
172
173 if (m->root_directory) {
174 _cleanup_free_ char *escaped;
175
176 escaped = cescape(m->root_directory);
177 if (!escaped) {
178 r = -ENOMEM;
dacd6cee 179 goto fail;
ca5405bb
LP
180 }
181 fprintf(f, "ROOT=%s\n", escaped);
182 }
9444b1f2 183
3bbaff3e 184 if (!sd_id128_is_null(m->id))
9444b1f2
LP
185 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
186
187 if (m->leader != 0)
90b2de37 188 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
9444b1f2
LP
189
190 if (m->class != _MACHINE_CLASS_INVALID)
191 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
192
193 if (dual_timestamp_is_set(&m->timestamp))
194 fprintf(f,
90b2de37
ZJS
195 "REALTIME="USEC_FMT"\n"
196 "MONOTONIC="USEC_FMT"\n",
197 m->timestamp.realtime,
198 m->timestamp.monotonic);
9444b1f2 199
9b5ed6fe
LP
200 if (m->n_netif > 0) {
201 unsigned i;
202
4b61c875 203 fputs_unlocked("NETIF=", f);
9b5ed6fe
LP
204
205 for (i = 0; i < m->n_netif; i++) {
206 if (i != 0)
4b61c875 207 fputc_unlocked(' ', f);
9b5ed6fe
LP
208
209 fprintf(f, "%i", m->netif[i]);
210 }
211
4b61c875 212 fputc_unlocked('\n', f);
9b5ed6fe
LP
213 }
214
034753ac
LP
215 r = fflush_and_check(f);
216 if (r < 0)
dacd6cee 217 goto fail;
9444b1f2 218
034753ac 219 if (rename(temp_path, m->state_file) < 0) {
9444b1f2 220 r = -errno;
dacd6cee 221 goto fail;
9444b1f2
LP
222 }
223
89f7c846
LP
224 if (m->unit) {
225 char *sl;
226
227 /* Create a symlink from the unit name to the machine
228 * name, so that we can quickly find the machine for
e62d9b81 229 * each given unit. Ignore error. */
63c372cb 230 sl = strjoina("/run/systemd/machines/unit:", m->unit);
e62d9b81 231 (void) symlink(m->name, sl);
89f7c846
LP
232 }
233
dacd6cee 234 return 0;
034753ac 235
dacd6cee
LP
236fail:
237 (void) unlink(m->state_file);
238
239 if (temp_path)
240 (void) unlink(temp_path);
9444b1f2 241
dacd6cee 242 return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
9444b1f2
LP
243}
244
89f7c846
LP
245static void machine_unlink(Machine *m) {
246 assert(m);
247
248 if (m->unit) {
249
250 char *sl;
251
63c372cb 252 sl = strjoina("/run/systemd/machines/unit:", m->unit);
491ac9f2 253 (void) unlink(sl);
89f7c846
LP
254 }
255
256 if (m->state_file)
491ac9f2 257 (void) unlink(m->state_file);
89f7c846
LP
258}
259
9444b1f2 260int machine_load(Machine *m) {
9b5ed6fe 261 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
9444b1f2
LP
262 int r;
263
264 assert(m);
265
fbe55073
LP
266 if (!m->state_file)
267 return 0;
268
9444b1f2 269 r = parse_env_file(m->state_file, NEWLINE,
89f7c846 270 "SCOPE", &m->unit,
fb6becb4 271 "SCOPE_JOB", &m->scope_job,
9444b1f2 272 "SERVICE", &m->service,
9444b1f2
LP
273 "ROOT", &m->root_directory,
274 "ID", &id,
275 "LEADER", &leader,
276 "CLASS", &class,
277 "REALTIME", &realtime,
278 "MONOTONIC", &monotonic,
9b5ed6fe 279 "NETIF", &netif,
9444b1f2
LP
280 NULL);
281 if (r < 0) {
282 if (r == -ENOENT)
283 return 0;
284
8d3d7072 285 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
9444b1f2
LP
286 }
287
288 if (id)
289 sd_id128_from_string(id, &m->id);
290
291 if (leader)
292 parse_pid(leader, &m->leader);
293
294 if (class) {
295 MachineClass c;
296
297 c = machine_class_from_string(class);
298 if (c >= 0)
299 m->class = c;
300 }
301
b895a735
BR
302 if (realtime)
303 timestamp_deserialize(realtime, &m->timestamp.realtime);
304 if (monotonic)
305 timestamp_deserialize(monotonic, &m->timestamp.monotonic);
9444b1f2 306
9b5ed6fe 307 if (netif) {
75a8fd6a
SS
308 size_t allocated = 0, nr = 0;
309 const char *p;
9b5ed6fe
LP
310 int *ni = NULL;
311
75a8fd6a 312 p = netif;
9ed794a3 313 for (;;) {
75a8fd6a 314 _cleanup_free_ char *word = NULL;
9b5ed6fe
LP
315 int ifi;
316
75a8fd6a 317 r = extract_first_word(&p, &word, NULL, 0);
75a8fd6a
SS
318 if (r == 0)
319 break;
6a37c684 320 if (r == -ENOMEM)
52278ad3 321 return log_oom();
6a37c684 322 if (r < 0) {
52278ad3 323 log_warning_errno(r, "Failed to parse NETIF: %s", netif);
6a37c684 324 break;
52278ad3 325 }
75a8fd6a 326
6ad623a3 327 if (parse_ifindex(word, &ifi) < 0)
9b5ed6fe
LP
328 continue;
329
330 if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
331 free(ni);
332 return log_oom();
333 }
334
335 ni[nr++] = ifi;
336 }
337
338 free(m->netif);
339 m->netif = ni;
340 m->n_netif = nr;
341 }
342
9444b1f2
LP
343 return r;
344}
345
c3350683 346static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
2c4c73b3 347 int r = 0;
9444b1f2
LP
348
349 assert(m);
fbe55073 350 assert(m->class != MACHINE_HOST);
9444b1f2 351
89f7c846 352 if (!m->unit) {
d0af76e6 353 _cleanup_free_ char *escaped = NULL;
39883f62 354 char *scope, *description, *job = NULL;
9444b1f2 355
fb6becb4 356 escaped = unit_name_escape(m->name);
9444b1f2
LP
357 if (!escaped)
358 return log_oom();
359
605405c6 360 scope = strjoin("machine-", escaped, ".scope");
f526ab7e 361 if (!scope)
9444b1f2 362 return log_oom();
9444b1f2 363
63c372cb 364 description = strjoina(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
9444b1f2 365
c3350683 366 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
d0af76e6 367 if (r < 0) {
c3350683 368 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
d0af76e6 369 free(scope);
f2d4f98d 370 return r;
d0af76e6 371 } else {
89f7c846 372 m->unit = scope;
d0af76e6
LP
373
374 free(m->scope_job);
375 m->scope_job = job;
376 }
9444b1f2
LP
377 }
378
89f7c846
LP
379 if (m->unit)
380 hashmap_put(m->manager->machine_units, m->unit, m);
d0af76e6 381
fb6becb4 382 return r;
9444b1f2
LP
383}
384
c3350683 385int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
9444b1f2
LP
386 int r;
387
388 assert(m);
389
fbe55073
LP
390 if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
391 return -EOPNOTSUPP;
392
9444b1f2
LP
393 if (m->started)
394 return 0;
395
4a0b58c4 396 r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
d3e84ddb
LP
397 if (r < 0)
398 return r;
399
fb6becb4 400 /* Create cgroup */
c3350683 401 r = machine_start_scope(m, properties, error);
fb6becb4
LP
402 if (r < 0)
403 return r;
404
9444b1f2 405 log_struct(LOG_INFO,
2b044526 406 "MESSAGE_ID=" SD_MESSAGE_MACHINE_START_STR,
9444b1f2 407 "NAME=%s", m->name,
de0671ee 408 "LEADER="PID_FMT, m->leader,
e2cc6eca 409 LOG_MESSAGE("New machine %s.", m->name),
9444b1f2
LP
410 NULL);
411
9444b1f2
LP
412 if (!dual_timestamp_is_set(&m->timestamp))
413 dual_timestamp_get(&m->timestamp);
414
415 m->started = true;
416
417 /* Save new machine data */
418 machine_save(m);
419
420 machine_send_signal(m, true);
421
422 return 0;
423}
424
fb6becb4 425static int machine_stop_scope(Machine *m) {
4afd3348 426 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
39883f62 427 char *job = NULL;
9444b1f2 428 int r;
9444b1f2
LP
429
430 assert(m);
fbe55073 431 assert(m->class != MACHINE_HOST);
9444b1f2 432
89f7c846 433 if (!m->unit)
fb6becb4 434 return 0;
9444b1f2 435
c00a4c8f
LP
436 r = manager_stop_unit(m->manager, m->unit, &error, &job);
437 if (r < 0) {
438 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
439 return r;
fb6becb4 440 }
9444b1f2 441
fb6becb4
LP
442 free(m->scope_job);
443 m->scope_job = job;
9444b1f2 444
f14aa1f1 445 return 0;
9444b1f2
LP
446}
447
448int machine_stop(Machine *m) {
49f3fffd
LP
449 int r;
450 assert(m);
451
fbe55073
LP
452 if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
453 return -EOPNOTSUPP;
454
49f3fffd
LP
455 r = machine_stop_scope(m);
456
457 m->stopping = true;
458
459 machine_save(m);
460
461 return r;
462}
463
464int machine_finalize(Machine *m) {
9444b1f2
LP
465 assert(m);
466
467 if (m->started)
468 log_struct(LOG_INFO,
2b044526 469 "MESSAGE_ID=" SD_MESSAGE_MACHINE_STOP_STR,
9444b1f2 470 "NAME=%s", m->name,
de0671ee 471 "LEADER="PID_FMT, m->leader,
e2cc6eca 472 LOG_MESSAGE("Machine %s terminated.", m->name),
9444b1f2
LP
473 NULL);
474
89f7c846 475 machine_unlink(m);
9444b1f2
LP
476 machine_add_to_gc_queue(m);
477
49f3fffd 478 if (m->started) {
9444b1f2 479 machine_send_signal(m, false);
49f3fffd
LP
480 m->started = false;
481 }
9444b1f2 482
49f3fffd 483 return 0;
9444b1f2
LP
484}
485
a658cafa 486bool machine_check_gc(Machine *m, bool drop_not_started) {
9444b1f2
LP
487 assert(m);
488
fbe55073
LP
489 if (m->class == MACHINE_HOST)
490 return true;
491
9444b1f2 492 if (drop_not_started && !m->started)
c3350683 493 return false;
9444b1f2 494
c3350683
LP
495 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
496 return true;
9444b1f2 497
89f7c846 498 if (m->unit && manager_unit_is_active(m->manager, m->unit))
c3350683 499 return true;
9444b1f2 500
c3350683 501 return false;
9444b1f2
LP
502}
503
504void machine_add_to_gc_queue(Machine *m) {
505 assert(m);
506
507 if (m->in_gc_queue)
508 return;
509
71fda00f 510 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2
LP
511 m->in_gc_queue = true;
512}
513
fb6becb4
LP
514MachineState machine_get_state(Machine *s) {
515 assert(s);
9444b1f2 516
fbe55073
LP
517 if (s->class == MACHINE_HOST)
518 return MACHINE_RUNNING;
519
49f3fffd
LP
520 if (s->stopping)
521 return MACHINE_CLOSING;
522
fb6becb4 523 if (s->scope_job)
49f3fffd 524 return MACHINE_OPENING;
9444b1f2 525
fb6becb4
LP
526 return MACHINE_RUNNING;
527}
9444b1f2 528
fb6becb4
LP
529int machine_kill(Machine *m, KillWho who, int signo) {
530 assert(m);
9444b1f2 531
fbe55073
LP
532 if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
533 return -EOPNOTSUPP;
534
89f7c846 535 if (!m->unit)
fb6becb4 536 return -ESRCH;
9444b1f2 537
de58a50e
LP
538 if (who == KILL_LEADER) {
539 /* If we shall simply kill the leader, do so directly */
540
541 if (kill(m->leader, signo) < 0)
542 return -errno;
9d685ca8
ED
543
544 return 0;
de58a50e
LP
545 }
546
b938cb90 547 /* Otherwise, make PID 1 do it for us, for the entire cgroup */
de58a50e 548 return manager_kill_unit(m->manager, m->unit, signo, NULL);
9444b1f2
LP
549}
550
fbe55073
LP
551int machine_openpt(Machine *m, int flags) {
552 assert(m);
553
554 switch (m->class) {
555
5f430ff7
LP
556 case MACHINE_HOST: {
557 int fd;
558
559 fd = posix_openpt(flags);
560 if (fd < 0)
561 return -errno;
562
563 if (unlockpt(fd) < 0)
564 return -errno;
565
566 return fd;
567 }
fbe55073
LP
568
569 case MACHINE_CONTAINER:
570 if (m->leader <= 0)
571 return -EINVAL;
572
573 return openpt_in_namespace(m->leader, flags);
574
575 default:
576 return -EOPNOTSUPP;
577 }
578}
579
40e1f4ea
LP
580int machine_open_terminal(Machine *m, const char *path, int mode) {
581 assert(m);
582
583 switch (m->class) {
584
585 case MACHINE_HOST:
586 return open_terminal(path, mode);
587
588 case MACHINE_CONTAINER:
589 if (m->leader <= 0)
590 return -EINVAL;
591
592 return open_terminal_in_namespace(m->leader, path, mode);
593
594 default:
595 return -EOPNOTSUPP;
596 }
597}
598
9b420b3c
LP
599void machine_release_unit(Machine *m) {
600 assert(m);
601
602 if (!m->unit)
603 return;
604
605 (void) hashmap_remove(m->manager->machine_units, m->unit);
a1e58e8e 606 m->unit = mfree(m->unit);
9b420b3c
LP
607}
608
3401419b
LP
609int machine_get_uid_shift(Machine *m, uid_t *ret) {
610 char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
611 uid_t uid_base, uid_shift, uid_range;
612 gid_t gid_base, gid_shift, gid_range;
613 _cleanup_fclose_ FILE *f = NULL;
614 int k;
615
616 assert(m);
617 assert(ret);
618
619 /* Return the base UID/GID of the specified machine. Note that this only works for containers with simple
620 * mappings. In most cases setups should be simple like this, and administrators should only care about the
621 * basic offset a container has relative to the host. This is what this function exposes.
622 *
623 * If we encounter any more complex mappings we politely refuse this with ENXIO. */
624
625 if (m->class == MACHINE_HOST) {
626 *ret = 0;
627 return 0;
628 }
629
630 if (m->class != MACHINE_CONTAINER)
631 return -EOPNOTSUPP;
632
633 xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader);
634 f = fopen(p, "re");
635 if (!f) {
636 if (errno == ENOENT) {
637 /* If the file doesn't exist, user namespacing is off in the kernel, return a zero mapping hence. */
638 *ret = 0;
639 return 0;
640 }
641
642 return -errno;
643 }
644
645 /* Read the first line. There's at least one. */
646 errno = 0;
647 k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
648 if (k != 3) {
649 if (ferror(f))
650 return -errno;
651
652 return -EBADMSG;
653 }
654
655 /* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
656 if (uid_base != 0)
657 return -ENXIO;
658 /* Insist that at least the nobody user is mapped, everything else is weird, and hence complex, and we don't support it */
659 if (uid_range < (uid_t) 65534U)
660 return -ENXIO;
661
662 /* If there's more than one line, then we don't support this mapping. */
663 if (fgetc(f) != EOF)
664 return -ENXIO;
665
666 fclose(f);
667
668 xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader);
669 f = fopen(p, "re");
670 if (!f)
671 return -errno;
672
673 /* Read the first line. There's at least one. */
674 errno = 0;
675 k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
676 if (k != 3) {
677 if (ferror(f))
678 return -errno;
679
680 return -EBADMSG;
681 }
682
683 /* If there's more than one line, then we don't support this file. */
684 if (fgetc(f) != EOF)
685 return -ENXIO;
686
687 /* If the UID and GID mapping doesn't match, we don't support this mapping. */
688 if (uid_base != (uid_t) gid_base)
689 return -ENXIO;
690 if (uid_shift != (uid_t) gid_shift)
691 return -ENXIO;
692 if (uid_range != (uid_t) gid_range)
693 return -ENXIO;
694
695 *ret = uid_shift;
696 return 0;
697}
698
9444b1f2
LP
699static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
700 [MACHINE_CONTAINER] = "container",
fbe55073
LP
701 [MACHINE_VM] = "vm",
702 [MACHINE_HOST] = "host",
9444b1f2
LP
703};
704
705DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
fb6becb4
LP
706
707static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
708 [MACHINE_OPENING] = "opening",
709 [MACHINE_RUNNING] = "running",
710 [MACHINE_CLOSING] = "closing"
711};
712
713DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
1ee306e1
LP
714
715static const char* const kill_who_table[_KILL_WHO_MAX] = {
716 [KILL_LEADER] = "leader",
717 [KILL_ALL] = "all"
718};
719
720DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);