source: trunk/src/sh_inotify.c@ 400

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

Fix for ticket #285 (NULL pointer dereference on systems without inotify).

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