chiark / gitweb /
b2831187965e4a86e843daa56bfe08cdeec34c63
[elogind.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 "bus-util.h"
36 #include "bus-error.h"
37 #include "machine.h"
38 #include "machine-dbus.h"
39
40 Machine* machine_new(Manager *manager, const char *name) {
41         Machine *m;
42
43         assert(manager);
44         assert(name);
45
46         m = new0(Machine, 1);
47         if (!m)
48                 return NULL;
49
50         m->name = strdup(name);
51         if (!m->name)
52                 goto fail;
53
54         m->state_file = strappend("/run/systemd/machines/", m->name);
55         if (!m->state_file)
56                 goto fail;
57
58         if (hashmap_put(manager->machines, m->name, m) < 0)
59                 goto fail;
60
61         m->class = _MACHINE_CLASS_INVALID;
62         m->manager = manager;
63
64         return m;
65
66 fail:
67         free(m->state_file);
68         free(m->name);
69         free(m);
70
71         return NULL;
72 }
73
74 void machine_free(Machine *m) {
75         assert(m);
76
77         if (m->in_gc_queue)
78                 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
79
80         if (m->unit) {
81                 hashmap_remove(m->manager->machine_units, m->unit);
82                 free(m->unit);
83         }
84
85         free(m->scope_job);
86
87         hashmap_remove(m->manager->machines, m->name);
88
89         if (m->leader > 0)
90                 hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
91
92         sd_bus_message_unref(m->create_message);
93
94         free(m->name);
95         free(m->state_file);
96         free(m->service);
97         free(m->root_directory);
98         free(m->netif);
99         free(m);
100 }
101
102 int machine_save(Machine *m) {
103         _cleanup_free_ char *temp_path = NULL;
104         _cleanup_fclose_ FILE *f = NULL;
105         int r;
106
107         assert(m);
108         assert(m->state_file);
109
110         if (!m->started)
111                 return 0;
112
113         r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
114         if (r < 0)
115                 goto finish;
116
117         r = fopen_temporary(m->state_file, &f, &temp_path);
118         if (r < 0)
119                 goto finish;
120
121         fchmod(fileno(f), 0644);
122
123         fprintf(f,
124                 "# This is private data. Do not parse.\n"
125                 "NAME=%s\n",
126                 m->name);
127
128         if (m->unit) {
129                 _cleanup_free_ char *escaped;
130
131                 escaped = cescape(m->unit);
132                 if (!escaped) {
133                         r = -ENOMEM;
134                         goto finish;
135                 }
136
137                 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         if (m->scope_job)
141                 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
142
143         if (m->service) {
144                 _cleanup_free_ char *escaped;
145
146                 escaped = cescape(m->service);
147                 if (!escaped) {
148                         r = -ENOMEM;
149                         goto finish;
150                 }
151                 fprintf(f, "SERVICE=%s\n", escaped);
152         }
153
154         if (m->root_directory) {
155                 _cleanup_free_ char *escaped;
156
157                 escaped = cescape(m->root_directory);
158                 if (!escaped) {
159                         r = -ENOMEM;
160                         goto finish;
161                 }
162                 fprintf(f, "ROOT=%s\n", escaped);
163         }
164
165         if (!sd_id128_equal(m->id, SD_ID128_NULL))
166                 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
167
168         if (m->leader != 0)
169                 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
170
171         if (m->class != _MACHINE_CLASS_INVALID)
172                 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
173
174         if (dual_timestamp_is_set(&m->timestamp))
175                 fprintf(f,
176                         "REALTIME="USEC_FMT"\n"
177                         "MONOTONIC="USEC_FMT"\n",
178                         m->timestamp.realtime,
179                         m->timestamp.monotonic);
180
181         if (m->n_netif > 0) {
182                 unsigned i;
183
184                 fputs("NETIF=", f);
185
186                 for (i = 0; i < m->n_netif; i++) {
187                         if (i != 0)
188                                 fputc(' ', f);
189
190                         fprintf(f, "%i", m->netif[i]);
191                 }
192
193                 fputc('\n', f);
194         }
195
196         r = fflush_and_check(f);
197         if (r < 0)
198                 goto finish;
199
200         if (rename(temp_path, m->state_file) < 0) {
201                 r = -errno;
202                 goto finish;
203         }
204
205         if (m->unit) {
206                 char *sl;
207
208                 /* Create a symlink from the unit name to the machine
209                  * name, so that we can quickly find the machine for
210                  * each given unit */
211                 sl = strappenda("/run/systemd/machines/unit:", m->unit);
212                 symlink(m->name, sl);
213         }
214
215 finish:
216         if (r < 0) {
217                 if (temp_path)
218                         unlink(temp_path);
219
220                 log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
221         }
222
223         return r;
224 }
225
226 static void machine_unlink(Machine *m) {
227         assert(m);
228
229         if (m->unit) {
230
231                 char *sl;
232
233                 sl = strappenda("/run/systemd/machines/unit:", m->unit);
234                 unlink(sl);
235         }
236
237         if (m->state_file)
238                 unlink(m->state_file);
239 }
240
241 int machine_load(Machine *m) {
242         _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
243         int r;
244
245         assert(m);
246
247         r = parse_env_file(m->state_file, NEWLINE,
248                            "SCOPE",     &m->unit,
249                            "SCOPE_JOB", &m->scope_job,
250                            "SERVICE",   &m->service,
251                            "ROOT",      &m->root_directory,
252                            "ID",        &id,
253                            "LEADER",    &leader,
254                            "CLASS",     &class,
255                            "REALTIME",  &realtime,
256                            "MONOTONIC", &monotonic,
257                            "NETIF",     &netif,
258                            NULL);
259         if (r < 0) {
260                 if (r == -ENOENT)
261                         return 0;
262
263                 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
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         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         free(m->scope_job);
416         m->scope_job = job;
417
418         return 0;
419 }
420
421 int machine_stop(Machine *m) {
422         int r = 0, k;
423         assert(m);
424
425         if (m->started)
426                 log_struct(LOG_INFO,
427                            LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
428                            "NAME=%s", m->name,
429                            "LEADER="PID_FMT, m->leader,
430                            LOG_MESSAGE("Machine %s terminated.", m->name),
431                            NULL);
432
433         /* Kill cgroup */
434         k = machine_stop_scope(m);
435         if (k < 0)
436                 r = k;
437
438         machine_unlink(m);
439         machine_add_to_gc_queue(m);
440
441         if (m->started)
442                 machine_send_signal(m, false);
443
444         m->started = false;
445
446         return r;
447 }
448
449 bool machine_check_gc(Machine *m, bool drop_not_started) {
450         assert(m);
451
452         if (drop_not_started && !m->started)
453                 return false;
454
455         if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
456                 return true;
457
458         if (m->unit && manager_unit_is_active(m->manager, m->unit))
459                 return true;
460
461         return false;
462 }
463
464 void machine_add_to_gc_queue(Machine *m) {
465         assert(m);
466
467         if (m->in_gc_queue)
468                 return;
469
470         LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
471         m->in_gc_queue = true;
472 }
473
474 MachineState machine_get_state(Machine *s) {
475         assert(s);
476
477         if (s->scope_job)
478                 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
479
480         return MACHINE_RUNNING;
481 }
482
483 int machine_kill(Machine *m, KillWho who, int signo) {
484         assert(m);
485
486         if (!m->unit)
487                 return -ESRCH;
488
489         if (who == KILL_LEADER) {
490                 /* If we shall simply kill the leader, do so directly */
491
492                 if (kill(m->leader, signo) < 0)
493                         return -errno;
494
495                 return 0;
496         }
497
498         /* Otherwise make PID 1 do it for us, for the entire cgroup */
499         return manager_kill_unit(m->manager, m->unit, signo, NULL);
500 }
501
502 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
503         [MACHINE_CONTAINER] = "container",
504         [MACHINE_VM] = "vm"
505 };
506
507 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
508
509 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
510         [MACHINE_OPENING] = "opening",
511         [MACHINE_RUNNING] = "running",
512         [MACHINE_CLOSING] = "closing"
513 };
514
515 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
516
517 static const char* const kill_who_table[_KILL_WHO_MAX] = {
518         [KILL_LEADER] = "leader",
519         [KILL_ALL] = "all"
520 };
521
522 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);