]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined.c
Merge pull request #3589 from brauner/cgroup_namespace
[thirdparty/systemd.git] / src / machine / machined.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2013 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include "sd-daemon.h"
25
26 #include "alloc-util.h"
27 #include "bus-error.h"
28 #include "bus-util.h"
29 #include "cgroup-util.h"
30 #include "dirent-util.h"
31 #include "fd-util.h"
32 #include "formats-util.h"
33 #include "hostname-util.h"
34 #include "label.h"
35 #include "machine-image.h"
36 #include "machined.h"
37 #include "signal-util.h"
38
39 Manager *manager_new(void) {
40 Manager *m;
41 int r;
42
43 m = new0(Manager, 1);
44 if (!m)
45 return NULL;
46
47 m->machines = hashmap_new(&string_hash_ops);
48 m->machine_units = hashmap_new(&string_hash_ops);
49 m->machine_leaders = hashmap_new(NULL);
50
51 if (!m->machines || !m->machine_units || !m->machine_leaders) {
52 manager_free(m);
53 return NULL;
54 }
55
56 r = sd_event_default(&m->event);
57 if (r < 0) {
58 manager_free(m);
59 return NULL;
60 }
61
62 sd_event_set_watchdog(m->event, true);
63
64 return m;
65 }
66
67 void manager_free(Manager *m) {
68 Machine *machine;
69 Image *i;
70
71 assert(m);
72
73 while (m->operations)
74 operation_free(m->operations);
75
76 assert(m->n_operations == 0);
77
78 while ((machine = hashmap_first(m->machines)))
79 machine_free(machine);
80
81 hashmap_free(m->machines);
82 hashmap_free(m->machine_units);
83 hashmap_free(m->machine_leaders);
84
85 while ((i = hashmap_steal_first(m->image_cache)))
86 image_unref(i);
87
88 hashmap_free(m->image_cache);
89
90 sd_event_source_unref(m->image_cache_defer_event);
91
92 bus_verify_polkit_async_registry_free(m->polkit_registry);
93
94 sd_bus_unref(m->bus);
95 sd_event_unref(m->event);
96
97 free(m);
98 }
99
100 static int manager_add_host_machine(Manager *m) {
101 _cleanup_free_ char *rd = NULL, *unit = NULL;
102 sd_id128_t mid;
103 Machine *t;
104 int r;
105
106 if (m->host_machine)
107 return 0;
108
109 r = sd_id128_get_machine(&mid);
110 if (r < 0)
111 return log_error_errno(r, "Failed to get machine ID: %m");
112
113 rd = strdup("/");
114 if (!rd)
115 return log_oom();
116
117 unit = strdup("-.slice");
118 if (!unit)
119 return log_oom();
120
121 t = machine_new(m, MACHINE_HOST, ".host");
122 if (!t)
123 return log_oom();
124
125 t->leader = 1;
126 t->id = mid;
127
128 t->root_directory = rd;
129 t->unit = unit;
130 rd = unit = NULL;
131
132 dual_timestamp_from_boottime_or_monotonic(&t->timestamp, 0);
133
134 m->host_machine = t;
135
136 return 0;
137 }
138
139 int manager_enumerate_machines(Manager *m) {
140 _cleanup_closedir_ DIR *d = NULL;
141 struct dirent *de;
142 int r = 0;
143
144 assert(m);
145
146 r = manager_add_host_machine(m);
147 if (r < 0)
148 return r;
149
150 /* Read in machine data stored on disk */
151 d = opendir("/run/systemd/machines");
152 if (!d) {
153 if (errno == ENOENT)
154 return 0;
155
156 return log_error_errno(errno, "Failed to open /run/systemd/machines: %m");
157 }
158
159 FOREACH_DIRENT(de, d, return -errno) {
160 struct Machine *machine;
161 int k;
162
163 if (!dirent_is_file(de))
164 continue;
165
166 /* Ignore symlinks that map the unit name to the machine */
167 if (startswith(de->d_name, "unit:"))
168 continue;
169
170 if (!machine_name_is_valid(de->d_name))
171 continue;
172
173 k = manager_add_machine(m, de->d_name, &machine);
174 if (k < 0) {
175 r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name);
176 continue;
177 }
178
179 machine_add_to_gc_queue(machine);
180
181 k = machine_load(machine);
182 if (k < 0)
183 r = k;
184 }
185
186 return r;
187 }
188
189 static int manager_connect_bus(Manager *m) {
190 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
191 int r;
192
193 assert(m);
194 assert(!m->bus);
195
196 r = sd_bus_default_system(&m->bus);
197 if (r < 0)
198 return log_error_errno(r, "Failed to connect to system bus: %m");
199
200 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", manager_vtable, m);
201 if (r < 0)
202 return log_error_errno(r, "Failed to add manager object vtable: %m");
203
204 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/machine", "org.freedesktop.machine1.Machine", machine_vtable, machine_object_find, m);
205 if (r < 0)
206 return log_error_errno(r, "Failed to add machine object vtable: %m");
207
208 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/machine", machine_node_enumerator, m);
209 if (r < 0)
210 return log_error_errno(r, "Failed to add machine enumerator: %m");
211
212 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/image", "org.freedesktop.machine1.Image", image_vtable, image_object_find, m);
213 if (r < 0)
214 return log_error_errno(r, "Failed to add image object vtable: %m");
215
216 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/image", image_node_enumerator, m);
217 if (r < 0)
218 return log_error_errno(r, "Failed to add image enumerator: %m");
219
220 r = sd_bus_add_match(m->bus,
221 NULL,
222 "type='signal',"
223 "sender='org.freedesktop.systemd1',"
224 "interface='org.freedesktop.systemd1.Manager',"
225 "member='JobRemoved',"
226 "path='/org/freedesktop/systemd1'",
227 match_job_removed,
228 m);
229 if (r < 0)
230 return log_error_errno(r, "Failed to add match for JobRemoved: %m");
231
232 r = sd_bus_add_match(m->bus,
233 NULL,
234 "type='signal',"
235 "sender='org.freedesktop.systemd1',"
236 "interface='org.freedesktop.systemd1.Manager',"
237 "member='UnitRemoved',"
238 "path='/org/freedesktop/systemd1'",
239 match_unit_removed,
240 m);
241 if (r < 0)
242 return log_error_errno(r, "Failed to add match for UnitRemoved: %m");
243
244 r = sd_bus_add_match(m->bus,
245 NULL,
246 "type='signal',"
247 "sender='org.freedesktop.systemd1',"
248 "interface='org.freedesktop.DBus.Properties',"
249 "member='PropertiesChanged',"
250 "arg0='org.freedesktop.systemd1.Unit'",
251 match_properties_changed,
252 m);
253 if (r < 0)
254 return log_error_errno(r, "Failed to add match for PropertiesChanged: %m");
255
256 r = sd_bus_add_match(m->bus,
257 NULL,
258 "type='signal',"
259 "sender='org.freedesktop.systemd1',"
260 "interface='org.freedesktop.systemd1.Manager',"
261 "member='Reloading',"
262 "path='/org/freedesktop/systemd1'",
263 match_reloading,
264 m);
265 if (r < 0)
266 return log_error_errno(r, "Failed to add match for Reloading: %m");
267
268 r = sd_bus_call_method(
269 m->bus,
270 "org.freedesktop.systemd1",
271 "/org/freedesktop/systemd1",
272 "org.freedesktop.systemd1.Manager",
273 "Subscribe",
274 &error,
275 NULL, NULL);
276 if (r < 0) {
277 log_error("Failed to enable subscription: %s", bus_error_message(&error, r));
278 return r;
279 }
280
281 r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", 0);
282 if (r < 0)
283 return log_error_errno(r, "Failed to register name: %m");
284
285 r = sd_bus_attach_event(m->bus, m->event, 0);
286 if (r < 0)
287 return log_error_errno(r, "Failed to attach bus to event loop: %m");
288
289 return 0;
290 }
291
292 void manager_gc(Manager *m, bool drop_not_started) {
293 Machine *machine;
294
295 assert(m);
296
297 while ((machine = m->machine_gc_queue)) {
298 LIST_REMOVE(gc_queue, m->machine_gc_queue, machine);
299 machine->in_gc_queue = false;
300
301 /* First, if we are not closing yet, initiate stopping */
302 if (!machine_check_gc(machine, drop_not_started) &&
303 machine_get_state(machine) != MACHINE_CLOSING)
304 machine_stop(machine);
305
306 /* Now, the stop probably made this referenced
307 * again, but if it didn't, then it's time to let it
308 * go entirely. */
309 if (!machine_check_gc(machine, drop_not_started)) {
310 machine_finalize(machine);
311 machine_free(machine);
312 }
313 }
314 }
315
316 int manager_startup(Manager *m) {
317 Machine *machine;
318 Iterator i;
319 int r;
320
321 assert(m);
322
323 /* Connect to the bus */
324 r = manager_connect_bus(m);
325 if (r < 0)
326 return r;
327
328 /* Deserialize state */
329 manager_enumerate_machines(m);
330
331 /* Remove stale objects before we start them */
332 manager_gc(m, false);
333
334 /* And start everything */
335 HASHMAP_FOREACH(machine, m->machines, i)
336 machine_start(machine, NULL, NULL);
337
338 return 0;
339 }
340
341 static bool check_idle(void *userdata) {
342 Manager *m = userdata;
343
344 if (m->operations)
345 return false;
346
347 manager_gc(m, true);
348
349 return hashmap_isempty(m->machines);
350 }
351
352 int manager_run(Manager *m) {
353 assert(m);
354
355 return bus_event_loop_with_idle(
356 m->event,
357 m->bus,
358 "org.freedesktop.machine1",
359 DEFAULT_EXIT_USEC,
360 check_idle, m);
361 }
362
363 int main(int argc, char *argv[]) {
364 Manager *m = NULL;
365 int r;
366
367 log_set_target(LOG_TARGET_AUTO);
368 log_set_facility(LOG_AUTH);
369 log_parse_environment();
370 log_open();
371
372 umask(0022);
373
374 if (argc != 1) {
375 log_error("This program takes no arguments.");
376 r = -EINVAL;
377 goto finish;
378 }
379
380 /* Always create the directories people can create inotify
381 * watches in. Note that some applications might check for the
382 * existence of /run/systemd/machines/ to determine whether
383 * machined is available, so please always make sure this
384 * check stays in. */
385 mkdir_label("/run/systemd/machines", 0755);
386
387 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
388
389 m = manager_new();
390 if (!m) {
391 r = log_oom();
392 goto finish;
393 }
394
395 r = manager_startup(m);
396 if (r < 0) {
397 log_error_errno(r, "Failed to fully start up daemon: %m");
398 goto finish;
399 }
400
401 log_debug("systemd-machined running as pid "PID_FMT, getpid());
402
403 sd_notify(false,
404 "READY=1\n"
405 "STATUS=Processing requests...");
406
407 r = manager_run(m);
408
409 log_debug("systemd-machined stopped as pid "PID_FMT, getpid());
410
411 finish:
412 manager_free(m);
413
414 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
415 }