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