]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fce_util.c
Import FCE
[netatalk.git] / etc / afpd / fce_util.c
1 /*\r
2  * $Id: fce_api.c,v 0.01 2010-10-01 00:00:0 mw Exp $\r
3  *\r
4  * Copyright (c) 2010 Mark Williams\r
5  *\r
6  * File change event API for netatalk\r
7  *\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
17  *\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
20  * \r
21  * All Rights Reserved.  See COPYRIGHT.\r
22  */\r
23 \r
24 #ifdef HAVE_CONFIG_H\r
25 #include "config.h"\r
26 #endif /* HAVE_CONFIG_H */\r
27 \r
28 #include <stdio.h>\r
29 \r
30 #include <string.h>\r
31 #include <stdlib.h>\r
32 #include <errno.h>\r
33 #include <time.h>\r
34 \r
35 \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
40 #include <netdb.h>\r
41 \r
42 #include <netatalk/at.h>\r
43 \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 \r
52 #include "fork.h"\r
53 #include "file.h"\r
54 #include "globals.h"\r
55 #include "directory.h"\r
56 #include "desktop.h"\r
57 #include "volume.h"\r
58 \r
59 #include "fce_api.h"\r
60 \r
61 // ONLY USED IN THIS FILE\r
62 #include "fce_api_internal.h"\r
63 \r
64 #define FCE_TRUE 1\r
65 #define FCE_FALSE 0\r
66 \r
67 /* We store our connection data here */\r
68 static char coalesce[80] = {""};\r
69 static struct fce_history fce_history_list[FCE_HISTORY_LEN];\r
70 \r
71 \r
72 \r
73 \r
74 /****\r
75 * With coalesce we try to reduce the events over UDP, the eventlistener would throw these \r
76 * events away anyway.\r
77 * This works only, if the connected listener uses the events on a "per directory" base\r
78 * It is a very simple aproach, but saves a lot of events sent to listeners.\r
79 * Every "child element" event is ignored as long as its parent event is not older \r
80 * than MAX_COALESCE_TIME_MS ms. If large directory trees or large files are created or deleted, \r
81 * this probably will not work recursive, because the time to copy data will exceed this \r
82 * event timeout. \r
83\r
84 ****/\r
85 static int coalesce_none()\r
86 {\r
87         return coalesce[0] == 0;\r
88 }\r
89 static int coalesce_all()\r
90 {\r
91         return !strcmp( coalesce, "all" );\r
92 }\r
93 static int coalesce_create()\r
94 {\r
95         return !strcmp( coalesce, "create" ) || coalesce_all();\r
96 }\r
97 static int coalesce_delete()\r
98 {\r
99         return !strcmp( coalesce, "delete" ) || coalesce_all();\r
100 }\r
101 \r
102 void fce_initialize_history()\r
103 {\r
104         for (int i = 0; i < FCE_HISTORY_LEN; i++)\r
105         {\r
106                 memset( &fce_history_list[i], 0, sizeof(fce_history_list[i]) );\r
107         }\r
108 }\r
109 \r
110 static long get_ms_difftime (  struct timeval *tv1, struct timeval *tv2 )\r
111 {\r
112         unsigned long s = tv2->tv_sec - tv1->tv_sec;\r
113         long us = tv2->tv_usec - tv1->tv_usec;\r
114 \r
115         return s * 1000 + us/1000;\r
116 }\r
117 \r
118 int fce_handle_coalescation( char *path, int is_dir, int mode )\r
119 {\r
120         if (coalesce_none())\r
121                 return FALSE;\r
122 \r
123                 \r
124 \r
125         // First one:\r
126         // After a file creation *ALWAYS* a file modification is produced\r
127         if (mode == FCE_FILE_CREATE)\r
128         {\r
129                 if (coalesce_create())\r
130                 {\r
131                         return TRUE;\r
132                 }\r
133         }\r
134 \r
135         /* get timestamp */\r
136         struct timeval tv;\r
137         gettimeofday(&tv, 0);\r
138 \r
139 \r
140         /* These two are used to eval our next index in history */\r
141         /* the history is unsorted, speed should not be a problem, length is 10 */\r
142         unsigned long oldest_entry = (unsigned long )((long)-1);\r
143         int oldest_entry_idx = -1;\r
144 \r
145         /* Now detect events in the very near history */\r
146         for (int i = 0; i < FCE_HISTORY_LEN; i++)\r
147         {\r
148                 struct fce_history *fh = &fce_history_list[i];\r
149 \r
150                 //* Not inited ? */\r
151                 if (fh->tv.tv_sec == 0)\r
152                 {\r
153                         /* we can use it for new elements */\r
154                         oldest_entry = 0;\r
155                         oldest_entry_idx = i;\r
156                         continue;\r
157                 }\r
158 \r
159                 //* Too old ? */\r
160                 if (get_ms_difftime( &fh->tv, &tv ) > MAX_COALESCE_TIME_MS)\r
161                 {\r
162                         /* Invalidate entry */\r
163                         fh->tv.tv_sec = 0;\r
164 \r
165                         oldest_entry = 0;\r
166                         oldest_entry_idx = i;                   \r
167                         continue;\r
168                 }\r
169 \r
170 \r
171                 /* If we find a parent dir wich was created we are done */\r
172                 if (coalesce_create() && fh->mode == FCE_DIR_CREATE)\r
173                 {\r
174                         //* Parent dir ? */\r
175                         if (!strncmp( fh->path, path, strlen( fh->path ) ) )\r
176                         {\r
177                                 return TRUE;\r
178                         }\r
179                 }\r
180 \r
181                 /* If we find a parent dir we should be DELETED we are done */\r
182                 if (coalesce_delete() && fh->is_dir && (mode == FCE_FILE_DELETE || mode == FCE_DIR_DELETE))\r
183                 {\r
184                         //* Parent dir ? */\r
185                         if (!strncmp( fh->path, path, strlen( fh->path ) ) )\r
186                         {\r
187                                 return TRUE;\r
188                         }\r
189                 }\r
190 \r
191                 //* Detect oldest entry for next new entry */\r
192                 if (oldest_entry_idx == -1 || fh->tv.tv_sec < oldest_entry)\r
193                 {\r
194                         oldest_entry = fh->tv.tv_sec;\r
195                         oldest_entry_idx = i;\r
196                 }\r
197         }\r
198 \r
199         /* We have a new entry for the history, register it */\r
200         fce_history_list[oldest_entry_idx].tv = tv;\r
201         fce_history_list[oldest_entry_idx].mode = mode;\r
202         fce_history_list[oldest_entry_idx].is_dir = is_dir;\r
203         strncpy( fce_history_list[oldest_entry_idx].path, path, FCE_MAX_PATH_LEN );\r
204 \r
205         /* we have to handle this event */\r
206         return FALSE;\r
207 \r
208 }\r
209 \r
210 \r
211 \r
212 /*\r
213  *\r
214  * Set event coalescation to reduce number of events sent over UDP \r
215  * all|delete|create\r
216  *\r
217  *\r
218  * */\r
219 \r
220 int fce_set_coalesce( char *coalesce_opt )\r
221 {\r
222         strncpy( coalesce, coalesce_opt, sizeof(coalesce) - 1 ); \r
223 }\r
224 \r
225 \r
226 \r
227 \r