]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fce_util.c
Merge sf.net
[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 #include <atalk/fce_api.h>\r
52 #include <atalk/globals.h>\r
53 \r
54 #include "fork.h"\r
55 #include "file.h"\r
56 #include "directory.h"\r
57 #include "desktop.h"\r
58 #include "volume.h"\r
59 \r
60 // ONLY USED IN THIS FILE\r
61 #include "fce_api_internal.h"\r
62 \r
63 #define FCE_TRUE 1\r
64 #define FCE_FALSE 0\r
65 \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
69 \r
70 \r
71 \r
72 \r
73 /****\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
81 * event timeout. \r
82\r
83 ****/\r
84 static int coalesce_none()\r
85 {\r
86         return coalesce[0] == 0;\r
87 }\r
88 static int coalesce_all()\r
89 {\r
90         return !strcmp( coalesce, "all" );\r
91 }\r
92 static int coalesce_create()\r
93 {\r
94         return !strcmp( coalesce, "create" ) || coalesce_all();\r
95 }\r
96 static int coalesce_delete()\r
97 {\r
98         return !strcmp( coalesce, "delete" ) || coalesce_all();\r
99 }\r
100 \r
101 void fce_initialize_history()\r
102 {\r
103         for (int i = 0; i < FCE_HISTORY_LEN; i++)\r
104         {\r
105                 memset( &fce_history_list[i], 0, sizeof(fce_history_list[i]) );\r
106         }\r
107 }\r
108 \r
109 static long get_ms_difftime (  struct timeval *tv1, struct timeval *tv2 )\r
110 {\r
111         unsigned long s = tv2->tv_sec - tv1->tv_sec;\r
112         long us = tv2->tv_usec - tv1->tv_usec;\r
113 \r
114         return s * 1000 + us/1000;\r
115 }\r
116 \r
117 int fce_handle_coalescation( char *path, int is_dir, int mode )\r
118 {\r
119         if (coalesce_none())\r
120                 return FALSE;\r
121 \r
122                 \r
123 \r
124         // First one:\r
125         // After a file creation *ALWAYS* a file modification is produced\r
126         if (mode == FCE_FILE_CREATE)\r
127         {\r
128                 if (coalesce_create())\r
129                 {\r
130                         return TRUE;\r
131                 }\r
132         }\r
133 \r
134         /* get timestamp */\r
135         struct timeval tv;\r
136         gettimeofday(&tv, 0);\r
137 \r
138 \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
143 \r
144         /* Now detect events in the very near history */\r
145         for (int i = 0; i < FCE_HISTORY_LEN; i++)\r
146         {\r
147                 struct fce_history *fh = &fce_history_list[i];\r
148 \r
149                 //* Not inited ? */\r
150                 if (fh->tv.tv_sec == 0)\r
151                 {\r
152                         /* we can use it for new elements */\r
153                         oldest_entry = 0;\r
154                         oldest_entry_idx = i;\r
155                         continue;\r
156                 }\r
157 \r
158                 //* Too old ? */\r
159                 if (get_ms_difftime( &fh->tv, &tv ) > MAX_COALESCE_TIME_MS)\r
160                 {\r
161                         /* Invalidate entry */\r
162                         fh->tv.tv_sec = 0;\r
163 \r
164                         oldest_entry = 0;\r
165                         oldest_entry_idx = i;                   \r
166                         continue;\r
167                 }\r
168 \r
169 \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
172                 {\r
173                         //* Parent dir ? */\r
174                         if (!strncmp( fh->path, path, strlen( fh->path ) ) )\r
175                         {\r
176                                 return TRUE;\r
177                         }\r
178                 }\r
179 \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
182                 {\r
183                         //* Parent dir ? */\r
184                         if (!strncmp( fh->path, path, strlen( fh->path ) ) )\r
185                         {\r
186                                 return TRUE;\r
187                         }\r
188                 }\r
189 \r
190                 //* Detect oldest entry for next new entry */\r
191                 if (oldest_entry_idx == -1 || fh->tv.tv_sec < oldest_entry)\r
192                 {\r
193                         oldest_entry = fh->tv.tv_sec;\r
194                         oldest_entry_idx = i;\r
195                 }\r
196         }\r
197 \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
203 \r
204         /* we have to handle this event */\r
205         return FALSE;\r
206 }\r
207 \r
208 /*\r
209  *\r
210  * Set event coalescation to reduce number of events sent over UDP \r
211  * all|delete|create\r
212  *\r
213  *\r
214  * */\r
215 \r
216 int fce_set_coalesce( char *coalesce_opt )\r
217 {\r
218         strncpy( coalesce, coalesce_opt, sizeof(coalesce) - 1 ); \r
219 }\r
220 \r
221 \r
222 \r
223 \r