source: trunk/src/sh_inotify.c@ 528

Last change on this file since 528 was 496, checked in by katerina, 9 years ago

Fix for ticket #393 (wrong policy assigned).

File size: 23.7 KB
RevLine 
[261]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
[474]24#if defined(GCC_VERSION_MAJOR) && !defined(__clang__)
[465]25#if (GCC_VERSION_MAJOR > 4) || ((GCC_VERSION_MAJOR == 4) && (GCC_VERSION_MINOR > 8))
26#pragma GCC diagnostic ignored "-Wclobbered"
27#endif
28#endif
29
[261]30#undef FIL__
31#define FIL__ _("sh_inotify.c")
32
33/* printf */
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <sys/inotify.h>
38#include <errno.h>
39#include <unistd.h>
40#include <fcntl.h>
41
42#include "samhain.h"
43#include "sh_pthread.h"
44#include "sh_calls.h"
45#include "sh_inotify.h"
46#include "sh_mem.h"
[364]47#include "sh_utils.h"
[261]48#include "slib.h"
49
50/**************************************************
51 *
52 * Make the inotify fd thread-specific by
53 * encapsulating it in get/set functions:
54 * sh_get_inotify_fd() / sh_set_inotify_fd()
55 *
56 **************************************************/
57
58#if defined(HAVE_PTHREAD)
[367]59
60SH_MUTEX_STATIC(mutex_list_dormant, PTHREAD_MUTEX_INITIALIZER);
61SH_MUTEX_STATIC(mutex_watches, PTHREAD_MUTEX_INITIALIZER);
62
[261]63static pthread_key_t inotify_key;
64static pthread_once_t inotify_key_once = PTHREAD_ONCE_INIT;
65
66static void make_inotify_key()
67{
68 (void) pthread_key_create(&inotify_key, free);
69}
70
71static int sh_get_inotify_fd()
72{
73 void * ptr;
74 int * fp;
75
76 (void) pthread_once(&inotify_key_once, make_inotify_key);
77
78 if ((ptr = pthread_getspecific(inotify_key)) == NULL)
79 {
[454]80 ptr = calloc(1,sizeof(int));
[261]81 if (ptr)
82 {
83 fp = (int*) ptr;
84 *fp = -1;
85 (void) pthread_setspecific(inotify_key, ptr);
86 }
87 else
88 {
89 return -1;
90 }
91 }
92 else
93 {
94 fp = (int*) ptr;
95 }
96 return *fp;
97}
98
99static void sh_set_inotify_fd(int fd)
100{
101 int * fp;
102
103 fp = (int*) pthread_getspecific(inotify_key);
104 if (fp)
105 *fp = fd;
106 return;
107}
108
109/* !defined(HAVE_PTHREAD) */
110#else
111
112static int sh_inotify_fd = -1;
113
114static inline int sh_get_inotify_fd()
115{
116 return sh_inotify_fd;
117}
118
119static inline void sh_set_inotify_fd(int fd)
120{
121 sh_inotify_fd = fd;
122}
123
124#endif
125
126/*--- nothing thread-related below this point --- */
127
[364]128#include "zAVLTree.h"
[261]129
[364]130typedef struct
131{
132 int watch;
[372]133 short flag;
134 short type;
[367]135 int class;
[373]136 int rdepth;
[481]137 unsigned long check_flags;
[364]138 char * file;
139} sh_watch;
140
[261]141/**************************************************
142 *
143 * Get inotify fd, initialize inotify if necessary
144 *
145 **************************************************/
146#define SH_INOTIFY_FAILED -2
147
148static int sh_inotify_getfd()
149{
150 int ifd = sh_get_inotify_fd();
151
152 if (ifd >= 0)
153 {
154 return ifd;
155 }
156
157 else if (ifd == SH_INOTIFY_FAILED)
158 {
159 return -1;
160 }
161
162 else /* if (ifd == -1) */
163 {
164#if defined(HAVE_INOTIFY_INIT1)
165 ifd = inotify_init1(IN_CLOEXEC);
166#else
167 ifd = inotify_init();
168 if (ifd >= 0)
169 {
170 long sflags;
171
[268]172 sflags = retry_fcntl(FIL__, __LINE__, ifd, F_GETFD, 0);
173 retry_fcntl(FIL__, __LINE__, ifd, F_SETFD, sflags|FD_CLOEXEC);
[261]174 }
175#endif
176
177 if (ifd < 0)
178 {
179 sh_set_inotify_fd(SH_INOTIFY_FAILED);
180 return -1;
181 }
182
183 sh_set_inotify_fd(ifd);
184 return ifd;
185 }
186}
187
188/**************************************************
189 *
190 * Public function:
191 * int sh_inotify_wait_for_change(char * filename,
192 * int watch,
193 * int * errnum,
194 * int waitsec);
195 * Returns: watch, if nonnegative
196 * -1 on error or reopen required
197 * (check errnum != 0)
198 *
199 * Caller needs to keep track of watch descriptor
200 *
201 **************************************************/
202
203#define SH_INOTIFY_REOPEN 0
204#define SH_INOTIFY_MODIFY 1
205
[367]206void sh_inotify_init(sh_watches * watches)
[261]207{
[367]208 SH_MUTEX_LOCK_UNSAFE(mutex_watches);
[364]209 watches->list_of_watches = NULL;
210 watches->count = 0;
211 watches->max_count = 0;
[367]212 SH_MUTEX_UNLOCK_UNSAFE(mutex_watches);
213
214 SH_MUTEX_LOCK_UNSAFE(mutex_list_dormant);
215 watches->dormant_watches = NULL;
216 SH_MUTEX_UNLOCK_UNSAFE(mutex_list_dormant);
217
[364]218 return;
219}
[261]220
[367]221ssize_t sh_inotify_read(char * buffer, size_t count)
222{
[371]223 ssize_t len = -1;
[367]224 int ifd = sh_inotify_getfd();
225
226 do {
[369]227 len = read (ifd, buffer, count);
[371]228 } while (len < 0 && (errno == EINTR || errno == EAGAIN));
[367]229
230 return len;
231}
232
[371]233ssize_t sh_inotify_read_timeout(char * buffer, size_t count, int timeout)
234{
235 ssize_t len;
236 int ifd = sh_inotify_getfd();
237
[481]238 len = sl_read_timeout_fd (ifd, buffer, count, timeout, S_FALSE);
[371]239
240 return len;
241}
242
243
[364]244static void sh_inotify_free_watch(void * item)
245{
246 sh_watch * this = (sh_watch *) item;
247
248 if (this->file)
249 SH_FREE(this->file);
250 SH_FREE(this);
251 return;
252}
253
[382]254static sh_watch * sh_inotify_create_watch(const char * file,
255 int nwatch, int flag)
[364]256{
257 sh_watch * this = SH_ALLOC(sizeof(sh_watch));
258
[382]259 this->file = sh_util_strdup_track(file, __FILE__, __LINE__);
[364]260 this->watch = nwatch;
261 this->flag = flag;
262 return this;
263}
264
265/********** List Handling ******************/
266
267struct sh_inotify_litem
268{
269 sh_watch * watch;
270 struct sh_inotify_litem * next;
271};
272
[367]273static void sh_inotify_listitem_destroy(struct sh_inotify_litem * this)
274{
275 if (this)
276 SH_FREE(this);
277 return;
278}
279
280/* No Mutex in the list cursor functions, must be in the caller
281 * function...
282 */
[364]283typedef struct {
284 struct sh_inotify_litem *prenode;
285 struct sh_inotify_litem *curnode;
286} sh_inotify_listCursor;
287
[367]288static sh_watch * sh_inotify_list_first(sh_inotify_listCursor * listcursor,
289 sh_watches * watches)
[364]290{
291 listcursor->prenode = watches->dormant_watches;
292 listcursor->curnode = watches->dormant_watches;
293
[367]294 if (listcursor->curnode)
295 return listcursor->curnode->watch;
296 return NULL;
[364]297}
298
[367]299static sh_watch * sh_inotify_list_next(sh_inotify_listCursor * listcursor,
300 sh_watches * watches)
[364]301{
302 (void) watches;
303
304 listcursor->prenode = listcursor->curnode;
305
306 if (listcursor->curnode)
[367]307 {
308 listcursor->curnode = listcursor->curnode->next;
[371]309 if (listcursor->curnode)
310 return listcursor->curnode->watch;
311 else
312 return NULL;
[367]313 }
314
[364]315 return NULL;
316}
317
[367]318static sh_watch * sh_inotify_list_del_cur(sh_inotify_listCursor * listcursor,
319 sh_watches * watches)
[364]320{
321 sh_watch * ret = NULL;
322
323 if (listcursor->curnode)
[261]324 {
[364]325 struct sh_inotify_litem * this = listcursor->curnode;
326
327 if (listcursor->prenode == this)
[261]328 {
[364]329 watches->dormant_watches = this->next;
330
331 listcursor->prenode = watches->dormant_watches;
332 listcursor->curnode = watches->dormant_watches;
[261]333 }
[364]334 else
335 {
336 listcursor->prenode->next = this->next;
337 listcursor->curnode = this->next;
338 }
[367]339 if (listcursor->curnode)
340 ret = listcursor->curnode->watch;
341 else
342 ret = NULL;
[364]343 sh_inotify_listitem_destroy(this);
[261]344 }
[364]345 return ret;
346}
347
348static int sh_inotify_add_dormant(sh_watches * watches, sh_watch * item)
349{
[367]350 struct sh_inotify_litem * this;
[364]351
[367]352 SH_MUTEX_LOCK(mutex_list_dormant);
353 this = SH_ALLOC(sizeof(struct sh_inotify_litem));
354
[364]355 this->watch = item;
356 this->next = (struct sh_inotify_litem *) watches->dormant_watches;
357
358 watches->dormant_watches = this;
[367]359 SH_MUTEX_UNLOCK(mutex_list_dormant);
[364]360 return 0;
361}
362
[367]363static void * sh_dummy_popret = NULL;
364
365char * sh_inotify_pop_dormant(sh_watches * watches,
[481]366 int * class, unsigned long * check_flags,
[373]367 int * type, int * rdepth)
[367]368{
369 char * popret = NULL;
370 struct sh_inotify_litem * this;
371
372 /* Take the address to keep gcc from putting it into a register.
373 * Avoids the 'clobbered by longjmp' warning.
374 */
375 sh_dummy_popret = (void *) &popret;
376
377 SH_MUTEX_LOCK(mutex_list_dormant);
378
379 this = (struct sh_inotify_litem *) watches->dormant_watches;
380
381 if (this)
382 {
[373]383 *class = this->watch->class;
384 *type = this->watch->type;
385 *rdepth = this->watch->rdepth;
[481]386 *check_flags = this->watch->check_flags;
[382]387 popret = sh_util_strdup_track(this->watch->file, __FILE__, __LINE__);
[367]388
389 watches->dormant_watches = this->next;
390
391 sh_inotify_free_watch(this->watch);
392 SH_FREE(this);
393 }
394 SH_MUTEX_UNLOCK(mutex_list_dormant);
395
[383]396 sh_dummy_popret = NULL;
[367]397 return popret;
398}
399
400void sh_inotify_purge_dormant(sh_watches * watches)
401{
402 struct sh_inotify_litem * this;
403
404 SH_MUTEX_LOCK(mutex_list_dormant);
405 this = (struct sh_inotify_litem *) watches->dormant_watches;
406
407 watches->dormant_watches = NULL;
408
409 while (this)
410 {
411 struct sh_inotify_litem * cur = this;
412
413 this = this->next;
414
415 sh_inotify_free_watch(cur->watch);
416 SH_FREE(cur);
417 }
418 SH_MUTEX_UNLOCK(mutex_list_dormant);
419 return;
420}
421
[364]422/********** End List Handling **************/
423
424static zAVLKey sh_inotify_getkey(void const *item)
425{
[481]426 return (&((const sh_watch *)item)->watch);
[364]427}
428
[496]429void sh_inotify_close()
430{
431 int ifd = sh_inotify_getfd();
[364]432
[496]433 if (ifd >= 0)
434 close(ifd);
435 sh_set_inotify_fd(-1);
436
437 return;
438}
439
440
[364]441/* This function removes all watches from the list,
442 * and closes the inode file descriptor in this thread.
443 */
444void sh_inotify_remove(sh_watches * watches)
445{
[367]446 zAVLTree * all_watches;
[364]447
[367]448 SH_MUTEX_LOCK(mutex_watches);
449 all_watches = (zAVLTree *)(watches->list_of_watches);
450
[364]451 if (all_watches)
452 zAVLFreeTree(all_watches, sh_inotify_free_watch);
453
[367]454 watches->list_of_watches = NULL;
455 watches->count = 0;
456 SH_MUTEX_UNLOCK(mutex_watches);
[364]457
[496]458 sh_inotify_close();
[261]459 return;
460}
461
462static int index_watched_file(char * filename, sh_watches * watches)
463{
[364]464 sh_watch * item;
465 zAVLCursor avlcursor;
466 zAVLTree * all_watches = (zAVLTree *)(watches->list_of_watches);
[261]467
[364]468 if (all_watches)
[261]469 {
[364]470 for (item = (sh_watch *) zAVLFirst(&avlcursor, all_watches); item;
471 item = (sh_watch *) zAVLNext(&avlcursor))
472 {
473 if (item->file)
474 {
475 if (0 == strcmp(filename, item->file))
476 return item->watch;
477 }
478 }
[261]479 }
480 return -1;
481}
482
[405]483#if !defined(IN_DONT_FOLLOW)
484#define IN_DONT_FOLLOW 0
485#endif
486
[367]487#define SH_INOTIFY_FILEFLAGS \
[405]488 (IN_ATTRIB|IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_DONT_FOLLOW)
[367]489#define SH_INOTIFY_DIRFLAGS \
490 (SH_INOTIFY_FILEFLAGS|IN_DELETE|IN_CREATE|IN_MOVED_FROM|IN_MOVED_TO)
491
492#define SH_INOTIFY_FLAGS (SH_INOTIFY_FILEFLAGS|SH_INOTIFY_DIRFLAGS)
493
[405]494
[367]495/* Create an item and put it on the 'dormant' list for later watch creation
496 */
[373]497int sh_inotify_add_watch_later(const char * filename, sh_watches * watches,
[367]498 int * errnum,
[481]499 int class, unsigned long check_flags, int type,
[382]500 int rdepth)
[367]501{
502 sh_watch * item;
503
504 item = sh_inotify_create_watch(filename, -1, /* flag */ 0);
505
506 item->class = class;
[372]507 item->type = (short) type;
[373]508 item->rdepth = (short) rdepth;
[481]509 item->check_flags = check_flags;
[367]510
511 sh_inotify_add_dormant(watches, item);
512 if (errnum)
513 *errnum = 0;
514
515 return 0;
516}
517
518int sh_inotify_rm_watch (sh_watches * watches, sh_watches * save, int wd)
519{
520 int ifd = sh_get_inotify_fd();
521
522 if (watches)
523 {
524 sh_watch * item;
525
526 SH_MUTEX_LOCK(mutex_watches);
527 item = zAVLSearch(watches->list_of_watches, &wd);
528
529 if (item)
530 {
531 zAVLDelete(watches->list_of_watches, &wd);
532 if (save) /* optionally save the item */
533 {
534 item->watch = -1;
535 sh_inotify_add_dormant(save, item);
536 }
537 else
538 {
539 sh_inotify_free_watch(item);
540 }
541 }
542 SH_MUTEX_UNLOCK(mutex_watches);
543 }
544 return inotify_rm_watch(ifd, wd);
545}
546
547#if (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
548static void * sh_dummy_litem;
549
550int sh_inotify_recheck_watches (sh_watches * watches, sh_watches * save)
551{
552 sh_watch * litem;
553 sh_inotify_listCursor listcursor;
554 int ifd = sh_get_inotify_fd();
555
[371]556 extern void sh_fInotify_report_add(char * path,
[481]557 int class, unsigned long check_flags);
[367]558
559 sh_dummy_litem = (void*) &litem;
560
561 /* -- Check dormant watches for reopening.
562 */
563 SH_MUTEX_LOCK(mutex_list_dormant);
564
[371]565 litem = sh_inotify_list_first(&listcursor, save);
566
567 while (litem)
[367]568 {
569 have_next:
570
571 /* sh_inotify_list_del_cur may return NULL */
572 if (litem && litem->file && litem->watch == -1)
573 {
574 litem->watch = inotify_add_watch (ifd, litem->file,
575 SH_INOTIFY_FLAGS);
576
577 if (litem->watch >= 0)
578 {
579 SH_MUTEX_LOCK(mutex_watches);
580 if (watches->list_of_watches)
581 zAVLInsert(watches->list_of_watches, litem);
582 SH_MUTEX_UNLOCK(mutex_watches);
583
[481]584 sh_fInotify_report_add(litem->file, litem->class, litem->check_flags);
[367]585
586 litem = sh_inotify_list_del_cur(&listcursor, save);
587
588 goto have_next;
589 }
590 }
[371]591 litem = sh_inotify_list_next(&listcursor, save);
[367]592 }
593 SH_MUTEX_UNLOCK(mutex_list_dormant);
594 return 0;
595}
596#endif
597
[261]598/* This function is idempotent; it will add the watch only once
599 */
[367]600int sh_inotify_add_watch(char * filename, sh_watches * watches, int * errnum,
[481]601 int class, unsigned long check_flags, int type, int rdepth)
[261]602{
[367]603 volatile int retval = 0;
604
605 SH_MUTEX_LOCK(mutex_watches);
606
[261]607 *errnum = 0;
608
609 if (filename)
610 {
611 int nwatch;
[364]612 sh_watch * item;
[261]613 int index = index_watched_file(filename, watches);
614
615 if (index < 0)
616 {
617 int ifd = sh_inotify_getfd();
618
[364]619 /*************************************
620
[261]621 if (watches->count == SH_INOTIFY_MAX)
622 {
623#ifdef EMFILE
624 *errnum = EMFILE;
625#else
626 *errnum = 24;
627#endif
628 return -1;
629 }
[364]630 **************************************/
[261]631
632 nwatch = inotify_add_watch (ifd, filename,
[367]633 SH_INOTIFY_FLAGS);
[261]634 if (nwatch < 0)
635 {
636 *errnum = errno;
[367]637 retval = -1;
638 goto retpoint;
[261]639 }
[364]640
641 item = sh_inotify_create_watch(filename, nwatch, /* flag */ 0);
[367]642
643 item->class = class;
[372]644 item->type = type;
[373]645 item->rdepth = rdepth;
[481]646 item->check_flags = check_flags;
[261]647
[364]648 if (NULL == watches->list_of_watches)
[367]649 watches->list_of_watches = zAVLAllocTree (sh_inotify_getkey,
650 zAVL_KEY_INT);
[364]651
652 if (watches->list_of_watches)
653 {
[367]654 *errnum = zAVLInsert((zAVLTree *)(watches->list_of_watches),
655 item);
656
[364]657 if (*errnum != 0)
658 {
[405]659 /* zAVLInsert returns -1 on malloc() error and 3 if
660 * the node already exists.
661 */
662 *errnum = (*errnum == -1) ? ENOMEM : EEXIST;
[364]663 sh_inotify_free_watch(item);
[367]664 retval = -1;
665 goto retpoint;
[364]666 }
667 }
668 else
669 {
[367]670 *errnum = ENOMEM;
[382]671 sh_inotify_free_watch(item);
[367]672 retval = -1;
673 goto retpoint;
[364]674 }
[261]675
676 ++(watches->count);
677 }
[373]678 else if (type == SH_INOTIFY_DIR) /* watch exists */
679 {
680 /* This covers the case that a directory has been added,
681 * but is watched as file at first because it is also
682 * specified as file in the config.
683 */
684 item = zAVLSearch(watches->list_of_watches, &index);
685
686 if (item && item->type == SH_INOTIFY_FILE)
687 {
688 item->type = SH_INOTIFY_DIR;
689 }
690 }
[261]691 }
[367]692 retpoint:
[377]693 ; /* 'label at end of compound statement' */
[367]694 SH_MUTEX_UNLOCK(mutex_watches);
695 return retval;
[261]696}
697
[367]698static void * sh_dummy_sret = NULL;
699
700char * sh_inotify_search_item(sh_watches * watches, int watch,
[481]701 int * class, unsigned long * check_flags,
[373]702 int * type, int * rdepth)
[367]703{
704 sh_watch * item;
705 char * sret = NULL;
706
707 /* Take the address to keep gcc from putting it into a register.
708 * Avoids the 'clobbered by longjmp' warning.
709 */
710 sh_dummy_sret = (void *) &sret;
711
712 SH_MUTEX_LOCK(mutex_watches);
713 item = zAVLSearch(watches->list_of_watches, &watch);
714
715 if (item)
716 {
717 *class = item->class;
[481]718 *check_flags = item->check_flags;
[372]719 *type = item->type;
[373]720 *rdepth = item->rdepth;
[382]721 sret = sh_util_strdup_track(item->file, __FILE__, __LINE__);
[367]722 }
723 SH_MUTEX_UNLOCK(mutex_watches);
724 return sret;
725}
726
727static void * sh_dummy_litem = NULL;
728
[261]729int sh_inotify_wait_for_change(char * filename, sh_watches * watches,
730 int * errnum, int waitsec)
731{
[367]732 sh_watch * litem;
733 sh_watch * zitem;
[364]734 int ifd = sh_inotify_getfd();
[367]735
736 /* Take the address to keep gcc from putting it into a register.
737 * Avoids the 'clobbered by longjmp' warning.
738 */
739 sh_dummy_litem = (void*) &litem;
740
[261]741 *errnum = 0;
742
743 start_it:
744
745 if (ifd >= 0)
746 {
[367]747 volatile ssize_t i = 0;
[261]748 ssize_t len = -1;
749 int flag = 0;
750 char buffer[1024];
751
[364]752 sh_inotify_listCursor listcursor;
753
[261]754 /* -- Add watch if required
755 */
756 if (filename)
757 {
[373]758 if (sh_inotify_add_watch(filename, watches, errnum,
759 0, 0, SH_INOTIFY_FILE, 0) < 0)
[261]760 {
761 retry_msleep(waitsec, 0);
762 return -1;
763 }
764 }
765
[364]766 /* -- Check dormant watches for reopening.
767 */
[367]768 SH_MUTEX_LOCK(mutex_list_dormant);
769
770 for (litem = sh_inotify_list_first(&listcursor, watches); litem;
771 litem = sh_inotify_list_next(&listcursor, watches))
[261]772 {
[364]773 have_next:
[367]774 /* sh_inotify_list_del_cur may return NULL */
775 if (litem && litem->file && litem->watch == -1)
[364]776 {
[367]777 litem->watch = inotify_add_watch (ifd, litem->file,
778 SH_INOTIFY_FLAGS);
779
780 if (litem->watch >= 0)
[364]781 {
[367]782 SH_MUTEX_LOCK(mutex_watches);
783 if (watches->list_of_watches)
784 zAVLInsert(watches->list_of_watches, litem);
785 SH_MUTEX_UNLOCK(mutex_watches);
786 litem = sh_inotify_list_del_cur(&listcursor, watches);
[364]787 goto have_next;
788 }
789 }
[261]790 }
[367]791 SH_MUTEX_UNLOCK(mutex_list_dormant);
[261]792
793
794 /* -- Blocking read on inotify file descriptor
795 */
[371]796 len = sh_inotify_read(buffer, sizeof(buffer));
[261]797
798 if (len > 0)
799 {
800 struct inotify_event *event;
801
802 i = 0;
803
804 while (i < len) {
805
806 event = (struct inotify_event *) &buffer[i];
807
[367]808 SH_MUTEX_LOCK(mutex_watches);
809 zitem = zAVLSearch(watches->list_of_watches, &(event->wd));
[364]810
[367]811 if (zitem)
[261]812 {
[364]813 if (event->mask & IN_MODIFY)
[261]814 {
[367]815 zitem->flag |= SH_INOTIFY_MODIFY;
[364]816 flag |= SH_INOTIFY_MODIFY;
[261]817 }
[364]818 else if (event->mask & IN_DELETE_SELF ||
819 event->mask & IN_UNMOUNT ||
820 event->mask & IN_MOVE_SELF )
821 {
[367]822 zitem->flag |= SH_INOTIFY_REOPEN;
823 (void) inotify_rm_watch(ifd, zitem->watch);
824 zAVLDelete(watches->list_of_watches, zitem);
825 sh_inotify_add_dormant(watches, zitem);
826 zitem->watch = -1;
[364]827 flag |= SH_INOTIFY_REOPEN;
828 }
[261]829 }
[367]830 SH_MUTEX_UNLOCK(mutex_watches);
[364]831
[261]832 i += sizeof (struct inotify_event) + event->len;
833 }
834 }
835 else if (len == -1)
836 {
837 *errnum = errno;
838 retry_msleep(waitsec, 0);
839
840 return -1;
841 }
842
843 if (flag & SH_INOTIFY_REOPEN)
844 {
845 if (flag & SH_INOTIFY_MODIFY)
846 return 0;
847 else
848 goto start_it;
849 }
850
851 return 0;
852 }
853
854 /* Inotify not working, sleep
855 */
856 retry_msleep(waitsec, 0);
857
858 *errnum = 0;
859 return -1;
860}
861
[367]862
[261]863/* !defined(HAVE_SYS_INOTIFY_H) */
864#else
865
866#include "sh_calls.h"
[269]867#include "sh_inotify.h"
[261]868
869void sh_inotify_remove(sh_watches * watches)
870{
871 (void) watches;
872 return;
873}
874
875int sh_inotify_wait_for_change(char * filename, sh_watches * watches,
876 int * errnum, int waitsec)
877{
878 (void) filename;
879 (void) watches;
880
881 /* Inotify not working, sleep for waitsec seconds
882 */
883 retry_msleep(waitsec, 0);
884
[388]885 if (errnum)
886 *errnum = 0;
[261]887 return -1;
888}
889
[367]890int sh_inotify_add_watch(char * filename, sh_watches * watches, int * errnum,
[481]891 int class, unsigned long check_flags, int type, int rdepth)
[261]892{
893 (void) filename;
894 (void) watches;
[367]895 (void) class;
[481]896 (void) check_flags;
[373]897 (void) type;
898 (void) rdepth;
[388]899
900 if (errnum)
901 *errnum = 0;
[261]902 return 0;
903}
904
[373]905int sh_inotify_add_watch_later(const char * filename, sh_watches * watches,
[367]906 int * errnum,
[481]907 int class, unsigned long check_flags, int type, int rdepth)
[367]908{
909 (void) filename;
910 (void) watches;
911 (void) class;
[481]912 (void) check_flags;
[373]913 (void) type;
914 (void) rdepth;
[388]915
916 if (errnum)
917 *errnum = 0;
[367]918 return 0;
919}
920
[261]921#endif
[367]922
923#ifdef SH_CUTEST
924#include "CuTest.h"
925void Test_inotify(CuTest *tc) {
926#if defined(HAVE_SYS_INOTIFY_H) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
927
928 int ret;
929 sh_watches twatch = SH_INOTIFY_INITIALIZER;
930 sh_watch * litem;
931 sh_inotify_listCursor listcursor;
932 char * p;
933 int class;
[373]934 int type;
935 int rdepth;
[481]936 unsigned long check_flags;
[367]937 int nrun = 0;
938
[373]939 sh_watch aw1 = { -1, 0, 0, 1, 99, 1, "a1" };
940 sh_watch aw2 = { -1, 0, 0, 2, 99, 1, "a2" };
941 sh_watch aw3 = { 2, 0, 0, 3, 99, 1, "a3" };
942 sh_watch aw4 = { -1, 0, 0, 4, 99, 1, "a4" };
943 sh_watch aw5 = { 5, 0, 0, 5, 99, 1, "a5" };
[367]944
945 do {
946
947 int count = 0;
948
949 sh_watch * w1 = SH_ALLOC(sizeof(sh_watch));
950 sh_watch * w2 = SH_ALLOC(sizeof(sh_watch));
951 sh_watch * w3 = SH_ALLOC(sizeof(sh_watch));
952 sh_watch * w4 = SH_ALLOC(sizeof(sh_watch));
953 sh_watch * w5 = SH_ALLOC(sizeof(sh_watch));
954
955 memcpy(w1, &aw1, sizeof(sh_watch));
956 w1->file = sh_util_strdup(aw1.file);
957 memcpy(w2, &aw2, sizeof(sh_watch));
958 w2->file = sh_util_strdup(aw2.file);
959 memcpy(w3, &aw3, sizeof(sh_watch));
960 w3->file = sh_util_strdup(aw3.file);
961 memcpy(w4, &aw4, sizeof(sh_watch));
962 w4->file = sh_util_strdup(aw4.file);
963 memcpy(w5, &aw5, sizeof(sh_watch));
964 w5->file = sh_util_strdup(aw5.file);
965
966 ret = sh_inotify_add_dormant(&twatch, w1);
967 CuAssertIntEquals(tc, ret, 0);
968 ret = sh_inotify_add_dormant(&twatch, w2);
969 CuAssertIntEquals(tc, ret, 0);
970 ret = sh_inotify_add_dormant(&twatch, w3);
971 CuAssertIntEquals(tc, ret, 0);
972 ret = sh_inotify_add_dormant(&twatch, w4);
973 CuAssertIntEquals(tc, ret, 0);
974 ret = sh_inotify_add_dormant(&twatch, w5);
975 CuAssertIntEquals(tc, ret, 0);
976
977 /* -- Check dormant watches for reopening.
978 */
979 for (litem = sh_inotify_list_first(&listcursor, &twatch); litem;
980 litem = sh_inotify_list_next(&listcursor, &twatch))
981 {
982 have_next:
983
984 /* sh_inotify_list_del_cur may return NULL */
985 if (litem)
986 {
987 ++count;
988
989 if (litem->file && litem->watch == -1)
990 {
991
992 switch (litem->class)
993 {
994 case 1:
995 CuAssertStrEquals(tc, litem->file, "a1");
996 break;
997 case 2:
998 CuAssertStrEquals(tc, litem->file, "a2");
999 break;
1000 case 3:
1001 CuAssertStrEquals(tc, litem->file, "deadbeef");
1002 break;
1003 case 4:
1004 CuAssertStrEquals(tc, litem->file, "a4");
1005 break;
1006 case 5:
1007 CuAssertStrEquals(tc, litem->file, "deadbeef");
1008 break;
1009 default:
1010 CuAssertStrEquals(tc, litem->file, "deadbeef");
1011 }
1012 litem = sh_inotify_list_del_cur(&listcursor, &twatch);
1013 goto have_next;
1014 }
1015 switch (litem->class)
1016 {
1017 case 3:
1018 CuAssertStrEquals(tc, litem->file, "a3");
1019 break;
1020 case 5:
1021 CuAssertStrEquals(tc, litem->file, "a5");
1022 break;
1023 default:
1024 CuAssertStrEquals(tc, litem->file, "foobar");
1025 }
1026 }
1027 }
1028
1029 CuAssertIntEquals(tc, count, 5);
1030
[481]1031 p = sh_inotify_pop_dormant(&twatch, &class, &check_flags, &type, &rdepth);
[367]1032 CuAssertStrEquals(tc, p, "a5");
1033
[481]1034 p = sh_inotify_pop_dormant(&twatch, &class, &check_flags, &type, &rdepth);
[367]1035 CuAssertStrEquals(tc, p, "a3");
1036 CuAssertIntEquals(tc, class, 3);
1037
[481]1038 p = sh_inotify_pop_dormant(&twatch, &class, &check_flags, &type, &rdepth);
[367]1039 CuAssertTrue(tc, NULL == p);
1040 CuAssertTrue(tc, NULL == twatch.dormant_watches);
1041
1042 ++nrun;
1043
1044 } while (nrun < 100);
1045
1046#else
1047 (void) tc;
1048#endif
1049
1050 return;
1051}
1052#endif
Note: See TracBrowser for help on using the repository browser.