]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machined.c
unit: when deserializing cgroup path add it back into cgroup hashmap
[thirdparty/systemd.git] / src / machine / machined.c
CommitLineData
1ee306e1
LP
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 <pwd.h>
24#include <fcntl.h>
25#include <string.h>
26#include <unistd.h>
27#include <sys/epoll.h>
28
29#include <systemd/sd-daemon.h>
30
31#include "machined.h"
32#include "dbus-common.h"
33#include "dbus-loop.h"
34#include "strv.h"
35#include "conf-parser.h"
36#include "mkdir.h"
37#include "cgroup-util.h"
38
39Manager *manager_new(void) {
40 Manager *m;
41
42 m = new0(Manager, 1);
43 if (!m)
44 return NULL;
45
46 m->bus_fd = -1;
47 m->epoll_fd = -1;
48
49 m->machines = hashmap_new(string_hash_func, string_compare_func);
50 m->machine_units = hashmap_new(string_hash_func, string_compare_func);
51
52 if (!m->machines || !m->machine_units) {
53 manager_free(m);
54 return NULL;
55 }
56
57 return m;
58}
59
60void manager_free(Manager *m) {
61 Machine *machine;
62
63 assert(m);
64
65 while ((machine = hashmap_first(m->machines)))
66 machine_free(machine);
67
68 hashmap_free(m->machines);
69 hashmap_free(m->machine_units);
70
71 if (m->bus) {
72 dbus_connection_flush(m->bus);
73 dbus_connection_close(m->bus);
74 dbus_connection_unref(m->bus);
75 }
76
77 if (m->bus_fd >= 0)
78 close_nointr_nofail(m->bus_fd);
79
80 if (m->epoll_fd >= 0)
81 close_nointr_nofail(m->epoll_fd);
82
83 free(m);
84}
85
86int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
87 Machine *machine;
88
89 assert(m);
90 assert(name);
91
92 machine = hashmap_get(m->machines, name);
93 if (machine) {
94 if (_machine)
95 *_machine = machine;
96
97 return 0;
98 }
99
100 machine = machine_new(m, name);
aba8b84e 101 if (!machine)
1ee306e1
LP
102 return -ENOMEM;
103
104 if (_machine)
105 *_machine = machine;
106
107 return 0;
108}
109
110int manager_enumerate_machines(Manager *m) {
111 _cleanup_closedir_ DIR *d = NULL;
112 struct dirent *de;
113 int r = 0;
114
115 assert(m);
116
117 /* Read in machine data stored on disk */
118 d = opendir("/run/systemd/machines");
119 if (!d) {
120 if (errno == ENOENT)
121 return 0;
122
123 log_error("Failed to open /run/systemd/machines: %m");
124 return -errno;
125 }
126
127 FOREACH_DIRENT(de, d, return -errno) {
128 struct Machine *machine;
129 int k;
130
131 if (!dirent_is_file(de))
132 continue;
133
134 k = manager_add_machine(m, de->d_name, &machine);
135 if (k < 0) {
136 log_error("Failed to add machine by file name %s: %s", de->d_name, strerror(-k));
137
138 r = k;
139 continue;
140 }
141
142 machine_add_to_gc_queue(machine);
143
144 k = machine_load(machine);
145 if (k < 0)
146 r = k;
147 }
148
149 return r;
150}
151
152int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
153 _cleanup_free_ char *unit = NULL;
154 Machine *mm;
155 int r;
156
157 assert(m);
158 assert(pid >= 1);
159 assert(machine);
160
161 r = cg_pid_get_unit(pid, &unit);
162 if (r < 0)
163 return r;
164
165 mm = hashmap_get(m->machine_units, unit);
166 if (!mm)
167 return 0;
168
169 *machine = mm;
170 return 1;
171}
172
173static int manager_connect_bus(Manager *m) {
174 DBusError error;
175 int r;
176 struct epoll_event ev = {
177 .events = EPOLLIN,
178 .data.u32 = FD_BUS,
179 };
180
181 assert(m);
182 assert(!m->bus);
183 assert(m->bus_fd < 0);
184
185 dbus_error_init(&error);
186
187 m->bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
188 if (!m->bus) {
189 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
190 r = -ECONNREFUSED;
191 goto fail;
192 }
193
194 if (!dbus_connection_register_object_path(m->bus, "/org/freedesktop/machine1", &bus_manager_vtable, m) ||
195 !dbus_connection_register_fallback(m->bus, "/org/freedesktop/machine1/machine", &bus_machine_vtable, m) ||
196 !dbus_connection_add_filter(m->bus, bus_message_filter, m, NULL)) {
197 r = log_oom();
198 goto fail;
199 }
200
201 dbus_bus_add_match(m->bus,
202 "type='signal',"
203 "sender='org.freedesktop.systemd1',"
204 "interface='org.freedesktop.systemd1.Manager',"
205 "member='JobRemoved',"
206 "path='/org/freedesktop/systemd1'",
207 &error);
208 if (dbus_error_is_set(&error)) {
209 log_error("Failed to add match for JobRemoved: %s", bus_error_message(&error));
210 dbus_error_free(&error);
211 }
212
943aca8e
LP
213 dbus_bus_add_match(m->bus,
214 "type='signal',"
215 "sender='org.freedesktop.systemd1',"
216 "interface='org.freedesktop.systemd1.Manager',"
217 "member='UnitRemoved',"
218 "path='/org/freedesktop/systemd1'",
219 &error);
220 if (dbus_error_is_set(&error)) {
221 log_error("Failed to add match for UnitRemoved: %s", bus_error_message(&error));
222 dbus_error_free(&error);
223 }
224
1ee306e1
LP
225 dbus_bus_add_match(m->bus,
226 "type='signal',"
227 "sender='org.freedesktop.systemd1',"
228 "interface='org.freedesktop.DBus.Properties',"
229 "member='PropertiesChanged'",
230 &error);
231 if (dbus_error_is_set(&error)) {
232 log_error("Failed to add match for PropertiesChanged: %s", bus_error_message(&error));
233 dbus_error_free(&error);
234 }
235
236 r = bus_method_call_with_reply(
237 m->bus,
238 "org.freedesktop.systemd1",
239 "/org/freedesktop/systemd1",
240 "org.freedesktop.systemd1.Manager",
241 "Subscribe",
242 NULL,
243 &error,
244 DBUS_TYPE_INVALID);
245 if (r < 0) {
246 log_error("Failed to enable subscription: %s", bus_error(&error, r));
247 dbus_error_free(&error);
248 }
249
250 r = dbus_bus_request_name(m->bus, "org.freedesktop.machine1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
251 if (dbus_error_is_set(&error)) {
252 log_error("Failed to register name on bus: %s", bus_error_message(&error));
253 r = -EIO;
254 goto fail;
255 }
256
257 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
258 log_error("Failed to acquire name.");
259 r = -EEXIST;
260 goto fail;
261 }
262
263 m->bus_fd = bus_loop_open(m->bus);
264 if (m->bus_fd < 0) {
265 r = m->bus_fd;
266 goto fail;
267 }
268
269 if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->bus_fd, &ev) < 0)
270 goto fail;
271
272 return 0;
273
274fail:
275 dbus_error_free(&error);
276
277 return r;
278}
279
280void manager_gc(Manager *m, bool drop_not_started) {
281 Machine *machine;
282
283 assert(m);
284
285 while ((machine = m->machine_gc_queue)) {
286 LIST_REMOVE(Machine, gc_queue, m->machine_gc_queue, machine);
287 machine->in_gc_queue = false;
288
289 if (machine_check_gc(machine, drop_not_started) == 0) {
290 machine_stop(machine);
291 machine_free(machine);
292 }
293 }
294}
295
296int manager_startup(Manager *m) {
297 int r;
298 Machine *machine;
299 Iterator i;
300
301 assert(m);
302 assert(m->epoll_fd <= 0);
303
304 m->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
305 if (m->epoll_fd < 0)
306 return -errno;
307
308 /* Connect to the bus */
309 r = manager_connect_bus(m);
310 if (r < 0)
311 return r;
312
313 /* Deserialize state */
314 manager_enumerate_machines(m);
315
316 /* Remove stale objects before we start them */
317 manager_gc(m, false);
318
319 /* And start everything */
320 HASHMAP_FOREACH(machine, m->machines, i)
321 machine_start(machine);
322
323 return 0;
324}
325
326int manager_run(Manager *m) {
327 assert(m);
328
329 for (;;) {
330 struct epoll_event event;
331 int n;
332
333 manager_gc(m, true);
334
335 if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE)
336 continue;
337
338 manager_gc(m, true);
339
340 n = epoll_wait(m->epoll_fd, &event, 1, -1);
341 if (n < 0) {
342 if (errno == EINTR || errno == EAGAIN)
343 continue;
344
345 log_error("epoll() failed: %m");
346 return -errno;
347 }
348
349 if (n == 0)
350 continue;
351
352 switch (event.data.u32) {
353
354 case FD_BUS:
355 bus_loop_dispatch(m->bus_fd);
356 break;
357
358 default:
359 assert_not_reached("Unknown fd");
360 }
361 }
362
363 return 0;
364}
365
366int main(int argc, char *argv[]) {
367 Manager *m = NULL;
368 int r;
369
370 log_set_target(LOG_TARGET_AUTO);
371 log_set_facility(LOG_AUTH);
372 log_parse_environment();
373 log_open();
374
375 umask(0022);
376
377 if (argc != 1) {
378 log_error("This program takes no arguments.");
379 r = -EINVAL;
380 goto finish;
381 }
382
383 /* Always create the directories people can create inotify
384 * watches in. Note that some applications might check for the
385 * existence of /run/systemd/seats/ to determine whether
386 * machined is available, so please always make sure this check
387 * stays in. */
388 mkdir_label("/run/systemd/machines", 0755);
389
390 m = manager_new();
391 if (!m) {
392 r = log_oom();
393 goto finish;
394 }
395
396 r = manager_startup(m);
397 if (r < 0) {
398 log_error("Failed to fully start up daemon: %s", strerror(-r));
399 goto finish;
400 }
401
402 log_debug("systemd-machined running as pid %lu", (unsigned long) getpid());
403
404 sd_notify(false,
405 "READY=1\n"
406 "STATUS=Processing requests...");
407
408 r = manager_run(m);
409
410 log_debug("systemd-machined stopped as pid %lu", (unsigned long) getpid());
411
412finish:
413 sd_notify(false,
414 "STATUS=Shutting down...");
415
416 if (m)
417 manager_free(m);
418
419 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
420}