chiark / gitweb /
switch from udev keymaps to hwdb
[elogind.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 <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
39 Manager *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
60 void 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
86 int 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);
101         if (!machine)
102                 return -ENOMEM;
103
104         if (_machine)
105                 *_machine = machine;
106
107         return 0;
108 }
109
110 int 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
152 int 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
173 static 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
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
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         dbus_bus_add_match(m->bus,
237                            "type='signal',"
238                            "sender='org.freedesktop.systemd1',"
239                            "interface='org.freedesktop.systemd1.Manager',"
240                            "member='Reloading',"
241                            "path='/org/freedesktop/systemd1'",
242                            &error);
243         if (dbus_error_is_set(&error)) {
244                 log_error("Failed to add match for Reloading: %s", bus_error_message(&error));
245                 dbus_error_free(&error);
246         }
247
248         r = bus_method_call_with_reply(
249                         m->bus,
250                         "org.freedesktop.systemd1",
251                         "/org/freedesktop/systemd1",
252                         "org.freedesktop.systemd1.Manager",
253                         "Subscribe",
254                         NULL,
255                         &error,
256                         DBUS_TYPE_INVALID);
257         if (r < 0) {
258                 log_error("Failed to enable subscription: %s", bus_error(&error, r));
259                 dbus_error_free(&error);
260         }
261
262         r = dbus_bus_request_name(m->bus, "org.freedesktop.machine1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
263         if (dbus_error_is_set(&error)) {
264                 log_error("Failed to register name on bus: %s", bus_error_message(&error));
265                 r = -EIO;
266                 goto fail;
267         }
268
269         if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)  {
270                 log_error("Failed to acquire name.");
271                 r = -EEXIST;
272                 goto fail;
273         }
274
275         m->bus_fd = bus_loop_open(m->bus);
276         if (m->bus_fd < 0) {
277                 r = m->bus_fd;
278                 goto fail;
279         }
280
281         if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->bus_fd, &ev) < 0)
282                 goto fail;
283
284         return 0;
285
286 fail:
287         dbus_error_free(&error);
288
289         return r;
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(Machine, gc_queue, m->machine_gc_queue, machine);
299                 machine->in_gc_queue = false;
300
301                 if (machine_check_gc(machine, drop_not_started) == 0) {
302                         machine_stop(machine);
303                         machine_free(machine);
304                 }
305         }
306 }
307
308 int manager_startup(Manager *m) {
309         int r;
310         Machine *machine;
311         Iterator i;
312
313         assert(m);
314         assert(m->epoll_fd <= 0);
315
316         m->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
317         if (m->epoll_fd < 0)
318                 return -errno;
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);
334
335         return 0;
336 }
337
338 int manager_run(Manager *m) {
339         assert(m);
340
341         for (;;) {
342                 struct epoll_event event;
343                 int n;
344
345                 manager_gc(m, true);
346
347                 if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE)
348                         continue;
349
350                 manager_gc(m, true);
351
352                 n = epoll_wait(m->epoll_fd, &event, 1, -1);
353                 if (n < 0) {
354                         if (errno == EINTR || errno == EAGAIN)
355                                 continue;
356
357                         log_error("epoll() failed: %m");
358                         return -errno;
359                 }
360
361                 if (n == 0)
362                         continue;
363
364                 switch (event.data.u32) {
365
366                 case FD_BUS:
367                         bus_loop_dispatch(m->bus_fd);
368                         break;
369
370                 default:
371                         assert_not_reached("Unknown fd");
372                 }
373         }
374
375         return 0;
376 }
377
378 int main(int argc, char *argv[]) {
379         Manager *m = NULL;
380         int r;
381
382         log_set_target(LOG_TARGET_AUTO);
383         log_set_facility(LOG_AUTH);
384         log_parse_environment();
385         log_open();
386
387         umask(0022);
388
389         if (argc != 1) {
390                 log_error("This program takes no arguments.");
391                 r = -EINVAL;
392                 goto finish;
393         }
394
395         /* Always create the directories people can create inotify
396          * watches in. Note that some applications might check for the
397          * existence of /run/systemd/seats/ to determine whether
398          * machined is available, so please always make sure this check
399          * stays in. */
400         mkdir_label("/run/systemd/machines", 0755);
401
402         m = manager_new();
403         if (!m) {
404                 r = log_oom();
405                 goto finish;
406         }
407
408         r = manager_startup(m);
409         if (r < 0) {
410                 log_error("Failed to fully start up daemon: %s", strerror(-r));
411                 goto finish;
412         }
413
414         log_debug("systemd-machined running as pid %lu", (unsigned long) getpid());
415
416         sd_notify(false,
417                   "READY=1\n"
418                   "STATUS=Processing requests...");
419
420         r = manager_run(m);
421
422         log_debug("systemd-machined stopped as pid %lu", (unsigned long) getpid());
423
424 finish:
425         sd_notify(false,
426                   "STATUS=Shutting down...");
427
428         if (m)
429                 manager_free(m);
430
431         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
432 }