source: trunk/src/sh_inotify.c@ 323

Last change on this file since 323 was 269, checked in by katerina, 15 years ago

Fix for a missing #include (ticket #187).

File size: 8.3 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2009 Rainer Wichmann */
3/* */
4/* This program is free software; you can redistribute it */
5/* and/or modify */
6/* it under the terms of the GNU General Public License as */
7/* published by */
8/* the Free Software Foundation; either version 2 of the License, or */
9/* (at your option) any later version. */
10/* */
11/* This program is distributed in the hope that it will be useful, */
12/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14/* GNU General Public License for more details. */
15/* */
16/* You should have received a copy of the GNU General Public License */
17/* along with this program; if not, write to the Free Software */
18/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20#include "config_xor.h"
21
22#if defined(HAVE_SYS_INOTIFY_H)
23
24#undef FIL__
25#define FIL__ _("sh_inotify.c")
26
27/* printf */
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/inotify.h>
32#include <errno.h>
33#include <unistd.h>
34#include <fcntl.h>
35
36#include "samhain.h"
37#include "sh_pthread.h"
38#include "sh_calls.h"
39#include "sh_inotify.h"
40#include "sh_mem.h"
41#include "slib.h"
42
43/**************************************************
44 *
45 * Make the inotify fd thread-specific by
46 * encapsulating it in get/set functions:
47 * sh_get_inotify_fd() / sh_set_inotify_fd()
48 *
49 **************************************************/
50
51#if defined(HAVE_PTHREAD)
52static pthread_key_t inotify_key;
53static pthread_once_t inotify_key_once = PTHREAD_ONCE_INIT;
54
55static void make_inotify_key()
56{
57 (void) pthread_key_create(&inotify_key, free);
58}
59
60static int sh_get_inotify_fd()
61{
62 void * ptr;
63 int * fp;
64
65 (void) pthread_once(&inotify_key_once, make_inotify_key);
66
67 if ((ptr = pthread_getspecific(inotify_key)) == NULL)
68 {
69 ptr = malloc(sizeof(int));
70 if (ptr)
71 {
72 fp = (int*) ptr;
73 *fp = -1;
74 (void) pthread_setspecific(inotify_key, ptr);
75 }
76 else
77 {
78 return -1;
79 }
80 }
81 else
82 {
83 fp = (int*) ptr;
84 }
85 return *fp;
86}
87
88static void sh_set_inotify_fd(int fd)
89{
90 int * fp;
91
92 fp = (int*) pthread_getspecific(inotify_key);
93 if (fp)
94 *fp = fd;
95 return;
96}
97
98/* !defined(HAVE_PTHREAD) */
99#else
100
101static int sh_inotify_fd = -1;
102
103static inline int sh_get_inotify_fd()
104{
105 return sh_inotify_fd;
106}
107
108static inline void sh_set_inotify_fd(int fd)
109{
110 sh_inotify_fd = fd;
111}
112
113#endif
114
115/*--- nothing thread-related below this point --- */
116
117
118/**************************************************
119 *
120 * Get inotify fd, initialize inotify if necessary
121 *
122 **************************************************/
123#define SH_INOTIFY_FAILED -2
124
125static int sh_inotify_getfd()
126{
127 int ifd = sh_get_inotify_fd();
128
129 if (ifd >= 0)
130 {
131 return ifd;
132 }
133
134 else if (ifd == SH_INOTIFY_FAILED)
135 {
136 return -1;
137 }
138
139 else /* if (ifd == -1) */
140 {
141#if defined(HAVE_INOTIFY_INIT1)
142 ifd = inotify_init1(IN_CLOEXEC);
143#else
144 ifd = inotify_init();
145 if (ifd >= 0)
146 {
147 long sflags;
148
149 sflags = retry_fcntl(FIL__, __LINE__, ifd, F_GETFD, 0);
150 retry_fcntl(FIL__, __LINE__, ifd, F_SETFD, sflags|FD_CLOEXEC);
151 }
152#endif
153
154 if (ifd < 0)
155 {
156 sh_set_inotify_fd(SH_INOTIFY_FAILED);
157 return -1;
158 }
159
160 sh_set_inotify_fd(ifd);
161 return ifd;
162 }
163}
164
165/**************************************************
166 *
167 * Public function:
168 * int sh_inotify_wait_for_change(char * filename,
169 * int watch,
170 * int * errnum,
171 * int waitsec);
172 * Returns: watch, if nonnegative
173 * -1 on error or reopen required
174 * (check errnum != 0)
175 *
176 * Caller needs to keep track of watch descriptor
177 *
178 **************************************************/
179
180#define SH_INOTIFY_REOPEN 0
181#define SH_INOTIFY_MODIFY 1
182
183void sh_inotify_remove(sh_watches * watches)
184{
185 int i;
186 int ifd = sh_inotify_getfd();
187
188 for (i = 0; i < watches->count; ++i)
189 {
190 if (watches->file[i])
191 {
192 SH_FREE (watches->file[i]);
193 watches->file[i] = 0;
194 }
195 watches->watch[i] = 0;
196 watches->flag[i] = 0;
197 }
198 watches->count = 0;
199 if (ifd >= 0)
200 close(ifd);
201 sh_set_inotify_fd(-1);
202
203 return;
204}
205
206static int index_watched_file(char * filename, sh_watches * watches)
207{
208 int i;
209
210 for (i = 0; i < watches->count; ++i)
211 {
212 if (0 == strcmp(filename, watches->file[i]))
213 return i;
214 }
215 return -1;
216}
217
218/* This function is idempotent; it will add the watch only once
219 */
220int sh_inotify_add_watch(char * filename, sh_watches * watches, int * errnum)
221{
222 size_t len;
223 *errnum = 0;
224
225 if (filename)
226 {
227 int nwatch;
228 int index = index_watched_file(filename, watches);
229
230 if (index < 0)
231 {
232 int ifd = sh_inotify_getfd();
233
234 if (watches->count == SH_INOTIFY_MAX)
235 {
236#ifdef EMFILE
237 *errnum = EMFILE;
238#else
239 *errnum = 24;
240#endif
241 return -1;
242 }
243
244 nwatch = inotify_add_watch (ifd, filename,
245 IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT);
246 if (nwatch < 0)
247 {
248 *errnum = errno;
249 return -1;
250 }
251
252 watches->watch[watches->count] = nwatch;
253 watches->flag[watches->count] = 0;
254
255 len = strlen(filename) + 1;
256 watches->file[watches->count] = SH_ALLOC(len);
257 sl_strlcpy(watches->file[watches->count], filename, len);
258
259 ++(watches->count);
260 }
261 }
262 return 0;
263}
264
265int sh_inotify_wait_for_change(char * filename, sh_watches * watches,
266 int * errnum, int waitsec)
267{
268 int ifd = sh_inotify_getfd();
269
270 *errnum = 0;
271
272 start_it:
273
274 if (ifd >= 0)
275 {
276 ssize_t len = -1;
277 ssize_t i = 0;
278 int flag = 0;
279 char buffer[1024];
280
281 /* -- Add watch if required
282 */
283 if (filename)
284 {
285 if (sh_inotify_add_watch(filename, watches, errnum) < 0)
286 {
287 retry_msleep(waitsec, 0);
288 return -1;
289 }
290 }
291
292 for (i = 0; i < watches->count; ++i)
293 {
294 if (watches->watch[i] == -1)
295 watches->watch[i] = inotify_add_watch (ifd, watches->file[i],
296 IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT);
297 }
298
299
300 /* -- Blocking read on inotify file descriptor
301 */
302 do {
303 len = read (ifd, &buffer, sizeof(buffer));
304 } while (len < 0 || errno == EINTR);
305
306 if (len > 0)
307 {
308 int j;
309 struct inotify_event *event;
310
311 i = 0;
312
313 while (i < len) {
314
315 event = (struct inotify_event *) &buffer[i];
316
317 for (j = 0; j < watches->count; ++j)
318 {
319 if (watches->watch[j] == event->wd)
320 {
321 if (event->mask & IN_MODIFY)
322 {
323 watches->flag[j] |= SH_INOTIFY_MODIFY;
324 flag |= SH_INOTIFY_MODIFY;
325 }
326 else if (event->mask & IN_DELETE_SELF ||
327 event->mask & IN_UNMOUNT ||
328 event->mask & IN_MOVE_SELF )
329 {
330 watches->flag[j] |= SH_INOTIFY_REOPEN;
331 (void) inotify_rm_watch(ifd, watches->watch[j]);
332 watches->watch[j] = -1;
333 flag |= SH_INOTIFY_REOPEN;
334 }
335 }
336 }
337 i += sizeof (struct inotify_event) + event->len;
338 }
339 }
340 else if (len == -1)
341 {
342 *errnum = errno;
343 retry_msleep(waitsec, 0);
344
345 return -1;
346 }
347
348 if (flag & SH_INOTIFY_REOPEN)
349 {
350 if (flag & SH_INOTIFY_MODIFY)
351 return 0;
352 else
353 goto start_it;
354 }
355
356 return 0;
357 }
358
359 /* Inotify not working, sleep
360 */
361 retry_msleep(waitsec, 0);
362
363 *errnum = 0;
364 return -1;
365}
366
367/* !defined(HAVE_SYS_INOTIFY_H) */
368#else
369
370#include "sh_calls.h"
371#include "sh_inotify.h"
372
373void sh_inotify_remove(sh_watches * watches)
374{
375 (void) watches;
376 return;
377}
378
379int sh_inotify_wait_for_change(char * filename, sh_watches * watches,
380 int * errnum, int waitsec)
381{
382 (void) filename;
383 (void) watches;
384
385 /* Inotify not working, sleep for waitsec seconds
386 */
387 retry_msleep(waitsec, 0);
388
389 *errnum = 0;
390 return -1;
391}
392
393int sh_inotify_add_watch(char * filename, sh_watches * watches, int * errnum)
394{
395 (void) filename;
396 (void) watches;
397 *errnum = 0;
398 return 0;
399}
400
401#endif
Note: See TracBrowser for help on using the repository browser.