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 #if !defined(HAVE_FAM_H_FAMCODES_TYPEDEF)
28 /* Gamin provides this typedef which means we can't use 'enum FAMCodes' as per
29 * every other FAM implementation. Phooey.
31 typedef enum FAMCodes FAMCodes
;
34 /* NOTE: There are multiple versions of FAM floating around the net, each with
35 * slight differences from the original SGI FAM implementation. In this file,
36 * we rely only on the SGI features and do not assume any extensions. For
37 * example, we do not look at FAMErrno, because it is not set by the original
41 * http://oss.sgi.com/projects/fam/
42 * http://savannah.nongnu.org/projects/fam/
43 * http://sourceforge.net/projects/bsdfam/
53 /* We are waiting for an event. */
55 /* An event has been receive, but we haven't been able to send it back
56 * to the client yet. It is stashed in the code member.
62 /* Don't initialise this until the first register request. We want a single
63 * FAM connection for each worker smbd. If we allow the master (parent) smbd to
64 * open a FAM connection, multiple processes talking on the same socket will
65 * undoubtedly create havoc.
67 static FAMConnection global_fc
;
68 static int global_fc_generation
;
71 #define FAM_TRACE_LOW 10
73 #define FAM_EVENT_DRAIN ((uint32_t)(-1))
75 static void * fam_register_notify(connection_struct
* conn
,
79 static BOOL
fam_check_notify(connection_struct
* conn
,
86 static void fam_remove_notify(void * data
);
88 static struct cnotify_fns global_fam_notify
=
97 /* Turn a FAM event code into a string. Don't rely on specific code values,
98 * because that might not work across all flavours of FAM.
101 fam_event_str(FAMCodes code
)
103 static const struct { FAMCodes code
; const char * name
; } evstr
[] =
105 { FAMChanged
, "FAMChanged"},
106 { FAMDeleted
, "FAMDeleted"},
107 { FAMStartExecuting
, "FAMStartExecuting"},
108 { FAMStopExecuting
, "FAMStopExecuting"},
109 { FAMCreated
, "FAMCreated"},
110 { FAMMoved
, "FAMMoved"},
111 { FAMAcknowledge
, "FAMAcknowledge"},
112 { FAMExists
, "FAMExists"},
113 { FAMEndExist
, "FAMEndExist"}
118 for (i
= 0; i
< ARRAY_SIZE(evstr
); ++i
) {
119 if (code
== evstr
[i
].code
)
120 return(evstr
[i
].name
);
127 fam_check_reconnect(void)
129 if (FAMCONNECTION_GETFD(&global_fc
) < 0) {
132 global_fc_generation
++;
133 snprintf(name
, sizeof(name
), "smbd (%lu)", (unsigned long)sys_getpid());
135 if (FAMOpen2(&global_fc
, name
) < 0) {
136 DEBUG(0, ("failed to connect to FAM service\n"));
141 global_fam_notify
.notification_fd
= FAMCONNECTION_GETFD(&global_fc
);
146 fam_monitor_path(connection_struct
* conn
,
147 struct fam_req_info
* info
,
154 DEBUG(FAM_TRACE
, ("requesting FAM notifications for '%s'\n", path
));
156 /* FAM needs an absolute pathname. */
158 /* It would be better to use reduce_name() here, but reduce_name does not
159 * actually return the reduced result. How utterly un-useful.
161 pstrcpy(fullpath
, path
);
162 if (!canonicalize_path(conn
, fullpath
)) {
163 DEBUG(0, ("failed to canonicalize path '%s'\n", path
));
167 if (*fullpath
!= '/') {
168 DEBUG(0, ("canonicalized path '%s' into `%s`\n", path
, fullpath
));
169 DEBUGADD(0, ("but expected an absolute path\n"));
173 if (SMB_VFS_STAT(conn
, path
, &st
) < 0) {
174 DEBUG(0, ("stat of '%s' failed: %s\n", path
, strerror(errno
)));
177 /* Start monitoring this file or directory. We hand the state structure to
178 * both the caller and the FAM library so we can match up the caller's
179 * status requests with FAM notifications.
181 if (S_ISDIR(st
.st_mode
)) {
182 FAMMonitorDirectory(&global_fc
, fullpath
, &(info
->req
), info
);
184 FAMMonitorFile(&global_fc
, fullpath
, &(info
->req
), info
);
187 /* Grr. On IRIX, neither of the monitor functions return a status. */
189 /* We will stay in initialising state until we see the FAMendExist message
192 info
->state
= FAM_REQ_MONITORING
;
193 info
->generation
= global_fc_generation
;
198 fam_handle_event(const FAMCodes code
, uint32 flags
)
200 #define F_CHANGE_MASK (FILE_NOTIFY_CHANGE_FILE | \
201 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
202 FILE_NOTIFY_CHANGE_SIZE | \
203 FILE_NOTIFY_CHANGE_LAST_WRITE | \
204 FILE_NOTIFY_CHANGE_LAST_ACCESS | \
205 FILE_NOTIFY_CHANGE_CREATION | \
206 FILE_NOTIFY_CHANGE_EA | \
207 FILE_NOTIFY_CHANGE_SECURITY)
209 #define F_DELETE_MASK (FILE_NOTIFY_CHANGE_FILE_NAME | \
210 FILE_NOTIFY_CHANGE_DIR_NAME)
212 #define F_CREATE_MASK (FILE_NOTIFY_CHANGE_FILE_NAME | \
213 FILE_NOTIFY_CHANGE_DIR_NAME)
217 if (flags
& F_CHANGE_MASK
)
221 if (flags
& F_DELETE_MASK
)
225 if (flags
& F_CREATE_MASK
)
229 /* Ignore anything else. */
241 fam_pump_events(struct fam_req_info
* info
, uint32_t flags
)
247 /* If we are draining the event queue we must keep going until we find
248 * the correct FAMAcknowledge event or the connection drops. Otherwise
249 * we should stop when there are no more events pending.
251 if (flags
!= FAM_EVENT_DRAIN
&& !FAMPending(&global_fc
)) {
255 if (FAMNextEvent(&global_fc
, &ev
) < 0) {
256 DEBUG(0, ("failed to fetch pending FAM event\n"));
257 DEBUGADD(0, ("resetting FAM connection\n"));
258 FAMClose(&global_fc
);
259 FAMCONNECTION_GETFD(&global_fc
) = -1;
263 DEBUG(FAM_TRACE_LOW
, ("FAM event %s on '%s' for request %d\n",
264 fam_event_str(ev
.code
), ev
.filename
, ev
.fr
.reqnum
));
268 /* FAM generates an ACK event when we cancel a monitor. We need
269 * this to know when it is safe to free out request state
272 if (info
->generation
== global_fc_generation
&&
273 info
->req
.reqnum
== ev
.fr
.reqnum
&&
274 flags
== FAM_EVENT_DRAIN
) {
280 /* Ignore these. FAM sends these enumeration events when we
281 * start monitoring. If we are monitoring a directory, we will
282 * get a FAMExists event for each directory entry.
285 /* TODO: we might be able to use these to implement recursive
286 * monitoring of entire subtrees.
289 /* These events never happen. A move or rename shows up as a
290 * create/delete pair.
292 case FAMStartExecuting
:
293 case FAMStopExecuting
:
294 /* We might get these, but we just don't care. */
300 if (info
->generation
!= global_fc_generation
) {
301 /* Ignore this; the req number can't be matched. */
305 if (info
->req
.reqnum
== ev
.fr
.reqnum
) {
306 /* This is the event the caller was interested in. */
307 DEBUG(FAM_TRACE
, ("handling FAM %s event on '%s'\n",
308 fam_event_str(ev
.code
), ev
.filename
));
309 /* Ignore events if we are draining this request. */
310 if (flags
!= FAM_EVENT_DRAIN
) {
311 return(fam_handle_event(ev
.code
, flags
));
315 /* Caller doesn't want this event. Stash the result so we
316 * can come back to it. Unfortunately, FAM doesn't
317 * guarantee to give us back evinfo.
319 struct fam_req_info
* evinfo
=
320 (struct fam_req_info
*)ev
.userdata
;
323 DEBUG(FAM_TRACE
, ("storing FAM %s event for winter\n",
324 fam_event_str(ev
.code
)));
325 evinfo
->state
= FAM_REQ_FIRED
;
326 evinfo
->code
= ev
.code
;
328 DEBUG(2, ("received FAM %s notification for %s, "
329 "but userdata was unexpectedly NULL\n",
330 fam_event_str(ev
.code
), ev
.filename
));
336 DEBUG(0, ("ignoring unknown FAM event code %d for `%s`\n",
337 ev
.code
, ev
.filename
));
341 /* No more notifications pending. */
346 fam_test_connection(void)
350 /* On IRIX FAMOpen2 leaks 960 bytes in 48 blocks. It's a deliberate leak
351 * in the library and there's nothing we can do about it here.
353 if (FAMOpen2(&fc
, "smbd probe") < 0)
360 /* ------------------------------------------------------------------------- */
363 fam_register_notify(connection_struct
* conn
,
367 struct fam_req_info
* info
;
369 if (!fam_check_reconnect()) {
373 if ((info
= SMB_MALLOC_P(struct fam_req_info
)) == NULL
) {
374 DEBUG(0, ("malloc of %u bytes failed\n", (unsigned int)sizeof(struct fam_req_info
)));
378 if (fam_monitor_path(conn
, info
, path
, flags
)) {
387 fam_check_notify(connection_struct
* conn
,
394 struct fam_req_info
* info
;
396 info
= (struct fam_req_info
*)data
;
397 SMB_ASSERT(info
!= NULL
);
399 DEBUG(10, ("checking FAM events for `%s`\n", path
));
401 if (info
->state
== FAM_REQ_FIRED
) {
402 DEBUG(FAM_TRACE
, ("handling previously fired FAM %s event\n",
403 fam_event_str(info
->code
)));
404 info
->state
= FAM_REQ_MONITORING
;
405 return(fam_handle_event(info
->code
, flags
));
408 if (!fam_check_reconnect()) {
412 if (info
->generation
!= global_fc_generation
) {
413 DEBUG(FAM_TRACE
, ("reapplying stale FAM monitor to %s\n", path
));
414 fam_monitor_path(conn
, info
, path
, flags
);
418 return(fam_pump_events(info
, flags
));
422 fam_remove_notify(void * data
)
424 struct fam_req_info
* info
;
426 if ((info
= (struct fam_req_info
*)data
) == NULL
)
429 /* No need to reconnect. If the FAM connection is gone, there's no need to
430 * cancel and we can safely let FAMCancelMonitor fail. If it we
431 * reconnected, then the generation check will stop us cancelling the wrong
435 if ((FAMCONNECTION_GETFD(&global_fc
) != -1)
436 && (info
->generation
== global_fc_generation
)) {
437 DEBUG(FAM_TRACE
, ("removing FAM notification for request %d\n",
439 FAMCancelMonitor(&global_fc
, &(info
->req
));
441 /* Soak up all events until the FAMAcknowledge. We can't free
442 * our request state until we are sure there are no more events in
445 fam_pump_events(info
, FAM_EVENT_DRAIN
);
451 struct cnotify_fns
* fam_notify_init(void)
453 FAMCONNECTION_GETFD(&global_fc
) = -1;
455 if (!fam_test_connection()) {
456 DEBUG(0, ("FAM file change notifications not available\n"));
460 DEBUG(FAM_TRACE
, ("enabling FAM change notifications\n"));
461 return &global_fam_notify
;
464 #endif /* HAVE_FAM_CHANGE_NOTIFY */