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