chiark / gitweb /
f2803a18c977bfb38599e9f15f46123679c391ef
[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         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
274 fail:
275         dbus_error_free(&error);
276
277         return r;
278 }
279
280 void 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
296 int 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
326 int 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
366 int 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
412 finish:
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 }