source: trunk/src/sh_inotify.c@ 364

Last change on this file since 364 was 364, checked in by katerina, 13 years ago

Change inotify watch list to a tree with unlimited members, lookup by integer watch.

File size: 12.0 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 "sh_utils.h"
42#include "slib.h"
43
44/**************************************************
45 *
46 * Make the inotify fd thread-specific by
47 * encapsulating it in get/set functions:
48 * sh_get_inotify_fd() / sh_set_inotify_fd()
49 *
50 **************************************************/
51
52#if defined(HAVE_PTHREAD)
53static pthread_key_t inotify_key;
54static pthread_once_t inotify_key_once = PTHREAD_ONCE_INIT;
55
56static void make_inotify_key()
57{
58 (void) pthread_key_create(&inotify_key, free);
59}
60
61static int sh_get_inotify_fd()
62{
63 void * ptr;
64 int * fp;
65
66 (void) pthread_once(&inotify_key_once, make_inotify_key);
67
68 if ((ptr = pthread_getspecific(inotify_key)) == NULL)
69 {
70 ptr = malloc(sizeof(int));
71 if (ptr)
72 {
73 fp = (int*) ptr;
74 *fp = -1;
75 (void) pthread_setspecific(inotify_key, ptr);
76 }
77 else
78 {
79 return -1;
80 }
81 }
82 else
83 {
84 fp = (int*) ptr;
85 }
86 return *fp;
87}
88
89static void sh_set_inotify_fd(int fd)
90{
91 int * fp;
92
93 fp = (int*) pthread_getspecific(inotify_key);
94 if (fp)
95 *fp = fd;
96 return;
97}
98
99/* !defined(HAVE_PTHREAD) */
100#else
101
102static int sh_inotify_fd = -1;
103
104static inline int sh_get_inotify_fd()
105{
106 return sh_inotify_fd;
107}
108
109static inline void sh_set_inotify_fd(int fd)
110{
111 sh_inotify_fd = fd;
112}
113
114#endif
115
116/*--- nothing thread-related below this point --- */
117
118#include "zAVLTree.h"
119
120typedef struct
121{
122 int watch;
123 int flag;
124 char * file;
125} sh_watch;
126
127/**************************************************
128 *
129 * Get inotify fd, initialize inotify if necessary
130 *
131 **************************************************/
132#define SH_INOTIFY_FAILED -2
133
134static int sh_inotify_getfd()
135{
136 int ifd = sh_get_inotify_fd();
137
138 if (ifd >= 0)
139 {
140 return ifd;
141 }
142
143 else if (ifd == SH_INOTIFY_FAILED)
144 {
145 return -1;
146 }
147
148 else /* if (ifd == -1) */
149 {
150#if defined(HAVE_INOTIFY_INIT1)
151 ifd = inotify_init1(IN_CLOEXEC);
152#else
153 ifd = inotify_init();
154 if (ifd >= 0)
155 {
156 long sflags;
157
158 sflags = retry_fcntl(FIL__, __LINE__, ifd, F_GETFD, 0);
159 retry_fcntl(FIL__, __LINE__, ifd, F_SETFD, sflags|FD_CLOEXEC);
160 }
161#endif
162
163 if (ifd < 0)
164 {
165 sh_set_inotify_fd(SH_INOTIFY_FAILED);
166 return -1;
167 }
168
169 sh_set_inotify_fd(ifd);
170 return ifd;
171 }
172}
173
174/**************************************************
175 *
176 * Public function:
177 * int sh_inotify_wait_for_change(char * filename,
178 * int watch,
179 * int * errnum,
180 * int waitsec);
181 * Returns: watch, if nonnegative
182 * -1 on error or reopen required
183 * (check errnum != 0)
184 *
185 * Caller needs to keep track of watch descriptor
186 *
187 **************************************************/
188
189#define SH_INOTIFY_REOPEN 0
190#define SH_INOTIFY_MODIFY 1
191
192static void sh_inotify_init(sh_watches * watches)
193{
194 watches->list_of_watches = NULL;
195 watches->count = 0;
196 watches->max_count = 0;
197 return;
198}
199
200static void sh_inotify_free_watch(void * item)
201{
202 sh_watch * this = (sh_watch *) item;
203
204 if (this->file)
205 SH_FREE(this->file);
206 SH_FREE(this);
207 return;
208}
209
210static sh_watch * sh_inotify_create_watch(char * file, int nwatch, int flag)
211{
212 sh_watch * this = SH_ALLOC(sizeof(sh_watch));
213
214 this->file = sh_util_strdup(file);
215 this->watch = nwatch;
216 this->flag = flag;
217 return this;
218}
219
220/********** List Handling ******************/
221
222struct sh_inotify_litem
223{
224 sh_watch * watch;
225 struct sh_inotify_litem * next;
226};
227
228typedef struct {
229 struct sh_inotify_litem *prenode;
230 struct sh_inotify_litem *curnode;
231} sh_inotify_listCursor;
232
233static sh_watch * sh_inotify_list_first(sh_inotify_listCursor * listcursor, sh_watches * watches)
234{
235 listcursor->prenode = watches->dormant_watches;
236 listcursor->curnode = watches->dormant_watches;
237
238 return listcursor->curnode->watch;
239}
240
241static sh_watch * sh_inotify_list_next(sh_inotify_listCursor * listcursor, sh_watches * watches)
242{
243 (void) watches;
244
245 listcursor->prenode = listcursor->curnode;
246 listcursor->curnode = listcursor->curnode->next;
247
248 if (listcursor->curnode)
249 return listcursor->curnode->watch;
250 return NULL;
251}
252
253static void sh_inotify_listitem_destroy(struct sh_inotify_litem * this)
254{
255 SH_FREE(this);
256 return;
257}
258
259static sh_watch * sh_inotify_list_del_current(sh_inotify_listCursor * listcursor, sh_watches * watches)
260{
261 sh_watch * ret = NULL;
262
263 if (listcursor->curnode)
264 {
265 struct sh_inotify_litem * this = listcursor->curnode;
266
267 if (listcursor->prenode == this)
268 {
269 watches->dormant_watches = this->next;
270
271 listcursor->prenode = watches->dormant_watches;
272 listcursor->curnode = watches->dormant_watches;
273 }
274 else
275 {
276 listcursor->prenode->next = this->next;
277 listcursor->curnode = this->next;
278 }
279 ret = listcursor->curnode->watch;
280 sh_inotify_listitem_destroy(this);
281 }
282 return ret;
283}
284
285static int sh_inotify_add_dormant(sh_watches * watches, sh_watch * item)
286{
287 struct sh_inotify_litem * this = SH_ALLOC(sizeof(struct sh_inotify_litem));
288
289 this->watch = item;
290 this->next = (struct sh_inotify_litem *) watches->dormant_watches;
291
292 watches->dormant_watches = this;
293 return 0;
294}
295
296/********** End List Handling **************/
297
298static zAVLKey sh_inotify_getkey(void const *item)
299{
300 return (&((sh_watch *)item)->watch);
301}
302
303
304/* This function removes all watches from the list,
305 * and closes the inode file descriptor in this thread.
306 */
307void sh_inotify_remove(sh_watches * watches)
308{
309 int ifd = sh_inotify_getfd();
310 zAVLTree * all_watches = (zAVLTree *)(watches->list_of_watches);
311
312 if (all_watches)
313 zAVLFreeTree(all_watches, sh_inotify_free_watch);
314
315 sh_inotify_init(watches);
316
317 if (ifd >= 0)
318 close(ifd);
319 sh_set_inotify_fd(-1);
320
321 watches->count = 0;
322
323 return;
324}
325
326static int index_watched_file(char * filename, sh_watches * watches)
327{
328 sh_watch * item;
329 zAVLCursor avlcursor;
330 zAVLTree * all_watches = (zAVLTree *)(watches->list_of_watches);
331
332 if (all_watches)
333 {
334 for (item = (sh_watch *) zAVLFirst(&avlcursor, all_watches); item;
335 item = (sh_watch *) zAVLNext(&avlcursor))
336 {
337 if (item->file)
338 {
339 if (0 == strcmp(filename, item->file))
340 return item->watch;
341 }
342 }
343 }
344 return -1;
345}
346
347/* This function is idempotent; it will add the watch only once
348 */
349int sh_inotify_add_watch(char * filename, sh_watches * watches, int * errnum)
350{
351 *errnum = 0;
352
353 if (filename)
354 {
355 int nwatch;
356 sh_watch * item;
357 int index = index_watched_file(filename, watches);
358
359 if (index < 0)
360 {
361 int ifd = sh_inotify_getfd();
362
363 /*************************************
364
365 if (watches->count == SH_INOTIFY_MAX)
366 {
367#ifdef EMFILE
368 *errnum = EMFILE;
369#else
370 *errnum = 24;
371#endif
372 return -1;
373 }
374 **************************************/
375
376 nwatch = inotify_add_watch (ifd, filename,
377 IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT);
378 if (nwatch < 0)
379 {
380 *errnum = errno;
381 return -1;
382 }
383
384 item = sh_inotify_create_watch(filename, nwatch, /* flag */ 0);
385
386 if (NULL == watches->list_of_watches)
387 watches->list_of_watches = zAVLAllocTree (sh_inotify_getkey, zAVL_KEY_INT);
388
389 if (watches->list_of_watches)
390 {
391 *errnum = zAVLInsert((zAVLTree *)(watches->list_of_watches), item);
392 if (*errnum != 0)
393 {
394 sh_inotify_free_watch(item);
395 return -1;
396 }
397 }
398 else
399 {
400 *errnum = -1;
401 return -1;
402 }
403
404 ++(watches->count);
405 }
406 }
407 return 0;
408}
409
410int sh_inotify_wait_for_change(char * filename, sh_watches * watches,
411 int * errnum, int waitsec)
412{
413 sh_watch * item;
414 zAVLTree * all_watches = (zAVLTree *)(watches->list_of_watches);
415 int ifd = sh_inotify_getfd();
416
417 *errnum = 0;
418
419 start_it:
420
421 if (ifd >= 0)
422 {
423 ssize_t len = -1;
424 ssize_t i = 0;
425 int flag = 0;
426 char buffer[1024];
427
428 sh_inotify_listCursor listcursor;
429
430 /* -- Add watch if required
431 */
432 if (filename)
433 {
434 if (sh_inotify_add_watch(filename, watches, errnum) < 0)
435 {
436 retry_msleep(waitsec, 0);
437 return -1;
438 }
439 }
440
441 /* -- Check dormant watches for reopening.
442 */
443 for (item = sh_inotify_list_first(&listcursor, watches); item;
444 item = sh_inotify_list_next(&listcursor, watches))
445 {
446 have_next:
447 if (item->file && item->watch == -1)
448 {
449 item->watch = inotify_add_watch (ifd, item->file,
450 IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT);
451 if (item->watch >= 0)
452 {
453 zAVLInsert(all_watches, item);
454 item = sh_inotify_list_del_current(&listcursor, watches);
455 goto have_next;
456 }
457 }
458 }
459
460
461 /* -- Blocking read on inotify file descriptor
462 */
463 do {
464 len = read (ifd, &buffer, sizeof(buffer));
465 } while (len < 0 || errno == EINTR);
466
467 if (len > 0)
468 {
469 struct inotify_event *event;
470
471 i = 0;
472
473 while (i < len) {
474
475 event = (struct inotify_event *) &buffer[i];
476
477 item = zAVLSearch(all_watches, &(event->wd));
478
479 if (item)
480 {
481 if (event->mask & IN_MODIFY)
482 {
483 item->flag |= SH_INOTIFY_MODIFY;
484 flag |= SH_INOTIFY_MODIFY;
485 }
486 else if (event->mask & IN_DELETE_SELF ||
487 event->mask & IN_UNMOUNT ||
488 event->mask & IN_MOVE_SELF )
489 {
490 item->flag |= SH_INOTIFY_REOPEN;
491 (void) inotify_rm_watch(ifd, item->watch);
492 zAVLDelete(all_watches, item);
493 sh_inotify_add_dormant(watches, item);
494 item->watch = -1;
495 flag |= SH_INOTIFY_REOPEN;
496 }
497 }
498
499 i += sizeof (struct inotify_event) + event->len;
500 }
501 }
502 else if (len == -1)
503 {
504 *errnum = errno;
505 retry_msleep(waitsec, 0);
506
507 return -1;
508 }
509
510 if (flag & SH_INOTIFY_REOPEN)
511 {
512 if (flag & SH_INOTIFY_MODIFY)
513 return 0;
514 else
515 goto start_it;
516 }
517
518 return 0;
519 }
520
521 /* Inotify not working, sleep
522 */
523 retry_msleep(waitsec, 0);
524
525 *errnum = 0;
526 return -1;
527}
528
529/* !defined(HAVE_SYS_INOTIFY_H) */
530#else
531
532#include "sh_calls.h"
533#include "sh_inotify.h"
534
535void sh_inotify_remove(sh_watches * watches)
536{
537 (void) watches;
538 return;
539}
540
541int sh_inotify_wait_for_change(char * filename, sh_watches * watches,
542 int * errnum, int waitsec)
543{
544 (void) filename;
545 (void) watches;
546
547 /* Inotify not working, sleep for waitsec seconds
548 */
549 retry_msleep(waitsec, 0);
550
551 *errnum = 0;
552 return -1;
553}
554
555int sh_inotify_add_watch(char * filename, sh_watches * watches, int * errnum)
556{
557 (void) filename;
558 (void) watches;
559 *errnum = 0;
560 return 0;
561}
562
563#endif
Note: See TracBrowser for help on using the repository browser.