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