chiark / gitweb /
56936fd673fd76d427c18eb8fdce7f2105279be5
[elogind.git] / src / path.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/inotify.h>
23 #include <sys/epoll.h>
24 #include <sys/ioctl.h>
25 #include <errno.h>
26 #include <unistd.h>
27
28 #include "unit.h"
29 #include "unit-name.h"
30 #include "path.h"
31 #include "dbus-path.h"
32
33 static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
34         [PATH_DEAD] = UNIT_INACTIVE,
35         [PATH_WAITING] = UNIT_ACTIVE,
36         [PATH_RUNNING] = UNIT_ACTIVE,
37         [PATH_MAINTENANCE] = UNIT_MAINTENANCE
38 };
39
40 static void path_done(Unit *u) {
41         Path *p = PATH(u);
42         PathSpec *s;
43
44         assert(p);
45
46         while ((s = p->specs)) {
47                 LIST_REMOVE(PathSpec, spec, p->specs, s);
48                 free(s);
49         }
50 }
51
52 int path_add_one_mount_link(Path *p, Mount *m) {
53         PathSpec *s;
54         int r;
55
56         assert(p);
57         assert(m);
58
59         if (p->meta.load_state != UNIT_LOADED ||
60             m->meta.load_state != UNIT_LOADED)
61                 return 0;
62
63         LIST_FOREACH(spec, s, p->specs) {
64
65                 if (!path_startswith(s->path, m->where))
66                         continue;
67
68                 if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
69                         return r;
70         }
71
72         return 0;
73 }
74
75 static int path_add_mount_links(Path *p) {
76         Meta *other;
77         int r;
78
79         assert(p);
80
81         LIST_FOREACH(units_per_type, other, p->meta.manager->units_per_type[UNIT_MOUNT])
82                 if ((r = path_add_one_mount_link(p, (Mount*) other)) < 0)
83                         return r;
84
85         return 0;
86 }
87
88 static int path_verify(Path *p) {
89         assert(p);
90
91         if (p->meta.load_state != UNIT_LOADED)
92                 return 0;
93
94         if (!p->specs) {
95                 log_error("%s lacks path setting. Refusing.", p->meta.id);
96                 return -EINVAL;
97         }
98
99         return 0;
100 }
101
102 static int path_load(Unit *u) {
103         Path *p = PATH(u);
104         int r;
105
106         assert(u);
107         assert(u->meta.load_state == UNIT_STUB);
108
109         if ((r = unit_load_fragment_and_dropin(u)) < 0)
110                 return r;
111
112         if (u->meta.load_state == UNIT_LOADED) {
113
114                 if (!p->unit)
115                         if ((r = unit_load_related_unit(u, ".service", &p->unit)))
116                                 return r;
117
118                 if ((r = unit_add_dependency(u, UNIT_BEFORE, p->unit, true)) < 0)
119                         return r;
120
121                 if ((r = path_add_mount_links(p)) < 0)
122                         return r;
123         }
124
125         return path_verify(p);
126 }
127
128 static void path_dump(Unit *u, FILE *f, const char *prefix) {
129         Path *p = PATH(u);
130         const char *prefix2;
131         char *p2;
132         PathSpec *s;
133
134         p2 = strappend(prefix, "\t");
135         prefix2 = p2 ? p2 : prefix;
136
137         fprintf(f,
138                 "%sPath State: %s\n"
139                 "%sUnit: %s\n",
140                 prefix, path_state_to_string(p->state),
141                 prefix, p->unit->meta.id);
142
143         LIST_FOREACH(spec, s, p->specs)
144                 fprintf(f,
145                         "%s%s: %s\n",
146                         prefix,
147                         path_type_to_string(s->type),
148                         s->path);
149
150         free(p2);
151 }
152
153 static void path_unwatch_one(Path *p, PathSpec *s) {
154
155         if (s->inotify_fd < 0)
156                 return;
157
158         unit_unwatch_fd(UNIT(p), &s->watch);
159
160         close_nointr_nofail(s->inotify_fd);
161         s->inotify_fd = -1;
162 }
163
164 static int path_watch_one(Path *p, PathSpec *s) {
165         static const int flags_table[_PATH_TYPE_MAX] = {
166                 [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF,
167                 [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
168                 [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_CREATE|IN_MOVED_TO
169         };
170
171         bool exists = false;
172         char *k;
173         int r;
174
175         assert(p);
176         assert(s);
177
178         path_unwatch_one(p, s);
179
180         if (!(k = strdup(s->path)))
181                 return -ENOMEM;
182
183         if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) {
184                 r = -errno;
185                 goto fail;
186         }
187
188         if (unit_watch_fd(UNIT(p), s->inotify_fd, EPOLLIN, &s->watch) < 0) {
189                 r = -errno;
190                 goto fail;
191         }
192
193         if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0)
194                 exists = true;
195
196         for (;;) {
197                 int flags;
198                 char *slash;
199
200                 /* This assumes the path was passed through path_kill_slashes()! */
201                 if (!(slash = strrchr(k, '/')))
202                         break;
203
204                 *slash = 0;
205
206                 flags = IN_DELETE_SELF|IN_MOVE_SELF;
207                 if (!exists)
208                         flags |= IN_CREATE | IN_MOVED_TO | IN_ATTRIB;
209
210                 if (inotify_add_watch(s->inotify_fd, k, flags) >= 0)
211                         exists = true;
212         }
213
214         return 0;
215
216 fail:
217         free(k);
218
219         path_unwatch_one(p, s);
220         return r;
221 }
222
223 static void path_unwatch(Path *p) {
224         PathSpec *s;
225
226         assert(p);
227
228         LIST_FOREACH(spec, s, p->specs)
229                 path_unwatch_one(p, s);
230 }
231
232 static int path_watch(Path *p) {
233         int r;
234         PathSpec *s;
235
236         assert(p);
237
238         LIST_FOREACH(spec, s, p->specs)
239                 if ((r = path_watch_one(p, s)) < 0)
240                         return r;
241
242         return 0;
243 }
244
245 static void path_set_state(Path *p, PathState state) {
246         PathState old_state;
247         assert(p);
248
249         old_state = p->state;
250         p->state = state;
251
252         if (state != PATH_WAITING)
253                 path_unwatch(p);
254
255         if (state != old_state)
256                 log_debug("%s changed %s -> %s",
257                           p->meta.id,
258                           path_state_to_string(old_state),
259                           path_state_to_string(state));
260
261         unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state]);
262 }
263
264 static void path_enter_waiting(Path *p, bool initial);
265
266 static int path_coldplug(Unit *u) {
267         Path *p = PATH(u);
268
269         assert(p);
270         assert(p->state == PATH_DEAD);
271
272         if (p->deserialized_state != p->state) {
273
274                 if (p->deserialized_state == PATH_WAITING ||
275                     p->deserialized_state == PATH_RUNNING)
276                         path_enter_waiting(p, true);
277                 else
278                         path_set_state(p, p->deserialized_state);
279         }
280
281         return 0;
282 }
283
284 static void path_enter_dead(Path *p, bool success) {
285         assert(p);
286
287         if (!success)
288                 p->failure = true;
289
290         path_set_state(p, p->failure ? PATH_MAINTENANCE : PATH_DEAD);
291 }
292
293 static void path_enter_running(Path *p) {
294         int r;
295         assert(p);
296
297         if ((r = manager_add_job(p->meta.manager, JOB_START, p->unit, JOB_REPLACE, true, NULL)) < 0)
298                 goto fail;
299
300         path_set_state(p, PATH_RUNNING);
301         return;
302
303 fail:
304         log_warning("%s failed to queue unit startup job: %s", p->meta.id, strerror(-r));
305         path_enter_dead(p, false);
306 }
307
308
309 static void path_enter_waiting(Path *p, bool initial) {
310         PathSpec *s;
311         int r;
312         bool good = false;
313
314         LIST_FOREACH(spec, s, p->specs) {
315
316                 switch (s->type) {
317
318                 case PATH_EXISTS:
319                         good = access(s->path, F_OK) >= 0;
320                         break;
321
322                 case PATH_DIRECTORY_NOT_EMPTY:
323                         good = dir_is_empty(s->path) == 0;
324                         break;
325
326                 case PATH_CHANGED: {
327                         bool b;
328
329                         b = access(s->path, F_OK) >= 0;
330                         good = !initial && b != s->previous_exists;
331                         s->previous_exists = b;
332                         break;
333                 }
334
335                 default:
336                         ;
337                 }
338
339                 if (good)
340                         break;
341         }
342
343         if (good) {
344                 path_enter_running(p);
345                 return;
346         }
347
348         if ((r = path_watch(p)) < 0)
349                 goto fail;
350
351         path_set_state(p, PATH_WAITING);
352         return;
353
354 fail:
355         log_warning("%s failed to enter waiting state: %s", p->meta.id, strerror(-r));
356         path_enter_dead(p, false);
357 }
358
359 static int path_start(Unit *u) {
360         Path *p = PATH(u);
361
362         assert(p);
363         assert(p->state == PATH_DEAD || p->state == PATH_MAINTENANCE);
364
365         if (p->unit->meta.load_state != UNIT_LOADED)
366                 return -ENOENT;
367
368         p->failure = false;
369 path_enter_waiting(p, true);
370         return 0;
371 }
372
373 static int path_stop(Unit *u) {
374         Path *p = PATH(u);
375
376         assert(p);
377         assert(p->state == PATH_WAITING || p->state == PATH_RUNNING);
378
379         path_enter_dead(p, true);
380         return 0;
381 }
382
383 static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
384         Path *p = PATH(u);
385
386         assert(u);
387         assert(f);
388         assert(fds);
389
390         unit_serialize_item(u, f, "state", path_state_to_string(p->state));
391
392         return 0;
393 }
394
395 static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
396         Path *p = PATH(u);
397
398         assert(u);
399         assert(key);
400         assert(value);
401         assert(fds);
402
403         if (streq(key, "state")) {
404                 PathState state;
405
406                 if ((state = path_state_from_string(value)) < 0)
407                         log_debug("Failed to parse state value %s", value);
408                 else
409                         p->deserialized_state = state;
410         } else
411                 log_debug("Unknown serialization key '%s'", key);
412
413         return 0;
414 }
415
416 static UnitActiveState path_active_state(Unit *u) {
417         assert(u);
418
419         return state_translation_table[PATH(u)->state];
420 }
421
422 static const char *path_sub_state_to_string(Unit *u) {
423         assert(u);
424
425         return path_state_to_string(PATH(u)->state);
426 }
427
428 static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
429         Path *p = PATH(u);
430         int l;
431         ssize_t k;
432         struct inotify_event *buf = NULL;
433         PathSpec *s;
434
435         assert(p);
436         assert(fd >= 0);
437
438         if (p->state != PATH_WAITING)
439                 return;
440
441         log_debug("inotify wakeup on %s.", u->meta.id);
442
443         if (events != EPOLLIN) {
444                 log_error("Got Invalid poll event on inotify.");
445                 goto fail;
446         }
447
448         LIST_FOREACH(spec, s, p->specs)
449                 if (s->inotify_fd == fd)
450                         break;
451
452         if (!s) {
453                 log_error("Got event on unknown fd.");
454                 goto fail;
455         }
456
457         if (ioctl(fd, FIONREAD, &l) < 0) {
458                 log_error("FIONREAD failed: %s", strerror(errno));
459                 goto fail;
460         }
461
462         if (!(buf = malloc(l))) {
463                 log_error("Failed to allocate buffer: %s", strerror(-ENOMEM));
464                 goto fail;
465         }
466
467         if ((k = read(fd, buf, l)) < 0) {
468                 log_error("Failed to read inotify event: %s", strerror(-errno));
469                 goto fail;
470         }
471
472         if ((size_t) k < sizeof(struct inotify_event) ||
473             (size_t) k < sizeof(struct inotify_event) + buf->len) {
474                 log_error("inotify event too small.");
475                 goto fail;
476         }
477
478         if (s->type == PATH_CHANGED && s->primary_wd == buf->wd)
479                 path_enter_running(p);
480         else
481                 path_enter_waiting(p, false);
482
483         free(buf);
484
485         return;
486
487 fail:
488         free(buf);
489         path_enter_dead(p, false);
490 }
491
492 void path_unit_notify(Unit *u, UnitActiveState new_state) {
493         char *n;
494         int r;
495         Iterator i;
496
497         if (u->meta.type == UNIT_PATH)
498                 return;
499
500         SET_FOREACH(n, u->meta.names, i) {
501                 char *k;
502                 Unit *t;
503                 Path *p;
504
505                 if (!(k = unit_name_change_suffix(n, ".path"))) {
506                         r = -ENOMEM;
507                         goto fail;
508                 }
509
510                 t = manager_get_unit(u->meta.manager, k);
511                 free(k);
512
513                 if (!t)
514                         continue;
515
516                 if (t->meta.load_state != UNIT_LOADED)
517                         continue;
518
519                 p = PATH(t);
520
521                 if (p->unit != u)
522                         continue;
523
524                 if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) {
525                         log_debug("%s got notified about unit deactivation.", p->meta.id);
526                         path_enter_waiting(p, false);
527                 }
528         }
529
530         return;
531
532 fail:
533         log_error("Failed find path unit: %s", strerror(-r));
534 }
535
536 static const char* const path_state_table[_PATH_STATE_MAX] = {
537         [PATH_DEAD] = "dead",
538         [PATH_WAITING] = "waiting",
539         [PATH_RUNNING] = "running",
540         [PATH_MAINTENANCE] = "maintenance"
541 };
542
543 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
544
545 static const char* const path_type_table[_PATH_TYPE_MAX] = {
546         [PATH_EXISTS] = "PathExists",
547         [PATH_CHANGED] = "PathChanged",
548         [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty"
549 };
550
551 DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
552
553 const UnitVTable path_vtable = {
554         .suffix = ".path",
555
556         .done = path_done,
557         .load = path_load,
558
559         .coldplug = path_coldplug,
560
561         .dump = path_dump,
562
563         .start = path_start,
564         .stop = path_stop,
565
566         .serialize = path_serialize,
567         .deserialize_item = path_deserialize_item,
568
569         .active_state = path_active_state,
570         .sub_state_to_string = path_sub_state_to_string,
571
572         .fd_event = path_fd_event,
573
574         .bus_message_handler = bus_path_message_handler
575 };