source: trunk/src/sh_inotify.c@ 372

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

One more patch for ticket #265 (inotify). Handle dirs that are only specified as files.

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