chiark / gitweb /
Remove src/locale
[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 "fileio.h"
32 #include "special.h"
33 #include "unit-name.h"
34 #include "bus-util.h"
35 #include "bus-error.h"
36 #include "machine.h"
37 #include "machine-dbus.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         while (m->operations)
77                 machine_operation_unref(m->operations);
78
79         if (m->in_gc_queue)
80                 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
81
82         if (m->unit) {
83                 hashmap_remove(m->manager->machine_units, m->unit);
84                 free(m->unit);
85         }
86
87         free(m->scope_job);
88
89         hashmap_remove(m->manager->machines, m->name);
90
91         if (m->leader > 0)
92                 hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
93
94         sd_bus_message_unref(m->create_message);
95
96         free(m->name);
97         free(m->state_file);
98         free(m->service);
99         free(m->root_directory);
100         free(m->netif);
101         free(m);
102 }
103
104 int machine_save(Machine *m) {
105         _cleanup_free_ char *temp_path = NULL;
106         _cleanup_fclose_ FILE *f = NULL;
107         int r;
108
109         assert(m);
110         assert(m->state_file);
111
112         if (!m->started)
113                 return 0;
114
115         r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
116         if (r < 0)
117                 goto finish;
118
119         r = fopen_temporary(m->state_file, &f, &temp_path);
120         if (r < 0)
121                 goto finish;
122
123         fchmod(fileno(f), 0644);
124
125         fprintf(f,
126                 "# This is private data. Do not parse.\n"
127                 "NAME=%s\n",
128                 m->name);
129
130         if (m->unit) {
131                 _cleanup_free_ char *escaped;
132
133                 escaped = cescape(m->unit);
134                 if (!escaped) {
135                         r = -ENOMEM;
136                         goto finish;
137                 }
138
139                 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 */
140         }
141
142         if (m->scope_job)
143                 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
144
145         if (m->service) {
146                 _cleanup_free_ char *escaped;
147
148                 escaped = cescape(m->service);
149                 if (!escaped) {
150                         r = -ENOMEM;
151                         goto finish;
152                 }
153                 fprintf(f, "SERVICE=%s\n", escaped);
154         }
155
156         if (m->root_directory) {
157                 _cleanup_free_ char *escaped;
158
159                 escaped = cescape(m->root_directory);
160                 if (!escaped) {
161                         r = -ENOMEM;
162                         goto finish;
163                 }
164                 fprintf(f, "ROOT=%s\n", escaped);
165         }
166
167         if (!sd_id128_equal(m->id, SD_ID128_NULL))
168                 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
169
170         if (m->leader != 0)
171                 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
172
173         if (m->class != _MACHINE_CLASS_INVALID)
174                 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
175
176         if (dual_timestamp_is_set(&m->timestamp))
177                 fprintf(f,
178                         "REALTIME="USEC_FMT"\n"
179                         "MONOTONIC="USEC_FMT"\n",
180                         m->timestamp.realtime,
181                         m->timestamp.monotonic);
182
183         if (m->n_netif > 0) {
184                 unsigned i;
185
186                 fputs("NETIF=", f);
187
188                 for (i = 0; i < m->n_netif; i++) {
189                         if (i != 0)
190                                 fputc(' ', f);
191
192                         fprintf(f, "%i", m->netif[i]);
193                 }
194
195                 fputc('\n', f);
196         }
197
198         r = fflush_and_check(f);
199         if (r < 0)
200                 goto finish;
201
202         if (rename(temp_path, m->state_file) < 0) {
203                 r = -errno;
204                 goto finish;
205         }
206
207         free(temp_path);
208         temp_path = NULL;
209
210         if (m->unit) {
211                 char *sl;
212
213                 /* Create a symlink from the unit name to the machine
214                  * name, so that we can quickly find the machine for
215                  * each given unit. Ignore error. */
216                 sl = strjoina("/run/systemd/machines/unit:", m->unit);
217                 (void) symlink(m->name, sl);
218         }
219
220 finish:
221         if (temp_path)
222                 unlink(temp_path);
223
224         if (r < 0)
225                 log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
226
227         return r;
228 }
229
230 static void machine_unlink(Machine *m) {
231         assert(m);
232
233         if (m->unit) {
234
235                 char *sl;
236
237                 sl = strjoina("/run/systemd/machines/unit:", m->unit);
238                 unlink(sl);
239         }
240
241         if (m->state_file)
242                 unlink(m->state_file);
243 }
244
245 int machine_load(Machine *m) {
246         _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
247         int r;
248
249         assert(m);
250
251         r = parse_env_file(m->state_file, NEWLINE,
252                            "SCOPE",     &m->unit,
253                            "SCOPE_JOB", &m->scope_job,
254                            "SERVICE",   &m->service,
255                            "ROOT",      &m->root_directory,
256                            "ID",        &id,
257                            "LEADER",    &leader,
258                            "CLASS",     &class,
259                            "REALTIME",  &realtime,
260                            "MONOTONIC", &monotonic,
261                            "NETIF",     &netif,
262                            NULL);
263         if (r < 0) {
264                 if (r == -ENOENT)
265                         return 0;
266
267                 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
268         }
269
270         if (id)
271                 sd_id128_from_string(id, &m->id);
272
273         if (leader)
274                 parse_pid(leader, &m->leader);
275
276         if (class) {
277                 MachineClass c;
278
279                 c = machine_class_from_string(class);
280                 if (c >= 0)
281                         m->class = c;
282         }
283
284         if (realtime) {
285                 unsigned long long l;
286                 if (sscanf(realtime, "%llu", &l) > 0)
287                         m->timestamp.realtime = l;
288         }
289
290         if (monotonic) {
291                 unsigned long long l;
292                 if (sscanf(monotonic, "%llu", &l) > 0)
293                         m->timestamp.monotonic = l;
294         }
295
296         if (netif) {
297                 size_t l, allocated = 0, nr = 0;
298                 const char *word, *state;
299                 int *ni = NULL;
300
301                 FOREACH_WORD(word, l, netif, state) {
302                         char buf[l+1];
303                         int ifi;
304
305                         *(char*) (mempcpy(buf, word, l)) = 0;
306
307                         if (safe_atoi(buf, &ifi) < 0)
308                                 continue;
309                         if (ifi <= 0)
310                                 continue;
311
312                         if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
313                                 free(ni);
314                                 return log_oom();
315                         }
316
317                         ni[nr++] = ifi;
318                 }
319
320                 free(m->netif);
321                 m->netif = ni;
322                 m->n_netif = nr;
323         }
324
325         return r;
326 }
327
328 static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
329         int r = 0;
330
331         assert(m);
332
333         if (!m->unit) {
334                 _cleanup_free_ char *escaped = NULL;
335                 char *scope, *description, *job = NULL;
336
337                 escaped = unit_name_escape(m->name);
338                 if (!escaped)
339                         return log_oom();
340
341                 scope = strjoin("machine-", escaped, ".scope", NULL);
342                 if (!scope)
343                         return log_oom();
344
345                 description = strjoina(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
346
347                 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
348                 if (r < 0) {
349                         log_error("Failed to start machine scope: %s", bus_error_message(error, r));
350                         free(scope);
351                         return r;
352                 } else {
353                         m->unit = scope;
354
355                         free(m->scope_job);
356                         m->scope_job = job;
357                 }
358         }
359
360         if (m->unit)
361                 hashmap_put(m->manager->machine_units, m->unit, m);
362
363         return r;
364 }
365
366 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
367         int r;
368
369         assert(m);
370
371         if (m->started)
372                 return 0;
373
374         r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
375         if (r < 0)
376                 return r;
377
378         /* Create cgroup */
379         r = machine_start_scope(m, properties, error);
380         if (r < 0)
381                 return r;
382
383         log_struct(LOG_INFO,
384                    LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START),
385                    "NAME=%s", m->name,
386                    "LEADER="PID_FMT, m->leader,
387                    LOG_MESSAGE("New machine %s.", m->name),
388                    NULL);
389
390         if (!dual_timestamp_is_set(&m->timestamp))
391                 dual_timestamp_get(&m->timestamp);
392
393         m->started = true;
394
395         /* Save new machine data */
396         machine_save(m);
397
398         machine_send_signal(m, true);
399
400         return 0;
401 }
402
403 static int machine_stop_scope(Machine *m) {
404         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
405         char *job = NULL;
406         int r;
407
408         assert(m);
409
410         if (!m->unit)
411                 return 0;
412
413         r = manager_stop_unit(m->manager, m->unit, &error, &job);
414         if (r < 0) {
415                 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
416                 return r;
417         }
418
419         free(m->scope_job);
420         m->scope_job = job;
421
422         return 0;
423 }
424
425 int machine_stop(Machine *m) {
426         int r = 0, k;
427         assert(m);
428
429         if (m->started)
430                 log_struct(LOG_INFO,
431                            LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
432                            "NAME=%s", m->name,
433                            "LEADER="PID_FMT, m->leader,
434                            LOG_MESSAGE("Machine %s terminated.", m->name),
435                            NULL);
436
437         /* Kill cgroup */
438         k = machine_stop_scope(m);
439         if (k < 0)
440                 r = k;
441
442         machine_unlink(m);
443         machine_add_to_gc_queue(m);
444
445         if (m->started)
446                 machine_send_signal(m, false);
447
448         m->started = false;
449
450         return r;
451 }
452
453 bool machine_check_gc(Machine *m, bool drop_not_started) {
454         assert(m);
455
456         if (drop_not_started && !m->started)
457                 return false;
458
459         if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
460                 return true;
461
462         if (m->unit && manager_unit_is_active(m->manager, m->unit))
463                 return true;
464
465         return false;
466 }
467
468 void machine_add_to_gc_queue(Machine *m) {
469         assert(m);
470
471         if (m->in_gc_queue)
472                 return;
473
474         LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
475         m->in_gc_queue = true;
476 }
477
478 MachineState machine_get_state(Machine *s) {
479         assert(s);
480
481         if (s->scope_job)
482                 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
483
484         return MACHINE_RUNNING;
485 }
486
487 int machine_kill(Machine *m, KillWho who, int signo) {
488         assert(m);
489
490         if (!m->unit)
491                 return -ESRCH;
492
493         if (who == KILL_LEADER) {
494                 /* If we shall simply kill the leader, do so directly */
495
496                 if (kill(m->leader, signo) < 0)
497                         return -errno;
498
499                 return 0;
500         }
501
502         /* Otherwise make PID 1 do it for us, for the entire cgroup */
503         return manager_kill_unit(m->manager, m->unit, signo, NULL);
504 }
505
506 MachineOperation *machine_operation_unref(MachineOperation *o) {
507         if (!o)
508                 return NULL;
509
510         sd_event_source_unref(o->event_source);
511
512         safe_close(o->errno_fd);
513
514         if (o->pid > 1)
515                 (void) kill(o->pid, SIGKILL);
516
517         sd_bus_message_unref(o->message);
518
519         if (o->machine) {
520                 LIST_REMOVE(operations, o->machine->operations, o);
521                 o->machine->n_operations--;
522         }
523
524         free(o);
525         return NULL;
526 }
527
528 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
529         [MACHINE_CONTAINER] = "container",
530         [MACHINE_VM] = "vm"
531 };
532
533 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
534
535 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
536         [MACHINE_OPENING] = "opening",
537         [MACHINE_RUNNING] = "running",
538         [MACHINE_CLOSING] = "closing"
539 };
540
541 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
542
543 static const char* const kill_who_table[_KILL_WHO_MAX] = {
544         [KILL_LEADER] = "leader",
545         [KILL_ALL] = "all"
546 };
547
548 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);