2 * $Id: fce_api.c,v 0.01 2010-10-01 00:00:0 mw Exp $
\r
4 * Copyright (c) 2010 Mark Williams
\r
6 * File change event API for netatalk
\r
8 * for every detected filesystem change a UDP packet is sent to an arbitrary list
\r
9 * of listeners. Each packet contains unix path of modified filesystem element,
\r
10 * event reason, and a consecutive event id (32 bit). Technically we are UDP client and are sending
\r
11 * out packets synchronuosly as they are created by the afp functions. This should not affect
\r
12 * performance measurably. The only delaying calls occur during initialization, if we have to
\r
13 * resolve non-IP hostnames to IP. All numeric data inside the packet is network byte order, so use
\r
14 * ntohs / ntohl to resolve length and event id. Ideally a listener receives every packet with
\r
15 * no gaps in event ids, starting with event id 1 and mode FCE_CONN_START followed by
\r
16 * data events from id 2 up to 0xFFFFFFFF, followed by 0 to 0xFFFFFFFF and so on.
\r
18 * A gap or not starting with 1 mode FCE_CONN_START or receiving mode FCE_CONN_BROKEN means that
\r
19 * the listener has lost at least one filesystem event
\r
21 * All Rights Reserved. See COPYRIGHT.
\r
24 #ifdef HAVE_CONFIG_H
\r
26 #endif /* HAVE_CONFIG_H */
\r
36 #include <sys/param.h>
\r
37 #include <sys/socket.h>
\r
38 #include <netinet/in.h>
\r
39 #include <arpa/inet.h>
\r
42 #include <netatalk/at.h>
\r
44 #include <atalk/adouble.h>
\r
45 #include <atalk/vfs.h>
\r
46 #include <atalk/logger.h>
\r
47 #include <atalk/afp.h>
\r
48 #include <atalk/util.h>
\r
49 #include <atalk/cnid.h>
\r
50 #include <atalk/unix.h>
\r
51 #include <atalk/fce_api.h>
\r
55 #include "globals.h"
\r
56 #include "directory.h"
\r
57 #include "desktop.h"
\r
60 // ONLY USED IN THIS FILE
\r
61 #include "fce_api_internal.h"
\r
66 /* We store our connection data here */
\r
67 static char coalesce[80] = {""};
\r
68 static struct fce_history fce_history_list[FCE_HISTORY_LEN];
\r
74 * With coalesce we try to reduce the events over UDP, the eventlistener would throw these
\r
75 * events away anyway.
\r
76 * This works only, if the connected listener uses the events on a "per directory" base
\r
77 * It is a very simple aproach, but saves a lot of events sent to listeners.
\r
78 * Every "child element" event is ignored as long as its parent event is not older
\r
79 * than MAX_COALESCE_TIME_MS ms. If large directory trees or large files are created or deleted,
\r
80 * this probably will not work recursive, because the time to copy data will exceed this
\r
84 static int coalesce_none()
\r
86 return coalesce[0] == 0;
\r
88 static int coalesce_all()
\r
90 return !strcmp( coalesce, "all" );
\r
92 static int coalesce_create()
\r
94 return !strcmp( coalesce, "create" ) || coalesce_all();
\r
96 static int coalesce_delete()
\r
98 return !strcmp( coalesce, "delete" ) || coalesce_all();
\r
101 void fce_initialize_history()
\r
103 for (int i = 0; i < FCE_HISTORY_LEN; i++)
\r
105 memset( &fce_history_list[i], 0, sizeof(fce_history_list[i]) );
\r
109 static long get_ms_difftime ( struct timeval *tv1, struct timeval *tv2 )
\r
111 unsigned long s = tv2->tv_sec - tv1->tv_sec;
\r
112 long us = tv2->tv_usec - tv1->tv_usec;
\r
114 return s * 1000 + us/1000;
\r
117 int fce_handle_coalescation( char *path, int is_dir, int mode )
\r
119 if (coalesce_none())
\r
125 // After a file creation *ALWAYS* a file modification is produced
\r
126 if (mode == FCE_FILE_CREATE)
\r
128 if (coalesce_create())
\r
134 /* get timestamp */
\r
136 gettimeofday(&tv, 0);
\r
139 /* These two are used to eval our next index in history */
\r
140 /* the history is unsorted, speed should not be a problem, length is 10 */
\r
141 unsigned long oldest_entry = (unsigned long )((long)-1);
\r
142 int oldest_entry_idx = -1;
\r
144 /* Now detect events in the very near history */
\r
145 for (int i = 0; i < FCE_HISTORY_LEN; i++)
\r
147 struct fce_history *fh = &fce_history_list[i];
\r
149 //* Not inited ? */
\r
150 if (fh->tv.tv_sec == 0)
\r
152 /* we can use it for new elements */
\r
154 oldest_entry_idx = i;
\r
159 if (get_ms_difftime( &fh->tv, &tv ) > MAX_COALESCE_TIME_MS)
\r
161 /* Invalidate entry */
\r
165 oldest_entry_idx = i;
\r
170 /* If we find a parent dir wich was created we are done */
\r
171 if (coalesce_create() && fh->mode == FCE_DIR_CREATE)
\r
173 //* Parent dir ? */
\r
174 if (!strncmp( fh->path, path, strlen( fh->path ) ) )
\r
180 /* If we find a parent dir we should be DELETED we are done */
\r
181 if (coalesce_delete() && fh->is_dir && (mode == FCE_FILE_DELETE || mode == FCE_DIR_DELETE))
\r
183 //* Parent dir ? */
\r
184 if (!strncmp( fh->path, path, strlen( fh->path ) ) )
\r
190 //* Detect oldest entry for next new entry */
\r
191 if (oldest_entry_idx == -1 || fh->tv.tv_sec < oldest_entry)
\r
193 oldest_entry = fh->tv.tv_sec;
\r
194 oldest_entry_idx = i;
\r
198 /* We have a new entry for the history, register it */
\r
199 fce_history_list[oldest_entry_idx].tv = tv;
\r
200 fce_history_list[oldest_entry_idx].mode = mode;
\r
201 fce_history_list[oldest_entry_idx].is_dir = is_dir;
\r
202 strncpy( fce_history_list[oldest_entry_idx].path, path, MAXPATHLEN);
\r
204 /* we have to handle this event */
\r
210 * Set event coalescation to reduce number of events sent over UDP
\r
211 * all|delete|create
\r
216 int fce_set_coalesce( char *coalesce_opt )
\r
218 strncpy( coalesce, coalesce_opt, sizeof(coalesce) - 1 );
\r