]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machine.c
machine: don't return uninitialized variable
[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);
98 }
99
100 int machine_save(Machine *m) {
101 _cleanup_free_ char *temp_path = NULL;
102 _cleanup_fclose_ FILE *f = NULL;
103 int r;
104
105 assert(m);
106 assert(m->state_file);
107
108 if (!m->started)
109 return 0;
110
111 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
112 if (r < 0)
113 goto finish;
114
115 r = fopen_temporary(m->state_file, &f, &temp_path);
116 if (r < 0)
117 goto finish;
118
119 fchmod(fileno(f), 0644);
120
121 fprintf(f,
122 "# This is private data. Do not parse.\n"
123 "NAME=%s\n",
124 m->name);
125
126 if (m->unit) {
127 _cleanup_free_ char *escaped;
128
129 escaped = cescape(m->unit);
130 if (!escaped) {
131 r = -ENOMEM;
132 goto finish;
133 }
134
135 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 */
136 }
137
138 if (m->scope_job)
139 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
140
141 if (m->service) {
142 _cleanup_free_ char *escaped;
143
144 escaped = cescape(m->service);
145 if (!escaped) {
146 r = -ENOMEM;
147 goto finish;
148 }
149 fprintf(f, "SERVICE=%s\n", escaped);
150 }
151
152 if (m->root_directory) {
153 _cleanup_free_ char *escaped;
154
155 escaped = cescape(m->root_directory);
156 if (!escaped) {
157 r = -ENOMEM;
158 goto finish;
159 }
160 fprintf(f, "ROOT=%s\n", escaped);
161 }
162
163 if (!sd_id128_equal(m->id, SD_ID128_NULL))
164 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
165
166 if (m->leader != 0)
167 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
168
169 if (m->class != _MACHINE_CLASS_INVALID)
170 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
171
172 if (dual_timestamp_is_set(&m->timestamp))
173 fprintf(f,
174 "REALTIME="USEC_FMT"\n"
175 "MONOTONIC="USEC_FMT"\n",
176 m->timestamp.realtime,
177 m->timestamp.monotonic);
178
179 r = fflush_and_check(f);
180 if (r < 0)
181 goto finish;
182
183 if (rename(temp_path, m->state_file) < 0) {
184 r = -errno;
185 goto finish;
186 }
187
188 if (m->unit) {
189 char *sl;
190
191 /* Create a symlink from the unit name to the machine
192 * name, so that we can quickly find the machine for
193 * each given unit */
194 sl = strappenda("/run/systemd/machines/unit:", m->unit);
195 symlink(m->name, sl);
196 }
197
198 finish:
199 if (r < 0) {
200 if (temp_path)
201 unlink(temp_path);
202
203 log_error("Failed to save machine data %s: %s", m->state_file, strerror(-r));
204 }
205
206 return r;
207 }
208
209 static void machine_unlink(Machine *m) {
210 assert(m);
211
212 if (m->unit) {
213
214 char *sl;
215
216 sl = strappenda("/run/systemd/machines/unit:", m->unit);
217 unlink(sl);
218 }
219
220 if (m->state_file)
221 unlink(m->state_file);
222 }
223
224 int machine_load(Machine *m) {
225 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL;
226 int r;
227
228 assert(m);
229
230 r = parse_env_file(m->state_file, NEWLINE,
231 "SCOPE", &m->unit,
232 "SCOPE_JOB", &m->scope_job,
233 "SERVICE", &m->service,
234 "ROOT", &m->root_directory,
235 "ID", &id,
236 "LEADER", &leader,
237 "CLASS", &class,
238 "REALTIME", &realtime,
239 "MONOTONIC", &monotonic,
240 NULL);
241 if (r < 0) {
242 if (r == -ENOENT)
243 return 0;
244
245 log_error("Failed to read %s: %s", m->state_file, strerror(-r));
246 return r;
247 }
248
249 if (id)
250 sd_id128_from_string(id, &m->id);
251
252 if (leader)
253 parse_pid(leader, &m->leader);
254
255 if (class) {
256 MachineClass c;
257
258 c = machine_class_from_string(class);
259 if (c >= 0)
260 m->class = c;
261 }
262
263 if (realtime) {
264 unsigned long long l;
265 if (sscanf(realtime, "%llu", &l) > 0)
266 m->timestamp.realtime = l;
267 }
268
269 if (monotonic) {
270 unsigned long long l;
271 if (sscanf(monotonic, "%llu", &l) > 0)
272 m->timestamp.monotonic = l;
273 }
274
275 return r;
276 }
277
278 static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
279 int r = 0;
280
281 assert(m);
282
283 if (!m->unit) {
284 _cleanup_free_ char *escaped = NULL;
285 char *scope, *description, *job = NULL;
286
287 escaped = unit_name_escape(m->name);
288 if (!escaped)
289 return log_oom();
290
291 scope = strjoin("machine-", escaped, ".scope", NULL);
292 if (!scope)
293 return log_oom();
294
295 description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
296
297 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
298 if (r < 0) {
299 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
300 free(scope);
301 return r;
302 } else {
303 m->unit = scope;
304
305 free(m->scope_job);
306 m->scope_job = job;
307 }
308 }
309
310 if (m->unit)
311 hashmap_put(m->manager->machine_units, m->unit, m);
312
313 return r;
314 }
315
316 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
317 int r;
318
319 assert(m);
320
321 if (m->started)
322 return 0;
323
324 r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
325 if (r < 0)
326 return r;
327
328 /* Create cgroup */
329 r = machine_start_scope(m, properties, error);
330 if (r < 0)
331 return r;
332
333 log_struct(LOG_INFO,
334 MESSAGE_ID(SD_MESSAGE_MACHINE_START),
335 "NAME=%s", m->name,
336 "LEADER="PID_FMT, m->leader,
337 "MESSAGE=New machine %s.", m->name,
338 NULL);
339
340 if (!dual_timestamp_is_set(&m->timestamp))
341 dual_timestamp_get(&m->timestamp);
342
343 m->started = true;
344
345 /* Save new machine data */
346 machine_save(m);
347
348 machine_send_signal(m, true);
349
350 return 0;
351 }
352
353 static int machine_stop_scope(Machine *m) {
354 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
355 char *job = NULL;
356 int r;
357
358 assert(m);
359
360 if (!m->unit)
361 return 0;
362
363 if (!m->registered) {
364 r = manager_stop_unit(m->manager, m->unit, &error, &job);
365 if (r < 0) {
366 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
367 return r;
368 }
369 }
370
371 free(m->scope_job);
372 m->scope_job = job;
373
374 return 0;
375 }
376
377 int machine_stop(Machine *m) {
378 int r = 0, k;
379 assert(m);
380
381 if (m->started)
382 log_struct(LOG_INFO,
383 MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
384 "NAME=%s", m->name,
385 "LEADER="PID_FMT, m->leader,
386 "MESSAGE=Machine %s terminated.", m->name,
387 NULL);
388
389 /* Kill cgroup */
390 k = machine_stop_scope(m);
391 if (k < 0)
392 r = k;
393
394 machine_unlink(m);
395 machine_add_to_gc_queue(m);
396
397 if (m->started)
398 machine_send_signal(m, false);
399
400 m->started = false;
401
402 return r;
403 }
404
405 bool machine_check_gc(Machine *m, bool drop_not_started) {
406 assert(m);
407
408 if (drop_not_started && !m->started)
409 return false;
410
411 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
412 return true;
413
414 if (m->unit && manager_unit_is_active(m->manager, m->unit))
415 return true;
416
417 return false;
418 }
419
420 void machine_add_to_gc_queue(Machine *m) {
421 assert(m);
422
423 if (m->in_gc_queue)
424 return;
425
426 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
427 m->in_gc_queue = true;
428 }
429
430 MachineState machine_get_state(Machine *s) {
431 assert(s);
432
433 if (s->scope_job)
434 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
435
436 return MACHINE_RUNNING;
437 }
438
439 int machine_kill(Machine *m, KillWho who, int signo) {
440 assert(m);
441
442 if (!m->unit)
443 return -ESRCH;
444
445 if (who == KILL_LEADER) {
446 /* If we shall simply kill the leader, do so directly */
447
448 if (kill(m->leader, signo) < 0)
449 return -errno;
450 }
451
452 /* Otherwise make PID 1 do it for us, for the entire cgroup */
453 return manager_kill_unit(m->manager, m->unit, signo, NULL);
454 }
455
456 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
457 [MACHINE_CONTAINER] = "container",
458 [MACHINE_VM] = "vm"
459 };
460
461 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
462
463 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
464 [MACHINE_OPENING] = "opening",
465 [MACHINE_RUNNING] = "running",
466 [MACHINE_CLOSING] = "closing"
467 };
468
469 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
470
471 static const char* const kill_who_table[_KILL_WHO_MAX] = {
472 [KILL_LEADER] = "leader",
473 [KILL_ALL] = "all"
474 };
475
476 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);