source: trunk/src/sh_inotify.c@ 382

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

Fix for ticket #280 (Memory leak in inotify related code).

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