chiark / gitweb /
journal: keep per-JournalFile location info during iteration
[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 "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);