diff options
Diffstat (limited to 'src/watcher.c')
-rw-r--r-- | src/watcher.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/src/watcher.c b/src/watcher.c new file mode 100644 index 0000000..f8761ee --- /dev/null +++ b/src/watcher.c | |||
@@ -0,0 +1,243 @@ | |||
1 | /* wydawca - automatic release submission daemon | ||
2 | Copyright (C) 2007, 2009-2012 Sergey Poznyakoff | ||
3 | |||
4 | Wydawca is free software; you can redistribute it and/or modify it | ||
5 | under the terms of the GNU General Public License as published by the | ||
6 | Free Software Foundation; either version 3 of the License, or (at your | ||
7 | option) any later version. | ||
8 | |||
9 | Wydawca is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License along | ||
15 | with wydawca. If not, see <http://www.gnu.org/licenses/>. */ | ||
16 | |||
17 | #include "wydawca.h" | ||
18 | #include <sys/inotify.h> | ||
19 | #include <sys/ioctl.h> | ||
20 | |||
21 | /* A directory watcher is described by the following structure */ | ||
22 | struct dirwatcher | ||
23 | { | ||
24 | struct dirwatcher *next, *prev; | ||
25 | struct dirwatcher *parent; /* Points to the parent watcher. | ||
26 | NULL for top-level watchers */ | ||
27 | struct spool *spool; | ||
28 | int wd; /* Watch descriptor */ | ||
29 | }; | ||
30 | |||
31 | static struct dirwatcher *dirwatcher_list; | ||
32 | |||
33 | struct dirwatcher * | ||
34 | dirwatcher_unlink(struct dirwatcher **root, struct dirwatcher *p) | ||
35 | { | ||
36 | if (p->prev) | ||
37 | p->prev->next = p->next; | ||
38 | else | ||
39 | *root = p->next; | ||
40 | if (p->next) | ||
41 | p->next->prev = p->prev; | ||
42 | p->next = p->prev = NULL; | ||
43 | return p; | ||
44 | } | ||
45 | |||
46 | struct dirwatcher * | ||
47 | dirwatcher_pop(struct dirwatcher **pp) | ||
48 | { | ||
49 | if (*pp) | ||
50 | return dirwatcher_unlink(pp, *pp); | ||
51 | return NULL; | ||
52 | } | ||
53 | |||
54 | static void | ||
55 | dirwatcher_push(struct dirwatcher **pp, struct dirwatcher *p) | ||
56 | { | ||
57 | p->prev = NULL; | ||
58 | p->next = *pp; | ||
59 | if (*pp) | ||
60 | (*pp)->prev = p; | ||
61 | *pp = p; | ||
62 | } | ||
63 | |||
64 | /* Find a watcher with the given descriptor */ | ||
65 | static struct dirwatcher * | ||
66 | dirwatcher_find_wd (int wd) | ||
67 | { | ||
68 | struct dirwatcher *dwp; | ||
69 | |||
70 | for (dwp = dirwatcher_list; dwp; dwp = dwp->next) | ||
71 | if (dwp->wd == wd) | ||
72 | break; | ||
73 | return dwp; | ||
74 | } | ||
75 | |||
76 | static int | ||
77 | create_watcher (struct spool *sp, void *data) | ||
78 | { | ||
79 | int ifd = *(int*)data; | ||
80 | struct dirwatcher *dwp; | ||
81 | int wd; | ||
82 | const char *path = get_path (sp); | ||
83 | |||
84 | if (!sp) | ||
85 | return 0; | ||
86 | |||
87 | if (debug_level > 1) | ||
88 | logmsg (LOG_DEBUG, "creating watcher %s", path); | ||
89 | dwp = malloc (sizeof(*dwp)); | ||
90 | if (!dwp) | ||
91 | { | ||
92 | logmsg (LOG_ERR, "not enough memory"); | ||
93 | return 1; | ||
94 | } | ||
95 | dwp->spool = sp; | ||
96 | dwp->parent = NULL; | ||
97 | |||
98 | wd = inotify_add_watch (ifd, path, IN_DELETE|IN_CREATE|IN_CLOSE_WRITE| | ||
99 | IN_MOVED_FROM|IN_MOVED_TO); | ||
100 | if (wd == -1) | ||
101 | { | ||
102 | logmsg (LOG_ERR, "cannot set watch on %s: %s", path, strerror (errno)); | ||
103 | free (dwp); | ||
104 | return 1; | ||
105 | } | ||
106 | |||
107 | dwp->wd = wd; | ||
108 | dirwatcher_push (&dirwatcher_list, dwp); | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | int | ||
113 | watcher_init () | ||
114 | { | ||
115 | int ifd, rc; | ||
116 | |||
117 | if (debug_level > 1) | ||
118 | logmsg (LOG_DEBUG, "setting up inotify"); | ||
119 | ifd = inotify_init (); | ||
120 | if (ifd == -1) | ||
121 | { | ||
122 | logmsg (LOG_ERR, "inotify_init: %s", strerror (errno)); | ||
123 | return -1; | ||
124 | } | ||
125 | |||
126 | rc = for_each_spool (create_watcher, &ifd); | ||
127 | if (rc) | ||
128 | exit (EX_OSERR); | ||
129 | if (!dirwatcher_list) | ||
130 | { | ||
131 | if (debug_level > 1) | ||
132 | logmsg (LOG_DEBUG, "inotify: nothing to watch"); | ||
133 | close (ifd); | ||
134 | ifd = -1; | ||
135 | } | ||
136 | else if (debug_level > 1) | ||
137 | logmsg (LOG_DEBUG, "inotify initialized successfully"); | ||
138 | |||
139 | return ifd; | ||
140 | } | ||
141 | |||
142 | static void | ||
143 | process_event (struct inotify_event *ep) | ||
144 | { | ||
145 | static struct dirwatcher *dwp; | ||
146 | dwp = dirwatcher_find_wd (ep->wd); | ||
147 | |||
148 | if (ep->mask & IN_IGNORED) | ||
149 | /* nothing */; | ||
150 | else if (ep->mask & IN_Q_OVERFLOW) | ||
151 | logmsg (LOG_NOTICE, "event queue overflow"); | ||
152 | else if (ep->mask & IN_UNMOUNT) | ||
153 | /* FIXME: not sure if there's | ||
154 | anything to do. Perhaps we should | ||
155 | deregister the watched dirs that | ||
156 | were located under the mountpoint | ||
157 | */; | ||
158 | else if (!dwp) | ||
159 | { | ||
160 | if (ep->name) | ||
161 | logmsg (LOG_NOTICE, "unrecognized event %x for %s", | ||
162 | ep->mask, ep->name); | ||
163 | else | ||
164 | logmsg (LOG_NOTICE, "unrecognized event %x", ep->mask); | ||
165 | } | ||
166 | else if (ep->mask & IN_CREATE) | ||
167 | { | ||
168 | if (debug_level > 0) | ||
169 | logmsg (LOG_DEBUG, "%s/%s created", dwp->spool->source_dir, ep->name); | ||
170 | } | ||
171 | else if (ep->mask & (IN_DELETE|IN_MOVED_FROM)) | ||
172 | { | ||
173 | if (debug_level > 0) | ||
174 | logmsg (LOG_DEBUG, "%s/%s %s", dwp->spool->source_dir, ep->name, | ||
175 | ep->mask & IN_DELETE ? "deleted" : "moved out"); | ||
176 | triplet_remove_file (dwp->spool, ep->name); | ||
177 | } | ||
178 | else if (ep->mask & (IN_CLOSE_WRITE|IN_MOVED_TO)) | ||
179 | { | ||
180 | if (debug_level > 0) | ||
181 | logmsg (LOG_DEBUG, "%s/%s written", dwp->spool->source_dir, ep->name); | ||
182 | if (spool_add_new_file (dwp->spool, ep->name, 0, NULL) == 0 && | ||
183 | count_processable_triplets ()) | ||
184 | schedule_job (&inotify_spool, getuid ()); | ||
185 | } | ||
186 | else | ||
187 | logmsg (LOG_NOTICE, "%s/%s: unexpected event %x", | ||
188 | dwp->spool->source_dir, ep->name, ep->mask); | ||
189 | } | ||
190 | |||
191 | static char buffer[4096]; | ||
192 | static int offset; | ||
193 | |||
194 | int | ||
195 | watcher_run (int ifd) | ||
196 | { | ||
197 | int n; | ||
198 | int rdbytes; | ||
199 | |||
200 | if (ioctl (ifd, FIONREAD, &n)) | ||
201 | { | ||
202 | logmsg (LOG_ERR, "ioctl: %s", strerror (errno)); | ||
203 | return -1; | ||
204 | } | ||
205 | if (offset + n > sizeof buffer) | ||
206 | n = sizeof buffer - offset; | ||
207 | if (n) | ||
208 | { | ||
209 | rdbytes = read (ifd, buffer + offset, n); | ||
210 | if (rdbytes == -1) | ||
211 | { | ||
212 | if (errno == EINTR) | ||
213 | { | ||
214 | //FIXME logmsg (LOG_NOTICE, "got signal %d", signo); | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | logmsg (LOG_NOTICE, "read failed: %s", strerror (errno)); | ||
219 | return -1; | ||
220 | } | ||
221 | } | ||
222 | offset += n; | ||
223 | |||
224 | for (n = 0; offset - n >= sizeof (struct inotify_event); ) | ||
225 | { | ||
226 | struct inotify_event *ep; | ||
227 | size_t size; | ||
228 | |||
229 | ep = (struct inotify_event *) (buffer + n); | ||
230 | size = sizeof(*ep) + ep->len; | ||
231 | if (offset - n < size) | ||
232 | break; | ||
233 | |||
234 | process_event (ep); | ||
235 | |||
236 | n += size; | ||
237 | } | ||
238 | if (n > 0 && offset - n > 0) | ||
239 | memmove (buffer, buffer + n, offset - n); | ||
240 | offset -= n; | ||
241 | |||
242 | return 0; | ||
243 | } | ||