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