source: trunk/src/sh_inotify.c@ 377

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

Fix for ticket #272 (Compile errors on RHEL5, gcc 4.1.2).

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