source: trunk/src/sh_inotify.c@ 481

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

Enhancements and fixes for tickets #374, #375, #376, #377, #378, and #379.

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