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