2 * FAM file notification support.
4 * Copyright (c) James Peach 2005
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #ifdef HAVE_FAM_CHANGE_NOTIFY
27 /* NOTE: There are multiple versions of FAM floating around the net, each with
28 * slight differences from the original SGI FAM implementation. In this file,
29 * we rely only on the SGI features and do not assume any extensions. For
30 * example, we do not look at FAMErrno, because it is not set by the original
34 * http://oss.sgi.com/projects/fam/
35 * http://savannah.nongnu.org/projects/fam/
36 * http://sourceforge.net/projects/bsdfam/
46 /* We are waiting for an event. */
48 /* An event has been receive, but we haven't been able to send it back
49 * to the client yet. It is stashed in the code member.
55 /* Don't initialise this until the first register request. We want a single
56 * FAM connection for each worker smbd. If we allow the master (parent) smbd to
57 * open a FAM connection, multiple processes talking on the same socket will
58 * undoubtedly create havoc.
60 static FAMConnection global_fc;
61 static int global_fc_generation;
64 #define FAM_TRACE_LOW 10
66 #define FAM_NOTIFY_CHECK_TIMEOUT 1 /* secs */
67 #define FAM_EVENT_DRAIN ((uint32_t)(-1))
69 /* Turn a FAM event code into a string. Don't rely on specific code values,
70 * because that might not work across all flavours of FAM.
73 fam_event_str(enum FAMCodes code)
75 static struct { enum FAMCodes code; const char * name; } evstr[] =
77 { FAMChanged, "FAMChanged"},
78 { FAMDeleted, "FAMDeleted"},
79 { FAMStartExecuting, "FAMStartExecuting"},
80 { FAMStopExecuting, "FAMStopExecuting"},
81 { FAMCreated, "FAMCreated"},
82 { FAMMoved, "FAMMoved"},
83 { FAMAcknowledge, "FAMAcknowledge"},
84 { FAMExists, "FAMExists"},
85 { FAMEndExist, "FAMEndExist"}
90 for (i = 0; i < ARRAY_SIZE(evstr); ++i) {
91 if (code == evstr[i].code)
92 return(evstr[i].name);
99 fam_check_reconnect(void)
101 if (FAMCONNECTION_GETFD(&global_fc) < 0) {
104 global_fc_generation++;
105 snprintf(name, sizeof(name), "smbd (%lu)", (unsigned long)sys_getpid());
107 if (FAMOpen2(&global_fc, name) < 0) {
108 DEBUG(0, ("failed to connect to FAM service\n"));
117 fam_monitor_path(connection_struct * conn,
118 struct fam_req_info * info,
125 DEBUG(FAM_TRACE, ("requesting FAM notifications for '%s'\n", path));
127 /* FAM needs an absolute pathname. */
129 /* It would be better to use reduce_name() here, but reduce_name does not
130 * actually return the reduced result. How utterly un-useful.
132 pstrcpy(fullpath, path);
133 if (!canonicalize_path(conn, fullpath)) {
134 DEBUG(0, ("failed to canonicalize path '%s'\n", path));
138 if (*fullpath != '/') {
139 DEBUG(0, ("canonicalized path '%s' into `%s`\n", path, fullpath));
140 DEBUGADD(0, ("but expected an absolute path\n"));
144 if (SMB_VFS_STAT(conn, path, &st) < 0) {
145 DEBUG(0, ("stat of '%s' failed: %s\n", path, strerror(errno)));
148 /* Start monitoring this file or directory. We hand the state structure to
149 * both the caller and the FAM library so we can match up the caller's
150 * status requests with FAM notifications.
152 if (S_ISDIR(st.st_mode)) {
153 FAMMonitorDirectory(&global_fc, fullpath, &(info->req), info);
155 FAMMonitorFile(&global_fc, fullpath, &(info->req), info);
158 /* Grr. On IRIX, neither of the monitor functions return a status. */
160 /* We will stay in initialising state until we see the FAMendExist message
163 info->state = FAM_REQ_MONITORING;
164 info->generation = global_fc_generation;
169 fam_handle_event(enum FAMCodes code, uint32 flags)
171 #define F_CHANGE_MASK (FILE_NOTIFY_CHANGE_FILE | \
172 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
173 FILE_NOTIFY_CHANGE_SIZE | \
174 FILE_NOTIFY_CHANGE_LAST_WRITE | \
175 FILE_NOTIFY_CHANGE_LAST_ACCESS | \
176 FILE_NOTIFY_CHANGE_CREATION | \
177 FILE_NOTIFY_CHANGE_EA | \
178 FILE_NOTIFY_CHANGE_SECURITY)
180 #define F_DELETE_MASK (FILE_NOTIFY_CHANGE_FILE_NAME | \
181 FILE_NOTIFY_CHANGE_DIR_NAME)
183 #define F_CREATE_MASK (FILE_NOTIFY_CHANGE_FILE_NAME | \
184 FILE_NOTIFY_CHANGE_DIR_NAME)
188 if (flags & F_CHANGE_MASK)
192 if (flags & F_DELETE_MASK)
196 if (flags & F_CREATE_MASK)
200 /* Ignore anything else. */
212 fam_pump_events(struct fam_req_info * info, uint32_t flags)
218 /* If we are draining the event queue we must keep going until we find
219 * the correct FAMAcknowledge event or the connection drops. Otherwise
220 * we should stop when there are no more events pending.
222 if (flags != FAM_EVENT_DRAIN && !FAMPending(&global_fc)) {
226 if (FAMNextEvent(&global_fc, &ev) < 0) {
227 DEBUG(0, ("failed to fetch pending FAM event\n"));
228 DEBUGADD(0, ("resetting FAM connection\n"));
229 FAMClose(&global_fc);
230 FAMCONNECTION_GETFD(&global_fc) = -1;
234 DEBUG(FAM_TRACE_LOW, ("FAM event %s on '%s' for request %d\n",
235 fam_event_str(ev.code), ev.filename, ev.fr.reqnum));
239 /* FAM generates an ACK event when we cancel a monitor. We need
240 * this to know when it is safe to free out request state
243 if (info->generation == global_fc_generation &&
244 info->req.reqnum == ev.fr.reqnum &&
245 flags == FAM_EVENT_DRAIN) {
251 /* Ignore these. FAM sends these enumeration events when we
252 * start monitoring. If we are monitoring a directory, we will
253 * get a FAMExists event for each directory entry.
256 /* TODO: we might be able to use these to implement recursive
257 * monitoring of entire subtrees.
260 /* These events never happen. A move or rename shows up as a
261 * create/delete pair.
263 case FAMStartExecuting:
264 case FAMStopExecuting:
265 /* We might get these, but we just don't care. */
271 if (info->generation != global_fc_generation) {
272 /* Ignore this; the req number can't be matched. */
276 if (info->req.reqnum == ev.fr.reqnum) {
277 /* This is the event the caller was interested in. */
278 DEBUG(FAM_TRACE, ("handling FAM %s event on '%s'\n",
279 fam_event_str(ev.code), ev.filename));
280 /* Ignore events if we are draining this request. */
281 if (flags != FAM_EVENT_DRAIN) {
282 return(fam_handle_event(ev.code, flags));
286 /* Caller doesn't want this event. Stash the result so we
287 * can come back to it. Unfortunately, FAM doesn't
288 * guarantee to give us back evinfo.
290 struct fam_req_info * evinfo =
291 (struct fam_req_info *)ev.userdata;
294 DEBUG(FAM_TRACE, ("storing FAM %s event for winter\n",
295 fam_event_str(ev.code)));
296 evinfo->state = FAM_REQ_FIRED;
297 evinfo->code = ev.code;
299 DEBUG(2, ("received FAM %s notification for %s, "
300 "but userdata was unexpectedly NULL\n",
301 fam_event_str(ev.code), ev.filename));
307 DEBUG(0, ("ignoring unknown FAM event code %d for `%s`\n",
308 ev.code, ev.filename));
312 /* No more notifications pending. */
317 fam_test_connection(void)
321 /* On IRIX FAMOpen2 leaks 960 bytes in 48 blocks. It's a deliberate leak
322 * in the library and there's nothing we can do about it here.
324 if (FAMOpen2(&fc, "smbd probe") < 0)
331 /* ------------------------------------------------------------------------- */
334 fam_register_notify(connection_struct * conn,
338 struct fam_req_info * info;
340 if (!fam_check_reconnect()) {
344 if ((info = SMB_MALLOC_P(struct fam_req_info)) == NULL) {
345 DEBUG(0, ("malloc of %d bytes failed\n", sizeof(struct fam_req_info)));
349 if (fam_monitor_path(conn, info, path, flags)) {
358 fam_check_notify(connection_struct * conn,
365 struct fam_req_info * info;
367 info = (struct fam_req_info *)data;
368 SMB_ASSERT(info != NULL);
370 DEBUG(10, ("checking FAM events for `%s`\n", path));
372 if (info->state == FAM_REQ_FIRED) {
373 DEBUG(FAM_TRACE, ("handling previously fired FAM %s event\n",
374 fam_event_str(info->code)));
375 info->state = FAM_REQ_MONITORING;
376 return(fam_handle_event(info->code, flags));
379 if (!fam_check_reconnect()) {
383 if (info->generation != global_fc_generation) {
384 DEBUG(FAM_TRACE, ("reapplying stale FAM monitor to %s\n", path));
385 fam_monitor_path(conn, info, path, flags);
389 return(fam_pump_events(info, flags));
393 fam_remove_notify(void * data)
395 struct fam_req_info * info;
397 if ((info = (struct fam_req_info *)data) == NULL)
400 /* No need to reconnect. If the FAM connection is gone, there's no need to
401 * cancel and we can safely let FAMCancelMonitor fail. If it we
402 * reconnected, then the generation check will stop us cancelling the wrong
406 if (info->generation == global_fc_generation) {
407 DEBUG(FAM_TRACE, ("removing FAM notification for request %d\n",
409 FAMCancelMonitor(&global_fc, &(info->req));
411 /* Soak up all events until the FAMAcknowledge. We can't free
412 * our request state until we are sure there are no more events in
415 fam_pump_events(info, FAM_EVENT_DRAIN);
421 struct cnotify_fns * fam_notify_init(void)
423 static struct cnotify_fns global_fam_notify =
428 FAM_NOTIFY_CHECK_TIMEOUT
431 /* TODO: rather than relying on FAM_NOTIFY_CHECK_TIMEOUT, we should have an
432 * API to push the FAM fd into the global server fd set.
435 FAMCONNECTION_GETFD(&global_fc) = -1;
437 if (!fam_test_connection()) {
438 DEBUG(0, ("FAM file change notifications not available\n"));
442 DEBUG(FAM_TRACE, ("enabling FAM change notifications\n"));
443 return &global_fam_notify;
446 #endif /* HAVE_FAM_CHANGE_NOTIFY */