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