chiark / gitweb /
8dec716d719057f1d63c774369be275cdd1920f8
[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         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);