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