source: trunk/src/sh_inotify.c@ 369

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

Fix bug in sh_inotify_read().

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