chiark / gitweb /
machined: simplifications
[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
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_func, string_compare_func);
48         m->machine_units = hashmap_new(string_hash_func, string_compare_func);
49
50         if (!m->machines || !m->machine_units) {
51                 manager_free(m);
52                 return NULL;
53         }
54
55         r = sd_event_new(&m->event);
56         if (r < 0) {
57                 manager_free(m);
58                 return NULL;
59         }
60
61         return m;
62 }
63
64 void manager_free(Manager *m) {
65         Machine *machine;
66
67         assert(m);
68
69         while ((machine = hashmap_first(m->machines)))
70                 machine_free(machine);
71
72         hashmap_free(m->machines);
73         hashmap_free(m->machine_units);
74
75         sd_bus_unref(m->bus);
76         sd_event_unref(m->event);
77
78         free(m);
79 }
80
81 int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
82         Machine *machine;
83
84         assert(m);
85         assert(name);
86
87         machine = hashmap_get(m->machines, name);
88         if (!machine) {
89                 machine = machine_new(m, name);
90                 if (!machine)
91                         return -ENOMEM;
92         }
93
94         if (_machine)
95                 *_machine = machine;
96
97         return 0;
98 }
99
100 int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
101         _cleanup_free_ char *unit = NULL;
102         Machine *mm;
103         int r;
104
105         assert(m);
106         assert(pid >= 1);
107         assert(machine);
108
109         r = cg_pid_get_unit(pid, &unit);
110         if (r < 0)
111                 return r;
112
113         mm = hashmap_get(m->machine_units, unit);
114         if (!mm)
115                 return 0;
116
117         *machine = mm;
118         return 1;
119 }
120
121 int manager_enumerate_machines(Manager *m) {
122         _cleanup_closedir_ DIR *d = NULL;
123         struct dirent *de;
124         int r = 0;
125
126         assert(m);
127
128         /* Read in machine data stored on disk */
129         d = opendir("/run/systemd/machines");
130         if (!d) {
131                 if (errno == ENOENT)
132                         return 0;
133
134                 log_error("Failed to open /run/systemd/machines: %m");
135                 return -errno;
136         }
137
138         FOREACH_DIRENT(de, d, return -errno) {
139                 struct Machine *machine;
140                 int k;
141
142                 if (!dirent_is_file(de))
143                         continue;
144
145                 k = manager_add_machine(m, de->d_name, &machine);
146                 if (k < 0) {
147                         log_error("Failed to add machine by file name %s: %s", de->d_name, strerror(-k));
148
149                         r = k;
150                         continue;
151                 }
152
153                 machine_add_to_gc_queue(machine);
154
155                 k = machine_load(machine);
156                 if (k < 0)
157                         r = k;
158         }
159
160         return r;
161 }
162
163 static int manager_connect_bus(Manager *m) {
164         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
165         int r;
166
167         assert(m);
168         assert(!m->bus);
169
170         r = sd_bus_open_system(&m->bus);
171         if (r < 0) {
172                 log_error("Failed to connect to system bus: %s", strerror(-r));
173                 return r;
174         }
175
176         r = sd_bus_add_object_vtable(m->bus, "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", manager_vtable, m);
177         if (r < 0) {
178                 log_error("Failed to add manager object vtable: %s", strerror(-r));
179                 return r;
180         }
181
182         r = sd_bus_add_fallback_vtable(m->bus, "/org/freedesktop/machine1/machine", "org.freedesktop.machine1.Machine", machine_vtable, machine_object_find, m);
183         if (r < 0) {
184                 log_error("Failed to add machine object vtable: %s", strerror(-r));
185                 return r;
186         }
187
188         r = sd_bus_add_node_enumerator(m->bus, "/org/freedesktop/machine1/machine", machine_node_enumerator, m);
189         if (r < 0) {
190                 log_error("Failed to add machine enumerator: %s", strerror(-r));
191                 return r;
192         }
193
194         r = sd_bus_add_match(m->bus,
195                              "type='signal',"
196                              "sender='org.freedesktop.systemd1',"
197                              "interface='org.freedesktop.systemd1.Manager',"
198                              "member='JobRemoved',"
199                              "path='/org/freedesktop/systemd1'",
200                              match_job_removed,
201                              m);
202         if (r < 0) {
203                 log_error("Failed to add match for JobRemoved: %s", strerror(-r));
204                 return r;
205         }
206
207         r = sd_bus_add_match(m->bus,
208                              "type='signal',"
209                              "sender='org.freedesktop.systemd1',"
210                              "interface='org.freedesktop.systemd1.Manager',"
211                              "member='UnitRemoved',"
212                              "path='/org/freedesktop/systemd1'",
213                              match_unit_removed,
214                              m);
215         if (r < 0) {
216                 log_error("Failed to add match for UnitRemoved: %s", strerror(-r));
217                 return r;
218         }
219
220         r = sd_bus_add_match(m->bus,
221                              "type='signal',"
222                              "sender='org.freedesktop.systemd1',"
223                              "interface='org.freedesktop.DBus.Properties',"
224                              "member='PropertiesChanged'",
225                              match_properties_changed,
226                              m);
227         if (r < 0) {
228                 log_error("Failed to add match for PropertiesChanged: %s", strerror(-r));
229                 return r;
230         }
231
232         r = sd_bus_add_match(m->bus,
233                              "type='signal',"
234                              "sender='org.freedesktop.systemd1',"
235                              "interface='org.freedesktop.systemd1.Manager',"
236                              "member='Reloading',"
237                              "path='/org/freedesktop/systemd1'",
238                              match_reloading,
239                              m);
240         if (r < 0) {
241                 log_error("Failed to add match for Reloading: %s", strerror(-r));
242                 return r;
243         }
244
245         r = sd_bus_call_method(
246                         m->bus,
247                         "org.freedesktop.systemd1",
248                         "/org/freedesktop/systemd1",
249                         "org.freedesktop.systemd1.Manager",
250                         "Subscribe",
251                         &error,
252                         NULL, NULL);
253         if (r < 0) {
254                 log_error("Failed to enable subscription: %s", bus_error_message(&error, r));
255                 return r;
256         }
257
258         r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", SD_BUS_NAME_DO_NOT_QUEUE);
259         if (r < 0) {
260                 log_error("Failed to register name: %s", strerror(-r));
261                 return r;
262         }
263
264         if (r != SD_BUS_NAME_PRIMARY_OWNER)  {
265                 log_error("Failed to acquire name.");
266                 return -EEXIST;
267         }
268
269         r = sd_bus_attach_event(m->bus, m->event, 0);
270         if (r < 0) {
271                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
272                 return r;
273         }
274
275         return 0;
276 }
277
278 void manager_gc(Manager *m, bool drop_not_started) {
279         Machine *machine;
280
281         assert(m);
282
283         while ((machine = m->machine_gc_queue)) {
284                 LIST_REMOVE(gc_queue, m->machine_gc_queue, machine);
285                 machine->in_gc_queue = false;
286
287                 if (!machine_check_gc(machine, drop_not_started)) {
288                         machine_stop(machine);
289                         machine_free(machine);
290                 }
291         }
292 }
293
294 int manager_startup(Manager *m) {
295         Machine *machine;
296         Iterator i;
297         int r;
298
299         assert(m);
300
301         /* Connect to the bus */
302         r = manager_connect_bus(m);
303         if (r < 0)
304                 return r;
305
306         /* Deserialize state */
307         manager_enumerate_machines(m);
308
309         /* Remove stale objects before we start them */
310         manager_gc(m, false);
311
312         /* And start everything */
313         HASHMAP_FOREACH(machine, m->machines, i)
314                 machine_start(machine, NULL, NULL);
315
316         return 0;
317 }
318
319 int manager_run(Manager *m) {
320         int r;
321
322         assert(m);
323
324         for (;;) {
325                 r = sd_event_get_state(m->event);
326                 if (r < 0)
327                         return r;
328                 if (r == SD_EVENT_FINISHED)
329                         return 0;
330
331                 manager_gc(m, true);
332
333                 r = sd_event_run(m->event, (uint64_t) -1);
334                 if (r < 0)
335                         return r;
336         }
337
338         return 0;
339 }
340
341 int main(int argc, char *argv[]) {
342         Manager *m = NULL;
343         int r;
344
345         log_set_target(LOG_TARGET_AUTO);
346         log_set_facility(LOG_AUTH);
347         log_parse_environment();
348         log_open();
349
350         umask(0022);
351
352         if (argc != 1) {
353                 log_error("This program takes no arguments.");
354                 r = -EINVAL;
355                 goto finish;
356         }
357
358         /* Always create the directories people can create inotify
359          * watches in. Note that some applications might check for the
360          * existence of /run/systemd/machines/ to determine whether
361          * machined is available, so please always make sure this
362          * check stays in. */
363         mkdir_label("/run/systemd/machines", 0755);
364
365         m = manager_new();
366         if (!m) {
367                 r = log_oom();
368                 goto finish;
369         }
370
371         r = manager_startup(m);
372         if (r < 0) {
373                 log_error("Failed to fully start up daemon: %s", strerror(-r));
374                 goto finish;
375         }
376
377         log_debug("systemd-machined running as pid %lu", (unsigned long) getpid());
378
379         sd_notify(false,
380                   "READY=1\n"
381                   "STATUS=Processing requests...");
382
383         r = manager_run(m);
384
385         log_debug("systemd-machined stopped as pid %lu", (unsigned long) getpid());
386
387 finish:
388         sd_notify(false,
389                   "STATUS=Shutting down...");
390
391         if (m)
392                 manager_free(m);
393
394         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
395 }