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