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