]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machine.c
treewide: a few more log_*_errno + return simplifications
[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_errno(r, "Failed to save machine data %s: %m", m->state_file);
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 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
263 }
264
265 if (id)
266 sd_id128_from_string(id, &m->id);
267
268 if (leader)
269 parse_pid(leader, &m->leader);
270
271 if (class) {
272 MachineClass c;
273
274 c = machine_class_from_string(class);
275 if (c >= 0)
276 m->class = c;
277 }
278
279 if (realtime) {
280 unsigned long long l;
281 if (sscanf(realtime, "%llu", &l) > 0)
282 m->timestamp.realtime = l;
283 }
284
285 if (monotonic) {
286 unsigned long long l;
287 if (sscanf(monotonic, "%llu", &l) > 0)
288 m->timestamp.monotonic = l;
289 }
290
291 if (netif) {
292 size_t l, allocated = 0, nr = 0;
293 const char *word, *state;
294 int *ni = NULL;
295
296 FOREACH_WORD(word, l, netif, state) {
297 char buf[l+1];
298 int ifi;
299
300 *(char*) (mempcpy(buf, word, l)) = 0;
301
302 if (safe_atoi(buf, &ifi) < 0)
303 continue;
304 if (ifi <= 0)
305 continue;
306
307 if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
308 free(ni);
309 return log_oom();
310 }
311
312 ni[nr++] = ifi;
313 }
314
315 free(m->netif);
316 m->netif = ni;
317 m->n_netif = nr;
318 }
319
320 return r;
321 }
322
323 static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
324 int r = 0;
325
326 assert(m);
327
328 if (!m->unit) {
329 _cleanup_free_ char *escaped = NULL;
330 char *scope, *description, *job = NULL;
331
332 escaped = unit_name_escape(m->name);
333 if (!escaped)
334 return log_oom();
335
336 scope = strjoin("machine-", escaped, ".scope", NULL);
337 if (!scope)
338 return log_oom();
339
340 description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
341
342 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
343 if (r < 0) {
344 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
345 free(scope);
346 return r;
347 } else {
348 m->unit = scope;
349
350 free(m->scope_job);
351 m->scope_job = job;
352 }
353 }
354
355 if (m->unit)
356 hashmap_put(m->manager->machine_units, m->unit, m);
357
358 return r;
359 }
360
361 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
362 int r;
363
364 assert(m);
365
366 if (m->started)
367 return 0;
368
369 r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
370 if (r < 0)
371 return r;
372
373 /* Create cgroup */
374 r = machine_start_scope(m, properties, error);
375 if (r < 0)
376 return r;
377
378 log_struct(LOG_INFO,
379 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START),
380 "NAME=%s", m->name,
381 "LEADER="PID_FMT, m->leader,
382 LOG_MESSAGE("New machine %s.", m->name),
383 NULL);
384
385 if (!dual_timestamp_is_set(&m->timestamp))
386 dual_timestamp_get(&m->timestamp);
387
388 m->started = true;
389
390 /* Save new machine data */
391 machine_save(m);
392
393 machine_send_signal(m, true);
394
395 return 0;
396 }
397
398 static int machine_stop_scope(Machine *m) {
399 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
400 char *job = NULL;
401 int r;
402
403 assert(m);
404
405 if (!m->unit)
406 return 0;
407
408 if (!m->registered) {
409 r = manager_stop_unit(m->manager, m->unit, &error, &job);
410 if (r < 0) {
411 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
412 return r;
413 }
414 }
415
416 free(m->scope_job);
417 m->scope_job = job;
418
419 return 0;
420 }
421
422 int machine_stop(Machine *m) {
423 int r = 0, k;
424 assert(m);
425
426 if (m->started)
427 log_struct(LOG_INFO,
428 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
429 "NAME=%s", m->name,
430 "LEADER="PID_FMT, m->leader,
431 LOG_MESSAGE("Machine %s terminated.", m->name),
432 NULL);
433
434 /* Kill cgroup */
435 k = machine_stop_scope(m);
436 if (k < 0)
437 r = k;
438
439 machine_unlink(m);
440 machine_add_to_gc_queue(m);
441
442 if (m->started)
443 machine_send_signal(m, false);
444
445 m->started = false;
446
447 return r;
448 }
449
450 bool machine_check_gc(Machine *m, bool drop_not_started) {
451 assert(m);
452
453 if (drop_not_started && !m->started)
454 return false;
455
456 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
457 return true;
458
459 if (m->unit && manager_unit_is_active(m->manager, m->unit))
460 return true;
461
462 return false;
463 }
464
465 void machine_add_to_gc_queue(Machine *m) {
466 assert(m);
467
468 if (m->in_gc_queue)
469 return;
470
471 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
472 m->in_gc_queue = true;
473 }
474
475 MachineState machine_get_state(Machine *s) {
476 assert(s);
477
478 if (s->scope_job)
479 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
480
481 return MACHINE_RUNNING;
482 }
483
484 int machine_kill(Machine *m, KillWho who, int signo) {
485 assert(m);
486
487 if (!m->unit)
488 return -ESRCH;
489
490 if (who == KILL_LEADER) {
491 /* If we shall simply kill the leader, do so directly */
492
493 if (kill(m->leader, signo) < 0)
494 return -errno;
495
496 return 0;
497 }
498
499 /* Otherwise make PID 1 do it for us, for the entire cgroup */
500 return manager_kill_unit(m->manager, m->unit, signo, NULL);
501 }
502
503 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
504 [MACHINE_CONTAINER] = "container",
505 [MACHINE_VM] = "vm"
506 };
507
508 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
509
510 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
511 [MACHINE_OPENING] = "opening",
512 [MACHINE_RUNNING] = "running",
513 [MACHINE_CLOSING] = "closing"
514 };
515
516 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
517
518 static const char* const kill_who_table[_KILL_WHO_MAX] = {
519 [KILL_LEADER] = "leader",
520 [KILL_ALL] = "all"
521 };
522
523 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);