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