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