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