source: trunk/src/sh_inotify.c @ 481

Last change on this file since 481 was 481, checked in by katerina, 6 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.