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