chiark / gitweb /
223eb0f3641be7677742a0f9d7dc1075a404b97c
[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         free(temp_path);
206         temp_path = NULL;
207
208         if (m->unit) {
209                 char *sl;
210
211                 /* Create a symlink from the unit name to the machine
212                  * name, so that we can quickly find the machine for
213                  * each given unit */
214                 sl = strjoina("/run/systemd/machines/unit:", m->unit);
215                 symlink(m->name, sl);
216         }
217
218 finish:
219         if (temp_path)
220                 unlink(temp_path);
221
222         if (r < 0)
223                 log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
224
225         return r;
226 }
227
228 static void machine_unlink(Machine *m) {
229         assert(m);
230
231         if (m->unit) {
232
233                 char *sl;
234
235                 sl = strjoina("/run/systemd/machines/unit:", m->unit);
236                 unlink(sl);
237         }
238
239         if (m->state_file)
240                 unlink(m->state_file);
241 }
242
243 int machine_load(Machine *m) {
244         _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
245         int r;
246
247         assert(m);
248
249         r = parse_env_file(m->state_file, NEWLINE,
250                            "SCOPE",     &m->unit,
251                            "SCOPE_JOB", &m->scope_job,
252                            "SERVICE",   &m->service,
253                            "ROOT",      &m->root_directory,
254                            "ID",        &id,
255                            "LEADER",    &leader,
256                            "CLASS",     &class,
257                            "REALTIME",  &realtime,
258                            "MONOTONIC", &monotonic,
259                            "NETIF",     &netif,
260                            NULL);
261         if (r < 0) {
262                 if (r == -ENOENT)
263                         return 0;
264
265                 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
266         }
267
268         if (id)
269                 sd_id128_from_string(id, &m->id);
270
271         if (leader)
272                 parse_pid(leader, &m->leader);
273
274         if (class) {
275                 MachineClass c;
276
277                 c = machine_class_from_string(class);
278                 if (c >= 0)
279                         m->class = c;
280         }
281
282         if (realtime) {
283                 unsigned long long l;
284                 if (sscanf(realtime, "%llu", &l) > 0)
285                         m->timestamp.realtime = l;
286         }
287
288         if (monotonic) {
289                 unsigned long long l;
290                 if (sscanf(monotonic, "%llu", &l) > 0)
291                         m->timestamp.monotonic = l;
292         }
293
294         if (netif) {
295                 size_t l, allocated = 0, nr = 0;
296                 const char *word, *state;
297                 int *ni = NULL;
298
299                 FOREACH_WORD(word, l, netif, state) {
300                         char buf[l+1];
301                         int ifi;
302
303                         *(char*) (mempcpy(buf, word, l)) = 0;
304
305                         if (safe_atoi(buf, &ifi) < 0)
306                                 continue;
307                         if (ifi <= 0)
308                                 continue;
309
310                         if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
311                                 free(ni);
312                                 return log_oom();
313                         }
314
315                         ni[nr++] = ifi;
316                 }
317
318                 free(m->netif);
319                 m->netif = ni;
320                 m->n_netif = nr;
321         }
322
323         return r;
324 }
325
326 static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
327         int r = 0;
328
329         assert(m);
330
331         if (!m->unit) {
332                 _cleanup_free_ char *escaped = NULL;
333                 char *scope, *description, *job = NULL;
334
335                 escaped = unit_name_escape(m->name);
336                 if (!escaped)
337                         return log_oom();
338
339                 scope = strjoin("machine-", escaped, ".scope", NULL);
340                 if (!scope)
341                         return log_oom();
342
343                 description = strjoina(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
344
345                 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
346                 if (r < 0) {
347                         log_error("Failed to start machine scope: %s", bus_error_message(error, r));
348                         free(scope);
349                         return r;
350                 } else {
351                         m->unit = scope;
352
353                         free(m->scope_job);
354                         m->scope_job = job;
355                 }
356         }
357
358         if (m->unit)
359                 hashmap_put(m->manager->machine_units, m->unit, m);
360
361         return r;
362 }
363
364 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
365         int r;
366
367         assert(m);
368
369         if (m->started)
370                 return 0;
371
372         r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
373         if (r < 0)
374                 return r;
375
376         /* Create cgroup */
377         r = machine_start_scope(m, properties, error);
378         if (r < 0)
379                 return r;
380
381         log_struct(LOG_INFO,
382                    LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START),
383                    "NAME=%s", m->name,
384                    "LEADER="PID_FMT, m->leader,
385                    LOG_MESSAGE("New machine %s.", m->name),
386                    NULL);
387
388         if (!dual_timestamp_is_set(&m->timestamp))
389                 dual_timestamp_get(&m->timestamp);
390
391         m->started = true;
392
393         /* Save new machine data */
394         machine_save(m);
395
396         machine_send_signal(m, true);
397
398         return 0;
399 }
400
401 static int machine_stop_scope(Machine *m) {
402         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
403         char *job = NULL;
404         int r;
405
406         assert(m);
407
408         if (!m->unit)
409                 return 0;
410
411         r = manager_stop_unit(m->manager, m->unit, &error, &job);
412         if (r < 0) {
413                 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
414                 return r;
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);