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