]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machine.c
util-lib: split string parsing related calls from util.[ch] into parse-util.[ch]
[thirdparty/systemd.git] / src / machine / machine.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include "sd-messages.h"
27
28 #include "bus-error.h"
29 #include "bus-util.h"
30 #include "escape.h"
31 #include "fd-util.h"
32 #include "fileio.h"
33 #include "formats-util.h"
34 #include "hashmap.h"
35 #include "machine-dbus.h"
36 #include "machine.h"
37 #include "mkdir.h"
38 #include "parse-util.h"
39 #include "special.h"
40 #include "terminal-util.h"
41 #include "unit-name.h"
42 #include "util.h"
43
44 Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
45 Machine *m;
46
47 assert(manager);
48 assert(class < _MACHINE_CLASS_MAX);
49 assert(name);
50
51 /* Passing class == _MACHINE_CLASS_INVALID here is fine. It
52 * means as much as "we don't know yet", and that we'll figure
53 * it out later when loading the state file. */
54
55 m = new0(Machine, 1);
56 if (!m)
57 return NULL;
58
59 m->name = strdup(name);
60 if (!m->name)
61 goto fail;
62
63 if (class != MACHINE_HOST) {
64 m->state_file = strappend("/run/systemd/machines/", m->name);
65 if (!m->state_file)
66 goto fail;
67 }
68
69 m->class = class;
70
71 if (hashmap_put(manager->machines, m->name, m) < 0)
72 goto fail;
73
74 m->manager = manager;
75
76 return m;
77
78 fail:
79 free(m->state_file);
80 free(m->name);
81 free(m);
82
83 return NULL;
84 }
85
86 void machine_free(Machine *m) {
87 assert(m);
88
89 while (m->operations)
90 machine_operation_unref(m->operations);
91
92 if (m->in_gc_queue)
93 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
94
95 machine_release_unit(m);
96
97 free(m->scope_job);
98
99 (void) hashmap_remove(m->manager->machines, m->name);
100
101 if (m->manager->host_machine == m)
102 m->manager->host_machine = NULL;
103
104 if (m->leader > 0)
105 (void) hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
106
107 sd_bus_message_unref(m->create_message);
108
109 free(m->name);
110 free(m->state_file);
111 free(m->service);
112 free(m->root_directory);
113 free(m->netif);
114 free(m);
115 }
116
117 int machine_save(Machine *m) {
118 _cleanup_free_ char *temp_path = NULL;
119 _cleanup_fclose_ FILE *f = NULL;
120 int r;
121
122 assert(m);
123
124 if (!m->state_file)
125 return 0;
126
127 if (!m->started)
128 return 0;
129
130 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
131 if (r < 0)
132 goto fail;
133
134 r = fopen_temporary(m->state_file, &f, &temp_path);
135 if (r < 0)
136 goto fail;
137
138 (void) fchmod(fileno(f), 0644);
139
140 fprintf(f,
141 "# This is private data. Do not parse.\n"
142 "NAME=%s\n",
143 m->name);
144
145 if (m->unit) {
146 _cleanup_free_ char *escaped;
147
148 escaped = cescape(m->unit);
149 if (!escaped) {
150 r = -ENOMEM;
151 goto fail;
152 }
153
154 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 */
155 }
156
157 if (m->scope_job)
158 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
159
160 if (m->service) {
161 _cleanup_free_ char *escaped;
162
163 escaped = cescape(m->service);
164 if (!escaped) {
165 r = -ENOMEM;
166 goto fail;
167 }
168 fprintf(f, "SERVICE=%s\n", escaped);
169 }
170
171 if (m->root_directory) {
172 _cleanup_free_ char *escaped;
173
174 escaped = cescape(m->root_directory);
175 if (!escaped) {
176 r = -ENOMEM;
177 goto fail;
178 }
179 fprintf(f, "ROOT=%s\n", escaped);
180 }
181
182 if (!sd_id128_equal(m->id, SD_ID128_NULL))
183 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
184
185 if (m->leader != 0)
186 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
187
188 if (m->class != _MACHINE_CLASS_INVALID)
189 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
190
191 if (dual_timestamp_is_set(&m->timestamp))
192 fprintf(f,
193 "REALTIME="USEC_FMT"\n"
194 "MONOTONIC="USEC_FMT"\n",
195 m->timestamp.realtime,
196 m->timestamp.monotonic);
197
198 if (m->n_netif > 0) {
199 unsigned i;
200
201 fputs("NETIF=", f);
202
203 for (i = 0; i < m->n_netif; i++) {
204 if (i != 0)
205 fputc(' ', f);
206
207 fprintf(f, "%i", m->netif[i]);
208 }
209
210 fputc('\n', f);
211 }
212
213 r = fflush_and_check(f);
214 if (r < 0)
215 goto fail;
216
217 if (rename(temp_path, m->state_file) < 0) {
218 r = -errno;
219 goto fail;
220 }
221
222 if (m->unit) {
223 char *sl;
224
225 /* Create a symlink from the unit name to the machine
226 * name, so that we can quickly find the machine for
227 * each given unit. Ignore error. */
228 sl = strjoina("/run/systemd/machines/unit:", m->unit);
229 (void) symlink(m->name, sl);
230 }
231
232 return 0;
233
234 fail:
235 (void) unlink(m->state_file);
236
237 if (temp_path)
238 (void) unlink(temp_path);
239
240 return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
241 }
242
243 static void machine_unlink(Machine *m) {
244 assert(m);
245
246 if (m->unit) {
247
248 char *sl;
249
250 sl = strjoina("/run/systemd/machines/unit:", m->unit);
251 (void) unlink(sl);
252 }
253
254 if (m->state_file)
255 (void) unlink(m->state_file);
256 }
257
258 int machine_load(Machine *m) {
259 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
260 int r;
261
262 assert(m);
263
264 if (!m->state_file)
265 return 0;
266
267 r = parse_env_file(m->state_file, NEWLINE,
268 "SCOPE", &m->unit,
269 "SCOPE_JOB", &m->scope_job,
270 "SERVICE", &m->service,
271 "ROOT", &m->root_directory,
272 "ID", &id,
273 "LEADER", &leader,
274 "CLASS", &class,
275 "REALTIME", &realtime,
276 "MONOTONIC", &monotonic,
277 "NETIF", &netif,
278 NULL);
279 if (r < 0) {
280 if (r == -ENOENT)
281 return 0;
282
283 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
284 }
285
286 if (id)
287 sd_id128_from_string(id, &m->id);
288
289 if (leader)
290 parse_pid(leader, &m->leader);
291
292 if (class) {
293 MachineClass c;
294
295 c = machine_class_from_string(class);
296 if (c >= 0)
297 m->class = c;
298 }
299
300 if (realtime) {
301 unsigned long long l;
302 if (sscanf(realtime, "%llu", &l) > 0)
303 m->timestamp.realtime = l;
304 }
305
306 if (monotonic) {
307 unsigned long long l;
308 if (sscanf(monotonic, "%llu", &l) > 0)
309 m->timestamp.monotonic = l;
310 }
311
312 if (netif) {
313 size_t l, allocated = 0, nr = 0;
314 const char *word, *state;
315 int *ni = NULL;
316
317 FOREACH_WORD(word, l, netif, state) {
318 char buf[l+1];
319 int ifi;
320
321 *(char*) (mempcpy(buf, word, l)) = 0;
322
323 if (safe_atoi(buf, &ifi) < 0)
324 continue;
325 if (ifi <= 0)
326 continue;
327
328 if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
329 free(ni);
330 return log_oom();
331 }
332
333 ni[nr++] = ifi;
334 }
335
336 free(m->netif);
337 m->netif = ni;
338 m->n_netif = nr;
339 }
340
341 return r;
342 }
343
344 static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
345 int r = 0;
346
347 assert(m);
348 assert(m->class != MACHINE_HOST);
349
350 if (!m->unit) {
351 _cleanup_free_ char *escaped = NULL;
352 char *scope, *description, *job = NULL;
353
354 escaped = unit_name_escape(m->name);
355 if (!escaped)
356 return log_oom();
357
358 scope = strjoin("machine-", escaped, ".scope", NULL);
359 if (!scope)
360 return log_oom();
361
362 description = strjoina(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
363
364 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
365 if (r < 0) {
366 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
367 free(scope);
368 return r;
369 } else {
370 m->unit = scope;
371
372 free(m->scope_job);
373 m->scope_job = job;
374 }
375 }
376
377 if (m->unit)
378 hashmap_put(m->manager->machine_units, m->unit, m);
379
380 return r;
381 }
382
383 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
384 int r;
385
386 assert(m);
387
388 if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
389 return -EOPNOTSUPP;
390
391 if (m->started)
392 return 0;
393
394 r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
395 if (r < 0)
396 return r;
397
398 /* Create cgroup */
399 r = machine_start_scope(m, properties, error);
400 if (r < 0)
401 return r;
402
403 log_struct(LOG_INFO,
404 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START),
405 "NAME=%s", m->name,
406 "LEADER="PID_FMT, m->leader,
407 LOG_MESSAGE("New machine %s.", m->name),
408 NULL);
409
410 if (!dual_timestamp_is_set(&m->timestamp))
411 dual_timestamp_get(&m->timestamp);
412
413 m->started = true;
414
415 /* Save new machine data */
416 machine_save(m);
417
418 machine_send_signal(m, true);
419
420 return 0;
421 }
422
423 static int machine_stop_scope(Machine *m) {
424 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
425 char *job = NULL;
426 int r;
427
428 assert(m);
429 assert(m->class != MACHINE_HOST);
430
431 if (!m->unit)
432 return 0;
433
434 r = manager_stop_unit(m->manager, m->unit, &error, &job);
435 if (r < 0) {
436 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
437 return r;
438 }
439
440 free(m->scope_job);
441 m->scope_job = job;
442
443 return 0;
444 }
445
446 int machine_stop(Machine *m) {
447 int r;
448 assert(m);
449
450 if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
451 return -EOPNOTSUPP;
452
453 r = machine_stop_scope(m);
454
455 m->stopping = true;
456
457 machine_save(m);
458
459 return r;
460 }
461
462 int machine_finalize(Machine *m) {
463 assert(m);
464
465 if (m->started)
466 log_struct(LOG_INFO,
467 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
468 "NAME=%s", m->name,
469 "LEADER="PID_FMT, m->leader,
470 LOG_MESSAGE("Machine %s terminated.", m->name),
471 NULL);
472
473 machine_unlink(m);
474 machine_add_to_gc_queue(m);
475
476 if (m->started) {
477 machine_send_signal(m, false);
478 m->started = false;
479 }
480
481 return 0;
482 }
483
484 bool machine_check_gc(Machine *m, bool drop_not_started) {
485 assert(m);
486
487 if (m->class == MACHINE_HOST)
488 return true;
489
490 if (drop_not_started && !m->started)
491 return false;
492
493 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
494 return true;
495
496 if (m->unit && manager_unit_is_active(m->manager, m->unit))
497 return true;
498
499 return false;
500 }
501
502 void machine_add_to_gc_queue(Machine *m) {
503 assert(m);
504
505 if (m->in_gc_queue)
506 return;
507
508 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
509 m->in_gc_queue = true;
510 }
511
512 MachineState machine_get_state(Machine *s) {
513 assert(s);
514
515 if (s->class == MACHINE_HOST)
516 return MACHINE_RUNNING;
517
518 if (s->stopping)
519 return MACHINE_CLOSING;
520
521 if (s->scope_job)
522 return MACHINE_OPENING;
523
524 return MACHINE_RUNNING;
525 }
526
527 int machine_kill(Machine *m, KillWho who, int signo) {
528 assert(m);
529
530 if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
531 return -EOPNOTSUPP;
532
533 if (!m->unit)
534 return -ESRCH;
535
536 if (who == KILL_LEADER) {
537 /* If we shall simply kill the leader, do so directly */
538
539 if (kill(m->leader, signo) < 0)
540 return -errno;
541
542 return 0;
543 }
544
545 /* Otherwise make PID 1 do it for us, for the entire cgroup */
546 return manager_kill_unit(m->manager, m->unit, signo, NULL);
547 }
548
549 int machine_openpt(Machine *m, int flags) {
550 assert(m);
551
552 switch (m->class) {
553
554 case MACHINE_HOST: {
555 int fd;
556
557 fd = posix_openpt(flags);
558 if (fd < 0)
559 return -errno;
560
561 if (unlockpt(fd) < 0)
562 return -errno;
563
564 return fd;
565 }
566
567 case MACHINE_CONTAINER:
568 if (m->leader <= 0)
569 return -EINVAL;
570
571 return openpt_in_namespace(m->leader, flags);
572
573 default:
574 return -EOPNOTSUPP;
575 }
576 }
577
578 int machine_open_terminal(Machine *m, const char *path, int mode) {
579 assert(m);
580
581 switch (m->class) {
582
583 case MACHINE_HOST:
584 return open_terminal(path, mode);
585
586 case MACHINE_CONTAINER:
587 if (m->leader <= 0)
588 return -EINVAL;
589
590 return open_terminal_in_namespace(m->leader, path, mode);
591
592 default:
593 return -EOPNOTSUPP;
594 }
595 }
596
597 MachineOperation *machine_operation_unref(MachineOperation *o) {
598 if (!o)
599 return NULL;
600
601 sd_event_source_unref(o->event_source);
602
603 safe_close(o->errno_fd);
604
605 if (o->pid > 1)
606 (void) kill(o->pid, SIGKILL);
607
608 sd_bus_message_unref(o->message);
609
610 if (o->machine) {
611 LIST_REMOVE(operations, o->machine->operations, o);
612 o->machine->n_operations--;
613 }
614
615 free(o);
616 return NULL;
617 }
618
619 void machine_release_unit(Machine *m) {
620 assert(m);
621
622 if (!m->unit)
623 return;
624
625 (void) hashmap_remove(m->manager->machine_units, m->unit);
626 m->unit = mfree(m->unit);
627 }
628
629 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
630 [MACHINE_CONTAINER] = "container",
631 [MACHINE_VM] = "vm",
632 [MACHINE_HOST] = "host",
633 };
634
635 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
636
637 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
638 [MACHINE_OPENING] = "opening",
639 [MACHINE_RUNNING] = "running",
640 [MACHINE_CLOSING] = "closing"
641 };
642
643 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
644
645 static const char* const kill_who_table[_KILL_WHO_MAX] = {
646 [KILL_LEADER] = "leader",
647 [KILL_ALL] = "all"
648 };
649
650 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);