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