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