chiark / gitweb /
label: unify code to make directories, symlinks
[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("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("Failed to add machine by file name %s: %s", de->d_name, strerror(-k));
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                 log_error("Failed to connect to system bus: %s", strerror(-r));
142                 return r;
143         }
144
145         r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", manager_vtable, m);
146         if (r < 0) {
147                 log_error("Failed to add manager object vtable: %s", strerror(-r));
148                 return r;
149         }
150
151         r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/machine", "org.freedesktop.machine1.Machine", machine_vtable, machine_object_find, m);
152         if (r < 0) {
153                 log_error("Failed to add machine object vtable: %s", strerror(-r));
154                 return r;
155         }
156
157         r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/machine", machine_node_enumerator, m);
158         if (r < 0) {
159                 log_error("Failed to add machine enumerator: %s", strerror(-r));
160                 return r;
161         }
162
163         r = sd_bus_add_match(m->bus,
164                              NULL,
165                              "type='signal',"
166                              "sender='org.freedesktop.systemd1',"
167                              "interface='org.freedesktop.systemd1.Manager',"
168                              "member='JobRemoved',"
169                              "path='/org/freedesktop/systemd1'",
170                              match_job_removed,
171                              m);
172         if (r < 0) {
173                 log_error("Failed to add match for JobRemoved: %s", strerror(-r));
174                 return r;
175         }
176
177         r = sd_bus_add_match(m->bus,
178                              NULL,
179                              "type='signal',"
180                              "sender='org.freedesktop.systemd1',"
181                              "interface='org.freedesktop.systemd1.Manager',"
182                              "member='UnitRemoved',"
183                              "path='/org/freedesktop/systemd1'",
184                              match_unit_removed,
185                              m);
186         if (r < 0) {
187                 log_error("Failed to add match for UnitRemoved: %s", strerror(-r));
188                 return r;
189         }
190
191         r = sd_bus_add_match(m->bus,
192                              NULL,
193                              "type='signal',"
194                              "sender='org.freedesktop.systemd1',"
195                              "interface='org.freedesktop.DBus.Properties',"
196                              "member='PropertiesChanged'",
197                              match_properties_changed,
198                              m);
199         if (r < 0) {
200                 log_error("Failed to add match for PropertiesChanged: %s", strerror(-r));
201                 return r;
202         }
203
204         r = sd_bus_add_match(m->bus,
205                              NULL,
206                              "type='signal',"
207                              "sender='org.freedesktop.systemd1',"
208                              "interface='org.freedesktop.systemd1.Manager',"
209                              "member='Reloading',"
210                              "path='/org/freedesktop/systemd1'",
211                              match_reloading,
212                              m);
213         if (r < 0) {
214                 log_error("Failed to add match for Reloading: %s", strerror(-r));
215                 return r;
216         }
217
218         r = sd_bus_call_method(
219                         m->bus,
220                         "org.freedesktop.systemd1",
221                         "/org/freedesktop/systemd1",
222                         "org.freedesktop.systemd1.Manager",
223                         "Subscribe",
224                         &error,
225                         NULL, NULL);
226         if (r < 0) {
227                 log_error("Failed to enable subscription: %s", bus_error_message(&error, r));
228                 return r;
229         }
230
231         r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", 0);
232         if (r < 0) {
233                 log_error("Failed to register name: %s", strerror(-r));
234                 return r;
235         }
236
237         r = sd_bus_attach_event(m->bus, m->event, 0);
238         if (r < 0) {
239                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
240                 return r;
241         }
242
243         return 0;
244 }
245
246 void manager_gc(Manager *m, bool drop_not_started) {
247         Machine *machine;
248
249         assert(m);
250
251         while ((machine = m->machine_gc_queue)) {
252                 LIST_REMOVE(gc_queue, m->machine_gc_queue, machine);
253                 machine->in_gc_queue = false;
254
255                 if (!machine_check_gc(machine, drop_not_started)) {
256                         machine_stop(machine);
257                         machine_free(machine);
258                 }
259         }
260 }
261
262 int manager_startup(Manager *m) {
263         Machine *machine;
264         Iterator i;
265         int r;
266
267         assert(m);
268
269         /* Connect to the bus */
270         r = manager_connect_bus(m);
271         if (r < 0)
272                 return r;
273
274         /* Deserialize state */
275         manager_enumerate_machines(m);
276
277         /* Remove stale objects before we start them */
278         manager_gc(m, false);
279
280         /* And start everything */
281         HASHMAP_FOREACH(machine, m->machines, i)
282                 machine_start(machine, NULL, NULL);
283
284         return 0;
285 }
286
287 static bool check_idle(void *userdata) {
288         Manager *m = userdata;
289
290         manager_gc(m, true);
291
292         return hashmap_isempty(m->machines);
293 }
294
295 int manager_run(Manager *m) {
296         assert(m);
297
298         return bus_event_loop_with_idle(
299                         m->event,
300                         m->bus,
301                         "org.freedesktop.machine1",
302                         DEFAULT_EXIT_USEC,
303                         check_idle, m);
304 }
305
306 int main(int argc, char *argv[]) {
307         Manager *m = NULL;
308         int r;
309
310         log_set_target(LOG_TARGET_AUTO);
311         log_set_facility(LOG_AUTH);
312         log_parse_environment();
313         log_open();
314
315         umask(0022);
316
317         if (argc != 1) {
318                 log_error("This program takes no arguments.");
319                 r = -EINVAL;
320                 goto finish;
321         }
322
323         /* Always create the directories people can create inotify
324          * watches in. Note that some applications might check for the
325          * existence of /run/systemd/machines/ to determine whether
326          * machined is available, so please always make sure this
327          * check stays in. */
328         mkdir_label("/run/systemd/machines", 0755);
329
330         m = manager_new();
331         if (!m) {
332                 r = log_oom();
333                 goto finish;
334         }
335
336         r = manager_startup(m);
337         if (r < 0) {
338                 log_error("Failed to fully start up daemon: %s", strerror(-r));
339                 goto finish;
340         }
341
342         log_debug("systemd-machined running as pid "PID_FMT, getpid());
343
344         sd_notify(false,
345                   "READY=1\n"
346                   "STATUS=Processing requests...");
347
348         r = manager_run(m);
349
350         log_debug("systemd-machined stopped as pid "PID_FMT, getpid());
351
352 finish:
353         if (m)
354                 manager_free(m);
355
356         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
357 }