chiark / gitweb /
systemctl: fix a leak
[elogind.git] / src / core / path.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
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 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 <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 "mkdir.h"
32 #include "dbus-path.h"
33 #include "special.h"
34 #include "path-util.h"
35 #include "macro.h"
36 #include "bus-util.h"
37 #include "bus-error.h"
38
39 static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
40         [PATH_DEAD] = UNIT_INACTIVE,
41         [PATH_WAITING] = UNIT_ACTIVE,
42         [PATH_RUNNING] = UNIT_ACTIVE,
43         [PATH_FAILED] = UNIT_FAILED
44 };
45
46 static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
47
48 int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
49
50         static const int flags_table[_PATH_TYPE_MAX] = {
51                 [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
52                 [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
53                 [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
54                 [PATH_MODIFIED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO|IN_MODIFY,
55                 [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO
56         };
57
58         bool exists = false;
59         char *slash, *oldslash = NULL;
60         int r;
61
62         assert(s);
63         assert(s->unit);
64         assert(handler);
65
66         path_spec_unwatch(s);
67
68         s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
69         if (s->inotify_fd < 0) {
70                 r = -errno;
71                 goto fail;
72         }
73
74         r = sd_event_add_io(s->unit->manager->event, &s->event_source, s->inotify_fd, EPOLLIN, handler, s);
75         if (r < 0)
76                 goto fail;
77
78         /* This assumes the path was passed through path_kill_slashes()! */
79
80         for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) {
81                 char *cut = NULL;
82                 int flags;
83                 char tmp;
84
85                 if (slash) {
86                         cut = slash + (slash == s->path);
87                         tmp = *cut;
88                         *cut = '\0';
89
90                         flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
91                 } else
92                         flags = flags_table[s->type];
93
94                 r = inotify_add_watch(s->inotify_fd, s->path, flags);
95                 if (r < 0) {
96                         if (errno == EACCES || errno == ENOENT) {
97                                 if (cut)
98                                         *cut = tmp;
99                                 break;
100                         }
101
102                         log_warning("Failed to add watch on %s: %s", s->path,
103                                     errno == ENOSPC ? "too many watches" : strerror(-r));
104                         r = -errno;
105                         if (cut)
106                                 *cut = tmp;
107                         goto fail;
108                 } else {
109                         exists = true;
110
111                         /* Path exists, we don't need to watch parent
112                            too closely. */
113                         if (oldslash) {
114                                 char *cut2 = oldslash + (oldslash == s->path);
115                                 char tmp2 = *cut2;
116                                 *cut2 = '\0';
117
118                                 inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF);
119                                 /* Error is ignored, the worst can happen is
120                                    we get spurious events. */
121
122                                 *cut2 = tmp2;
123                         }
124                 }
125
126                 if (cut)
127                         *cut = tmp;
128
129                 if (slash)
130                         oldslash = slash;
131                 else {
132                         /* whole path has been iterated over */
133                         s->primary_wd = r;
134                         break;
135                 }
136         }
137
138         if (!exists) {
139                 log_error_errno(errno, "Failed to add watch on any of the components of %s: %m",
140                           s->path);
141                 r = -errno; /* either EACCESS or ENOENT */
142                 goto fail;
143         }
144
145         return 0;
146
147 fail:
148         path_spec_unwatch(s);
149         return r;
150 }
151
152 void path_spec_unwatch(PathSpec *s) {
153         assert(s);
154
155         s->event_source = sd_event_source_unref(s->event_source);
156         s->inotify_fd = safe_close(s->inotify_fd);
157 }
158
159 int path_spec_fd_event(PathSpec *s, uint32_t revents) {
160         _cleanup_free_ uint8_t *buf = NULL;
161         struct inotify_event *e;
162         ssize_t k;
163         int l;
164         int r = 0;
165
166         if (revents != EPOLLIN) {
167                 log_error("Got invalid poll event on inotify.");
168                 return -EINVAL;
169         }
170
171         if (ioctl(s->inotify_fd, FIONREAD, &l) < 0)
172                 return log_error_errno(errno, "FIONREAD failed: %m");
173
174         assert(l > 0);
175
176         buf = malloc(l);
177         if (!buf)
178                 return log_oom();
179
180         k = read(s->inotify_fd, buf, l);
181         if (k < 0)
182                 return log_error_errno(errno, "Failed to read inotify event: %m");
183
184         e = (struct inotify_event*) buf;
185
186         while (k > 0) {
187                 size_t step;
188
189                 if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) &&
190                     s->primary_wd == e->wd)
191                         r = 1;
192
193                 step = sizeof(struct inotify_event) + e->len;
194                 assert(step <= (size_t) k);
195
196                 e = (struct inotify_event*) ((uint8_t*) e + step);
197                 k -= step;
198         }
199
200         return r;
201 }
202
203 static bool path_spec_check_good(PathSpec *s, bool initial) {
204         bool good = false;
205
206         switch (s->type) {
207
208         case PATH_EXISTS:
209                 good = access(s->path, F_OK) >= 0;
210                 break;
211
212         case PATH_EXISTS_GLOB:
213                 good = glob_exists(s->path) > 0;
214                 break;
215
216         case PATH_DIRECTORY_NOT_EMPTY: {
217                 int k;
218
219                 k = dir_is_empty(s->path);
220                 good = !(k == -ENOENT || k > 0);
221                 break;
222         }
223
224         case PATH_CHANGED:
225         case PATH_MODIFIED: {
226                 bool b;
227
228                 b = access(s->path, F_OK) >= 0;
229                 good = !initial && b != s->previous_exists;
230                 s->previous_exists = b;
231                 break;
232         }
233
234         default:
235                 ;
236         }
237
238         return good;
239 }
240
241 static void path_spec_mkdir(PathSpec *s, mode_t mode) {
242         int r;
243
244         if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB)
245                 return;
246
247         r = mkdir_p_label(s->path, mode);
248         if (r < 0)
249                 log_warning_errno(r, "mkdir(%s) failed: %m", s->path);
250 }
251
252 static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) {
253         fprintf(f,
254                 "%s%s: %s\n",
255                 prefix,
256                 path_type_to_string(s->type),
257                 s->path);
258 }
259
260 void path_spec_done(PathSpec *s) {
261         assert(s);
262         assert(s->inotify_fd == -1);
263
264         free(s->path);
265 }
266
267 static void path_init(Unit *u) {
268         Path *p = PATH(u);
269
270         assert(u);
271         assert(u->load_state == UNIT_STUB);
272
273         p->directory_mode = 0755;
274 }
275
276 void path_free_specs(Path *p) {
277         PathSpec *s;
278
279         assert(p);
280
281         while ((s = p->specs)) {
282                 path_spec_unwatch(s);
283                 LIST_REMOVE(spec, p->specs, s);
284                 path_spec_done(s);
285                 free(s);
286         }
287 }
288
289 static void path_done(Unit *u) {
290         Path *p = PATH(u);
291
292         assert(p);
293
294         path_free_specs(p);
295 }
296
297 static int path_add_mount_links(Path *p) {
298         PathSpec *s;
299         int r;
300
301         assert(p);
302
303         LIST_FOREACH(spec, s, p->specs) {
304                 r = unit_require_mounts_for(UNIT(p), s->path);
305                 if (r < 0)
306                         return r;
307         }
308
309         return 0;
310 }
311
312 static int path_verify(Path *p) {
313         assert(p);
314
315         if (UNIT(p)->load_state != UNIT_LOADED)
316                 return 0;
317
318         if (!p->specs) {
319                 log_unit_error(UNIT(p)->id,
320                                "%s lacks path setting. Refusing.", UNIT(p)->id);
321                 return -EINVAL;
322         }
323
324         return 0;
325 }
326
327 static int path_add_default_dependencies(Path *p) {
328         int r;
329
330         assert(p);
331
332         r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE,
333                                         SPECIAL_PATHS_TARGET, NULL, true);
334         if (r < 0)
335                 return r;
336
337         if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) {
338                 r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
339                                                       SPECIAL_SYSINIT_TARGET, NULL, true);
340                 if (r < 0)
341                         return r;
342         }
343
344         return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS,
345                                                  SPECIAL_SHUTDOWN_TARGET, NULL, true);
346 }
347
348 static int path_load(Unit *u) {
349         Path *p = PATH(u);
350         int r;
351
352         assert(u);
353         assert(u->load_state == UNIT_STUB);
354
355         r = unit_load_fragment_and_dropin(u);
356         if (r < 0)
357                 return r;
358
359         if (u->load_state == UNIT_LOADED) {
360
361                 if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
362                         Unit *x;
363
364                         r = unit_load_related_unit(u, ".service", &x);
365                         if (r < 0)
366                                 return r;
367
368                         r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
369                         if (r < 0)
370                                 return r;
371                 }
372
373                 r = path_add_mount_links(p);
374                 if (r < 0)
375                         return r;
376
377                 if (UNIT(p)->default_dependencies) {
378                         r = path_add_default_dependencies(p);
379                         if (r < 0)
380                                 return r;
381                 }
382         }
383
384         return path_verify(p);
385 }
386
387 static void path_dump(Unit *u, FILE *f, const char *prefix) {
388         Path *p = PATH(u);
389         Unit *trigger;
390         PathSpec *s;
391
392         assert(p);
393         assert(f);
394
395         trigger = UNIT_TRIGGER(u);
396
397         fprintf(f,
398                 "%sPath State: %s\n"
399                 "%sResult: %s\n"
400                 "%sUnit: %s\n"
401                 "%sMakeDirectory: %s\n"
402                 "%sDirectoryMode: %04o\n",
403                 prefix, path_state_to_string(p->state),
404                 prefix, path_result_to_string(p->result),
405                 prefix, trigger ? trigger->id : "n/a",
406                 prefix, yes_no(p->make_directory),
407                 prefix, p->directory_mode);
408
409         LIST_FOREACH(spec, s, p->specs)
410                 path_spec_dump(s, f, prefix);
411 }
412
413 static void path_unwatch(Path *p) {
414         PathSpec *s;
415
416         assert(p);
417
418         LIST_FOREACH(spec, s, p->specs)
419                 path_spec_unwatch(s);
420 }
421
422 static int path_watch(Path *p) {
423         int r;
424         PathSpec *s;
425
426         assert(p);
427
428         LIST_FOREACH(spec, s, p->specs) {
429                 r = path_spec_watch(s, path_dispatch_io);
430                 if (r < 0)
431                         return r;
432         }
433
434         return 0;
435 }
436
437 static void path_set_state(Path *p, PathState state) {
438         PathState old_state;
439         assert(p);
440
441         old_state = p->state;
442         p->state = state;
443
444         if (state != PATH_WAITING &&
445             (state != PATH_RUNNING || p->inotify_triggered))
446                 path_unwatch(p);
447
448         if (state != old_state)
449                 log_debug("%s changed %s -> %s",
450                           UNIT(p)->id,
451                           path_state_to_string(old_state),
452                           path_state_to_string(state));
453
454         unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true);
455 }
456
457 static void path_enter_waiting(Path *p, bool initial, bool recheck);
458
459 static int path_coldplug(Unit *u) {
460         Path *p = PATH(u);
461
462         assert(p);
463         assert(p->state == PATH_DEAD);
464
465         if (p->deserialized_state != p->state) {
466
467                 if (p->deserialized_state == PATH_WAITING ||
468                     p->deserialized_state == PATH_RUNNING)
469                         path_enter_waiting(p, true, true);
470                 else
471                         path_set_state(p, p->deserialized_state);
472         }
473
474         return 0;
475 }
476
477 static void path_enter_dead(Path *p, PathResult f) {
478         assert(p);
479
480         if (f != PATH_SUCCESS)
481                 p->result = f;
482
483         path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
484 }
485
486 static void path_enter_running(Path *p) {
487         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
488         int r;
489
490         assert(p);
491
492         /* Don't start job if we are supposed to go down */
493         if (unit_stop_pending(UNIT(p)))
494                 return;
495
496         r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)),
497                             JOB_REPLACE, true, &error, NULL);
498         if (r < 0)
499                 goto fail;
500
501         p->inotify_triggered = false;
502
503         r = path_watch(p);
504         if (r < 0)
505                 goto fail;
506
507         path_set_state(p, PATH_RUNNING);
508         return;
509
510 fail:
511         log_warning("%s failed to queue unit startup job: %s",
512                     UNIT(p)->id, bus_error_message(&error, r));
513         path_enter_dead(p, PATH_FAILURE_RESOURCES);
514 }
515
516 static bool path_check_good(Path *p, bool initial) {
517         PathSpec *s;
518         bool good = false;
519
520         assert(p);
521
522         LIST_FOREACH(spec, s, p->specs) {
523                 good = path_spec_check_good(s, initial);
524
525                 if (good)
526                         break;
527         }
528
529         return good;
530 }
531
532 static void path_enter_waiting(Path *p, bool initial, bool recheck) {
533         int r;
534
535         if (recheck)
536                 if (path_check_good(p, initial)) {
537                         log_debug("%s got triggered.", UNIT(p)->id);
538                         path_enter_running(p);
539                         return;
540                 }
541
542         r = path_watch(p);
543         if (r < 0)
544                 goto fail;
545
546         /* Hmm, so now we have created inotify watches, but the file
547          * might have appeared/been removed by now, so we must
548          * recheck */
549
550         if (recheck)
551                 if (path_check_good(p, false)) {
552                         log_debug("%s got triggered.", UNIT(p)->id);
553                         path_enter_running(p);
554                         return;
555                 }
556
557         path_set_state(p, PATH_WAITING);
558         return;
559
560 fail:
561         log_warning_errno(r, "%s failed to enter waiting state: %m", UNIT(p)->id);
562         path_enter_dead(p, PATH_FAILURE_RESOURCES);
563 }
564
565 static void path_mkdir(Path *p) {
566         PathSpec *s;
567
568         assert(p);
569
570         if (!p->make_directory)
571                 return;
572
573         LIST_FOREACH(spec, s, p->specs)
574                 path_spec_mkdir(s, p->directory_mode);
575 }
576
577 static int path_start(Unit *u) {
578         Path *p = PATH(u);
579
580         assert(p);
581         assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
582
583         if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
584                 return -ENOENT;
585
586         path_mkdir(p);
587
588         p->result = PATH_SUCCESS;
589         path_enter_waiting(p, true, true);
590
591         return 0;
592 }
593
594 static int path_stop(Unit *u) {
595         Path *p = PATH(u);
596
597         assert(p);
598         assert(p->state == PATH_WAITING || p->state == PATH_RUNNING);
599
600         path_enter_dead(p, PATH_SUCCESS);
601         return 0;
602 }
603
604 static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
605         Path *p = PATH(u);
606
607         assert(u);
608         assert(f);
609         assert(fds);
610
611         unit_serialize_item(u, f, "state", path_state_to_string(p->state));
612         unit_serialize_item(u, f, "result", path_result_to_string(p->result));
613
614         return 0;
615 }
616
617 static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
618         Path *p = PATH(u);
619
620         assert(u);
621         assert(key);
622         assert(value);
623         assert(fds);
624
625         if (streq(key, "state")) {
626                 PathState state;
627
628                 state = path_state_from_string(value);
629                 if (state < 0)
630                         log_debug("Failed to parse state value %s", value);
631                 else
632                         p->deserialized_state = state;
633
634         } else if (streq(key, "result")) {
635                 PathResult f;
636
637                 f = path_result_from_string(value);
638                 if (f < 0)
639                         log_debug("Failed to parse result value %s", value);
640                 else if (f != PATH_SUCCESS)
641                         p->result = f;
642
643         } else
644                 log_debug("Unknown serialization key '%s'", key);
645
646         return 0;
647 }
648
649 _pure_ static UnitActiveState path_active_state(Unit *u) {
650         assert(u);
651
652         return state_translation_table[PATH(u)->state];
653 }
654
655 _pure_ static const char *path_sub_state_to_string(Unit *u) {
656         assert(u);
657
658         return path_state_to_string(PATH(u)->state);
659 }
660
661 static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
662         PathSpec *s = userdata;
663         Path *p;
664         int changed;
665
666         assert(s);
667         assert(s->unit);
668         assert(fd >= 0);
669
670         p = PATH(s->unit);
671
672         if (p->state != PATH_WAITING &&
673             p->state != PATH_RUNNING)
674                 return 0;
675
676         /* log_debug("inotify wakeup on %s.", u->id); */
677
678         LIST_FOREACH(spec, s, p->specs)
679                 if (path_spec_owns_inotify_fd(s, fd))
680                         break;
681
682         if (!s) {
683                 log_error("Got event on unknown fd.");
684                 goto fail;
685         }
686
687         changed = path_spec_fd_event(s, revents);
688         if (changed < 0)
689                 goto fail;
690
691         /* If we are already running, then remember that one event was
692          * dispatched so that we restart the service only if something
693          * actually changed on disk */
694         p->inotify_triggered = true;
695
696         if (changed)
697                 path_enter_running(p);
698         else
699                 path_enter_waiting(p, false, true);
700
701         return 0;
702
703 fail:
704         path_enter_dead(p, PATH_FAILURE_RESOURCES);
705         return 0;
706 }
707
708 static void path_trigger_notify(Unit *u, Unit *other) {
709         Path *p = PATH(u);
710
711         assert(u);
712         assert(other);
713
714         /* Invoked whenever the unit we trigger changes state or gains
715          * or loses a job */
716
717         if (other->load_state != UNIT_LOADED)
718                 return;
719
720         if (p->state == PATH_RUNNING &&
721             UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
722                 log_unit_debug(UNIT(p)->id,
723                                "%s got notified about unit deactivation.",
724                                UNIT(p)->id);
725
726                 /* Hmm, so inotify was triggered since the
727                  * last activation, so I guess we need to
728                  * recheck what is going on. */
729                 path_enter_waiting(p, false, p->inotify_triggered);
730         }
731 }
732
733 static void path_reset_failed(Unit *u) {
734         Path *p = PATH(u);
735
736         assert(p);
737
738         if (p->state == PATH_FAILED)
739                 path_set_state(p, PATH_DEAD);
740
741         p->result = PATH_SUCCESS;
742 }
743
744 static const char* const path_state_table[_PATH_STATE_MAX] = {
745         [PATH_DEAD] = "dead",
746         [PATH_WAITING] = "waiting",
747         [PATH_RUNNING] = "running",
748         [PATH_FAILED] = "failed"
749 };
750
751 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
752
753 static const char* const path_type_table[_PATH_TYPE_MAX] = {
754         [PATH_EXISTS] = "PathExists",
755         [PATH_EXISTS_GLOB] = "PathExistsGlob",
756         [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty",
757         [PATH_CHANGED] = "PathChanged",
758         [PATH_MODIFIED] = "PathModified",
759 };
760
761 DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
762
763 static const char* const path_result_table[_PATH_RESULT_MAX] = {
764         [PATH_SUCCESS] = "success",
765         [PATH_FAILURE_RESOURCES] = "resources",
766 };
767
768 DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
769
770 const UnitVTable path_vtable = {
771         .object_size = sizeof(Path),
772
773         .sections =
774                 "Unit\0"
775                 "Path\0"
776                 "Install\0",
777
778         .init = path_init,
779         .done = path_done,
780         .load = path_load,
781
782         .coldplug = path_coldplug,
783
784         .dump = path_dump,
785
786         .start = path_start,
787         .stop = path_stop,
788
789         .serialize = path_serialize,
790         .deserialize_item = path_deserialize_item,
791
792         .active_state = path_active_state,
793         .sub_state_to_string = path_sub_state_to_string,
794
795         .trigger_notify = path_trigger_notify,
796
797         .reset_failed = path_reset_failed,
798
799         .bus_interface = "org.freedesktop.systemd1.Path",
800         .bus_vtable = bus_path_vtable
801 };