2 * Copyright (c) 2010 Mark Williams
4 * File change event API for netatalk
6 * for every detected filesystem change a UDP packet is sent to an arbitrary list
7 * of listeners. Each packet contains unix path of modified filesystem element,
8 * event reason, and a consecutive event id (32 bit). Technically we are UDP client and are sending
9 * out packets synchronuosly as they are created by the afp functions. This should not affect
10 * performance measurably. The only delaying calls occur during initialization, if we have to
11 * resolve non-IP hostnames to IP. All numeric data inside the packet is network byte order, so use
12 * ntohs / ntohl to resolve length and event id. Ideally a listener receives every packet with
13 * no gaps in event ids, starting with event id 1 and mode FCE_CONN_START followed by
14 * data events from id 2 up to 0xFFFFFFFF, followed by 0 to 0xFFFFFFFF and so on.
16 * A gap or not starting with 1 mode FCE_CONN_START or receiving mode FCE_CONN_BROKEN means that
17 * the listener has lost at least one filesystem event
19 * All Rights Reserved. See COPYRIGHT.
24 #endif /* HAVE_CONFIG_H */
34 #include <sys/param.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
40 #include <netatalk/at.h>
42 #include <atalk/adouble.h>
43 #include <atalk/vfs.h>
44 #include <atalk/logger.h>
45 #include <atalk/afp.h>
46 #include <atalk/util.h>
47 #include <atalk/cnid.h>
48 #include <atalk/unix.h>
49 #include <atalk/fce_api.h>
50 #include <atalk/globals.h>
54 #include "directory.h"
58 // ONLY USED IN THIS FILE
59 #include "fce_api_internal.h"
64 /* We store our connection data here */
65 static uint32_t coalesce = 0;
66 static struct fce_history fce_history_list[FCE_HISTORY_LEN];
69 * With coalesce we try to reduce the events over UDP, the eventlistener would throw these
71 * This works only, if the connected listener uses the events on a "per directory" base
72 * It is a very simple aproach, but saves a lot of events sent to listeners.
73 * Every "child element" event is ignored as long as its parent event is not older
74 * than MAX_COALESCE_TIME_MS ms. If large directory trees or large files are created or deleted,
75 * this probably will not work recursive, because the time to copy data will exceed this
80 static long get_ms_difftime ( struct timeval *tv1, struct timeval *tv2 )
82 unsigned long s = tv2->tv_sec - tv1->tv_sec;
83 long us = tv2->tv_usec - tv1->tv_usec;
85 return s * 1000 + us/1000;
88 /******************************************************************************
89 * Public functions follow
90 ******************************************************************************/
92 void fce_initialize_history()
94 for (int i = 0; i < FCE_HISTORY_LEN; i++) {
95 memset( &fce_history_list[i], 0, sizeof(fce_history_list[i]) );
99 int fce_handle_coalescation( char *path, int is_dir, int mode )
101 /* These two are used to eval our next index in history */
102 /* the history is unsorted, speed should not be a problem, length is 10 */
103 unsigned long oldest_entry = (unsigned long )((long)-1);
104 int oldest_entry_idx = -1;
110 /* After a file creation *ALWAYS* a file modification is produced */
111 if ((mode == FCE_FILE_CREATE) && (coalesce & FCE_COALESCE_CREATE))
115 gettimeofday(&tv, 0);
117 /* Now detect events in the very near history */
118 for (int i = 0; i < FCE_HISTORY_LEN; i++) {
119 struct fce_history *fh = &fce_history_list[i];
122 if (fh->tv.tv_sec == 0) {
123 /* we can use it for new elements */
125 oldest_entry_idx = i;
130 if (get_ms_difftime( &fh->tv, &tv ) > MAX_COALESCE_TIME_MS) {
131 /* Invalidate entry */
134 oldest_entry_idx = i;
139 /* If we find a parent dir wich was created we are done */
140 if ((coalesce & FCE_COALESCE_CREATE) && (fh->mode == FCE_DIR_CREATE)) {
142 if (!strncmp(fh->path, path, strlen(fh->path)))
146 /* If we find a parent dir we should be DELETED we are done */
147 if ((coalesce & FCE_COALESCE_DELETE)
149 && (mode == FCE_FILE_DELETE || mode == FCE_DIR_DELETE)) {
151 if (!strncmp(fh->path, path, strlen(fh->path)))
155 /* Detect oldest entry for next new entry */
156 if (oldest_entry_idx == -1 || fh->tv.tv_sec < oldest_entry) {
157 oldest_entry = fh->tv.tv_sec;
158 oldest_entry_idx = i;
162 /* We have a new entry for the history, register it */
163 fce_history_list[oldest_entry_idx].tv = tv;
164 fce_history_list[oldest_entry_idx].mode = mode;
165 fce_history_list[oldest_entry_idx].is_dir = is_dir;
166 strncpy( fce_history_list[oldest_entry_idx].path, path, MAXPATHLEN);
168 /* we have to handle this event */
173 * Set event coalescation to reduce number of events sent over UDP
177 int fce_set_coalesce(char *opt)
187 for (p = strtok(e, ","); p; p = strtok(NULL, ",")) {
188 if (strcmp(p, "all") == 0) {
189 coalesce = FCE_COALESCE_ALL;
190 } else if (strcmp(p, "delete") == 0) {
191 coalesce = FCE_COALESCE_DELETE;
192 } else if (strcmp(p, "create") == 0) {
193 coalesce = FCE_COALESCE_CREATE;