chiark / gitweb /
cgroups: simplify CPUQuota= logic
[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);
98 }
99
100 int machine_save(Machine *m) {
101         _cleanup_free_ char *temp_path = NULL;
102         _cleanup_fclose_ FILE *f = NULL;
103         int r;
104
105         assert(m);
106         assert(m->state_file);
107
108         if (!m->started)
109                 return 0;
110
111         r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
112         if (r < 0)
113                 goto finish;
114
115         r = fopen_temporary(m->state_file, &f, &temp_path);
116         if (r < 0)
117                 goto finish;
118
119         fchmod(fileno(f), 0644);
120
121         fprintf(f,
122                 "# This is private data. Do not parse.\n"
123                 "NAME=%s\n",
124                 m->name);
125
126         if (m->unit) {
127                 _cleanup_free_ char *escaped;
128
129                 escaped = cescape(m->unit);
130                 if (!escaped) {
131                         r = -ENOMEM;
132                         goto finish;
133                 }
134
135                 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 */
136         }
137
138         if (m->scope_job)
139                 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
140
141         if (m->service) {
142                 _cleanup_free_ char *escaped;
143
144                 escaped = cescape(m->service);
145                 if (!escaped) {
146                         r = -ENOMEM;
147                         goto finish;
148                 }
149                 fprintf(f, "SERVICE=%s\n", escaped);
150         }
151
152         if (m->root_directory) {
153                 _cleanup_free_ char *escaped;
154
155                 escaped = cescape(m->root_directory);
156                 if (!escaped) {
157                         r = -ENOMEM;
158                         goto finish;
159                 }
160                 fprintf(f, "ROOT=%s\n", escaped);
161         }
162
163         if (!sd_id128_equal(m->id, SD_ID128_NULL))
164                 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
165
166         if (m->leader != 0)
167                 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
168
169         if (m->class != _MACHINE_CLASS_INVALID)
170                 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
171
172         if (dual_timestamp_is_set(&m->timestamp))
173                 fprintf(f,
174                         "REALTIME="USEC_FMT"\n"
175                         "MONOTONIC="USEC_FMT"\n",
176                         m->timestamp.realtime,
177                         m->timestamp.monotonic);
178
179         fflush(f);
180
181         if (ferror(f) || rename(temp_path, m->state_file) < 0) {
182                 r = -errno;
183                 unlink(m->state_file);
184                 unlink(temp_path);
185         }
186
187         if (m->unit) {
188                 char *sl;
189
190                 /* Create a symlink from the unit name to the machine
191                  * name, so that we can quickly find the machine for
192                  * each given unit */
193                 sl = strappenda("/run/systemd/machines/unit:", m->unit);
194                 symlink(m->name, sl);
195         }
196
197 finish:
198         if (r < 0)
199                 log_error("Failed to save machine data %s: %s", m->state_file, strerror(-r));
200
201         return r;
202 }
203
204 static void machine_unlink(Machine *m) {
205         assert(m);
206
207         if (m->unit) {
208
209                 char *sl;
210
211                 sl = strappenda("/run/systemd/machines/unit:", m->unit);
212                 unlink(sl);
213         }
214
215         if (m->state_file)
216                 unlink(m->state_file);
217 }
218
219 int machine_load(Machine *m) {
220         _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL;
221         int r;
222
223         assert(m);
224
225         r = parse_env_file(m->state_file, NEWLINE,
226                            "SCOPE",     &m->unit,
227                            "SCOPE_JOB", &m->scope_job,
228                            "SERVICE",   &m->service,
229                            "ROOT",      &m->root_directory,
230                            "ID",        &id,
231                            "LEADER",    &leader,
232                            "CLASS",     &class,
233                            "REALTIME",  &realtime,
234                            "MONOTONIC", &monotonic,
235                            NULL);
236         if (r < 0) {
237                 if (r == -ENOENT)
238                         return 0;
239
240                 log_error("Failed to read %s: %s", m->state_file, strerror(-r));
241                 return r;
242         }
243
244         if (id)
245                 sd_id128_from_string(id, &m->id);
246
247         if (leader)
248                 parse_pid(leader, &m->leader);
249
250         if (class) {
251                 MachineClass c;
252
253                 c = machine_class_from_string(class);
254                 if (c >= 0)
255                         m->class = c;
256         }
257
258         if (realtime) {
259                 unsigned long long l;
260                 if (sscanf(realtime, "%llu", &l) > 0)
261                         m->timestamp.realtime = l;
262         }
263
264         if (monotonic) {
265                 unsigned long long l;
266                 if (sscanf(monotonic, "%llu", &l) > 0)
267                         m->timestamp.monotonic = l;
268         }
269
270         return r;
271 }
272
273 static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
274         int r = 0;
275
276         assert(m);
277
278         if (!m->unit) {
279                 _cleanup_free_ char *escaped = NULL;
280                 char *scope, *description, *job = NULL;
281
282                 escaped = unit_name_escape(m->name);
283                 if (!escaped)
284                         return log_oom();
285
286                 scope = strjoin("machine-", escaped, ".scope", NULL);
287                 if (!scope)
288                         return log_oom();
289
290                 description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
291
292                 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
293                 if (r < 0) {
294                         log_error("Failed to start machine scope: %s", bus_error_message(error, r));
295                         free(scope);
296                         return r;
297                 } else {
298                         m->unit = scope;
299
300                         free(m->scope_job);
301                         m->scope_job = job;
302                 }
303         }
304
305         if (m->unit)
306                 hashmap_put(m->manager->machine_units, m->unit, m);
307
308         return r;
309 }
310
311 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
312         int r;
313
314         assert(m);
315
316         if (m->started)
317                 return 0;
318
319         r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
320         if (r < 0)
321                 return r;
322
323         /* Create cgroup */
324         r = machine_start_scope(m, properties, error);
325         if (r < 0)
326                 return r;
327
328         log_struct(LOG_INFO,
329                    MESSAGE_ID(SD_MESSAGE_MACHINE_START),
330                    "NAME=%s", m->name,
331                    "LEADER="PID_FMT, m->leader,
332                    "MESSAGE=New machine %s.", m->name,
333                    NULL);
334
335         if (!dual_timestamp_is_set(&m->timestamp))
336                 dual_timestamp_get(&m->timestamp);
337
338         m->started = true;
339
340         /* Save new machine data */
341         machine_save(m);
342
343         machine_send_signal(m, true);
344
345         return 0;
346 }
347
348 static int machine_stop_scope(Machine *m) {
349         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
350         char *job = NULL;
351         int r;
352
353         assert(m);
354
355         if (!m->unit)
356                 return 0;
357
358         r = manager_stop_unit(m->manager, m->unit, &error, &job);
359         if (r < 0) {
360                 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
361                 return r;
362         }
363
364         free(m->scope_job);
365         m->scope_job = job;
366
367         return r;
368 }
369
370 int machine_stop(Machine *m) {
371         int r = 0, k;
372         assert(m);
373
374         if (m->started)
375                 log_struct(LOG_INFO,
376                            MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
377                            "NAME=%s", m->name,
378                            "LEADER="PID_FMT, m->leader,
379                            "MESSAGE=Machine %s terminated.", m->name,
380                            NULL);
381
382         /* Kill cgroup */
383         k = machine_stop_scope(m);
384         if (k < 0)
385                 r = k;
386
387         machine_unlink(m);
388         machine_add_to_gc_queue(m);
389
390         if (m->started)
391                 machine_send_signal(m, false);
392
393         m->started = false;
394
395         return r;
396 }
397
398 bool machine_check_gc(Machine *m, bool drop_not_started) {
399         assert(m);
400
401         if (drop_not_started && !m->started)
402                 return false;
403
404         if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
405                 return true;
406
407         if (m->unit && manager_unit_is_active(m->manager, m->unit))
408                 return true;
409
410         return false;
411 }
412
413 void machine_add_to_gc_queue(Machine *m) {
414         assert(m);
415
416         if (m->in_gc_queue)
417                 return;
418
419         LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
420         m->in_gc_queue = true;
421 }
422
423 MachineState machine_get_state(Machine *s) {
424         assert(s);
425
426         if (s->scope_job)
427                 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
428
429         return MACHINE_RUNNING;
430 }
431
432 int machine_kill(Machine *m, KillWho who, int signo) {
433         assert(m);
434
435         if (!m->unit)
436                 return -ESRCH;
437
438         if (who == KILL_LEADER) {
439                 /* If we shall simply kill the leader, do so directly */
440
441                 if (kill(m->leader, signo) < 0)
442                         return -errno;
443         }
444
445         /* Otherwise make PID 1 do it for us, for the entire cgroup */
446         return manager_kill_unit(m->manager, m->unit, signo, NULL);
447 }
448
449 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
450         [MACHINE_CONTAINER] = "container",
451         [MACHINE_VM] = "vm"
452 };
453
454 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
455
456 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
457         [MACHINE_OPENING] = "opening",
458         [MACHINE_RUNNING] = "running",
459         [MACHINE_CLOSING] = "closing"
460 };
461
462 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
463
464 static const char* const kill_who_table[_KILL_WHO_MAX] = {
465         [KILL_LEADER] = "leader",
466         [KILL_ALL] = "all"
467 };
468
469 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);