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