1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #if !defined(__FreeBSD__)
6 #include <sys/select.h>
7 #endif
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <time.h>
11 #include <unistd.h>
12 #include "fileschanged.h"
13 #include "monitor.h"
14 #include "node.h"
15 #include "list.h"
16 #include "opts.h"
17 #include "handlers.h"
18 
19 /*
20  * monitor.c:
21  *
22  *   Basic operations are `open' and `close' on the FAMConnection.
23  *
24  *     monitor_open (FAMConnection *c);
25  *     monitor_close (FAMConnection *c);
26  *
27  *   To do anything, you have to `open' the monitor first.
28  *
29  *   While in an open state (eg, we are connected to the FAM server),
30  *   we may `begin', `stop' or `pause' monitoring for certain files.  These
31  *   operations require a LIST of files that we want to begin, stop, or pause
32  *   monitoring of, and the open FAMConnection.
33  *
34  *     monitor_begin(FAMConnection *c, void *list);
35  *     monitor_stop (FAMConnection *c, void *list);
36  *     monitor_pause_toggle (FAMConnection *c, void *list);
37  *
38  *   After files have begun to be monitored, the FAM server will be notifying
39  *   us with events about the files that have somehow changed.  The main loop
40  *   of fileschanged is looped around the `handle_events' function. This
41  *   function needs the open FAMConnection, and the LIST of files that we want
42  *   to handle events for.  It also needs a SECS_TO_WAIT_FOR_PENDING parameter.
43  *   This is the maximum number of seconds to wait for a message to arrive from
44  *   the FAM server.  After that it needs the SECS_TO_HANDLE_PENDING parameter.
45  *   This is the maximum number of seconds to process messages from the server
46  *   before giving up.  If this value is -1, then it never gives up.
47  *
48  *   Note that `begin' also calls `handle_events'.  This is because the server
49  *   can get backlogged trying to send us notifications, when we're not ready
50  *   for them.
51  *   SECS_TO_HANDLE_PENDING is not -1 when it's called from monitor_begin --
52  *   the idea being that we don't want to wait for the all of the messages from
53  *   the server before beginning to monitor more files.
54  *
55  *     monitor_handle_events (FAMConnection *c, void *list,
56  *       int secs_to_wait_for_pending, int secs_to_handle_pending);
57  *
58  *   This function connects to the handlers via handle_event().
59  */
60 int
monitor_open(FAMConnection * c)61 monitor_open (FAMConnection *c)
62 {
63   return FAMOpen (c);
64 }
65 
66 int
monitor_close(FAMConnection * c)67 monitor_close (FAMConnection *c)
68 {
69   return FAMClose (c);
70 }
71 
72 int
monitor_begin(FAMConnection * c,void * list)73 monitor_begin (FAMConnection *c, void *list)
74 {
75   int retval;
76   unsigned int i;
77   unsigned int count;
78   struct node_t *node;
79   list_count (list, &count);
80   for(i = 0; i < count; i++)
81     {
82       list_get_element (list, i, &node);
83       if (S_ISDIR (node->statbuf.st_mode))
84 	{
85 	  //printf ("%04d monitoring directory: '%s'\n", i, node->filename);
86 	  retval = FAMMonitorDirectory (c, node->filename, &node->request,
87 					(void *) node);
88 	  //printf ("FAMMonitorDirectory returns %d (reqnum %d)\n", retval, node->request.reqnum);
89 	}
90       else if (S_ISREG (node->statbuf.st_mode))
91 	{
92 	  //printf ("%04d monitoring file: '%s'\n", i, node->filename);
93 	  retval = FAMMonitorFile (c, node->filename, &node->request,
94 				   (void *) node);
95 	  //printf ("FAMMonitorFile returns %d (reqnum %d)\n", retval, node->request.reqnum);
96 	}
97       monitor_handle_events (c, list, 0, 30);
98     }
99   return 0;
100 }
101 
102 static int
monitor_do(FAMConnection * c,void * list,int (* FAMFunc)(FAMConnection * fc,const FAMRequest * fr))103 monitor_do (FAMConnection *c, void *list, int (*FAMFunc)(FAMConnection *fc, const FAMRequest *fr))
104 {
105   unsigned int i;
106   unsigned int count;
107   struct node_t *node;
108   list_count (list, &count);
109   for(i = 0; i < count; i++)
110     {
111       list_get_element (list, i, &node);
112       FAMFunc (c, &node->request);
113       monitor_handle_events (c, list, 0, 30);
114     }
115   return 0;
116 }
117 
118 int
monitor_stop(FAMConnection * c,void * list)119 monitor_stop (FAMConnection *c, void *list)
120 {
121   return monitor_do (c, list, FAMCancelMonitor);
122 }
123 
124 int
monitor_pause_toggle(FAMConnection * c,void * list)125 monitor_pause_toggle (FAMConnection *c, void *list)
126 {
127   int retval;
128   static int paused;
129   int (*FAMFunc)(FAMConnection *fc, const FAMRequest *fr);
130   if (paused)
131     FAMFunc = FAMResumeMonitor;
132   else
133     FAMFunc = FAMSuspendMonitor;
134   retval = monitor_do (c, list, FAMFunc);
135   paused = !paused;
136   return retval;
137 }
138 
139 static int
monitor_handle_event(FAMConnection * c,void * list,int time_limit)140 monitor_handle_event (FAMConnection *c, void *list, int time_limit)
141 {
142   int retval;
143   FAMEvent e;
144   time_t ending_time;
145   time_t current_time;
146   ending_time = time(NULL) + time_limit;
147   while ((ending_time >= (current_time = time (NULL))) || (time_limit == -1))
148     {
149       if (FAMPending (c))
150 	{
151 	  retval = FAMNextEvent (c, &e);
152 	  if (retval < 0)
153 	    return -1;
154 	}
155       else
156 	{
157 	  //second chance for the FAM server.  yay.
158 	  sleep (0);
159 	  if (!FAMPending (c))
160 	    break;
161 	}
162       switch (e.code)
163 	{
164 	case FAMExists:
165 	case FAMEndExist:
166 	case FAMAcknowledge:
167 	case FAMMoved:
168 	  continue;
169 	case FAMChanged:
170 	case FAMDeleted:
171 	case FAMStartExecuting:
172 	case FAMStopExecuting:
173 	case FAMCreated:
174 	    {
175 	      retval = handle_event (c, list, &e, current_time);
176 	      if (retval < 0)
177 		return -1;
178 	      break;
179 	    }
180 	}
181     }
182   return 0;
183 }
184 
185 int
monitor_handle_events(FAMConnection * c,void * list,int secs_to_wait_for_pending,int secs_to_handle_pending)186 monitor_handle_events (FAMConnection *c, void *list, int secs_to_wait_for_pending, int secs_to_handle_pending)
187 {
188   fd_set rfds;
189   int numfds;
190   struct timeval *tv_ptr = NULL;
191   struct timeval tv;
192   int retval;
193   tv.tv_sec = secs_to_wait_for_pending; //wait for fam events for 2 seconds.
194   tv.tv_usec = 500; //always add a bit here.
195   tv_ptr = &tv;
196   FD_ZERO (&rfds);
197   FD_SET (c->fd, &rfds);
198   numfds = select (FD_SETSIZE, &rfds, NULL, NULL, tv_ptr);
199   if (numfds == 1)
200     {//if a fam event happened,
201       retval = monitor_handle_event (c, list, secs_to_handle_pending);
202       //get notifications for 30secs tops.
203       if (retval < 0)
204 	{
205 	  return -1;
206 	}
207     }
208   return 0;
209 }
210