chiark / gitweb /
machine: make sure unpriviliged "machinectl status" can show the machine's OS version
[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("Failed to save machine data %s: %s", m->state_file, strerror(-r));
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                 log_error("Failed to read %s: %s", m->state_file, strerror(-r));
263                 return r;
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                    MESSAGE_ID(SD_MESSAGE_MACHINE_START),
381                    "NAME=%s", m->name,
382                    "LEADER="PID_FMT, m->leader,
383                    "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                            MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
430                            "NAME=%s", m->name,
431                            "LEADER="PID_FMT, m->leader,
432                            "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
498         /* Otherwise make PID 1 do it for us, for the entire cgroup */
499         return manager_kill_unit(m->manager, m->unit, signo, NULL);
500 }
501
502 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
503         [MACHINE_CONTAINER] = "container",
504         [MACHINE_VM] = "vm"
505 };
506
507 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
508
509 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
510         [MACHINE_OPENING] = "opening",
511         [MACHINE_RUNNING] = "running",
512         [MACHINE_CLOSING] = "closing"
513 };
514
515 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
516
517 static const char* const kill_who_table[_KILL_WHO_MAX] = {
518         [KILL_LEADER] = "leader",
519         [KILL_ALL] = "all"
520 };
521
522 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);