]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fce_util.c
Some cleanup in FCE coalesce. Use bitflags instead of strings.
[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 uint32_t coalesce = 0;\r
68 static struct fce_history fce_history_list[FCE_HISTORY_LEN];\r
69 \r
70 /****\r
71 * With coalesce we try to reduce the events over UDP, the eventlistener would throw these \r
72 * events away anyway.\r
73 * This works only, if the connected listener uses the events on a "per directory" base\r
74 * It is a very simple aproach, but saves a lot of events sent to listeners.\r
75 * Every "child element" event is ignored as long as its parent event is not older \r
76 * than MAX_COALESCE_TIME_MS ms. If large directory trees or large files are created or deleted, \r
77 * this probably will not work recursive, because the time to copy data will exceed this \r
78 * event timeout. \r
79\r
80 ****/\r
81 \r
82 static long get_ms_difftime (  struct timeval *tv1, struct timeval *tv2 )\r
83 {\r
84         unsigned long s = tv2->tv_sec - tv1->tv_sec;\r
85         long us = tv2->tv_usec - tv1->tv_usec;\r
86 \r
87         return s * 1000 + us/1000;\r
88 }\r
89 \r
90 /******************************************************************************\r
91  * Public functions follow\r
92  ******************************************************************************/\r
93 \r
94 void fce_initialize_history()\r
95 {\r
96         for (int i = 0; i < FCE_HISTORY_LEN; i++) {\r
97                 memset( &fce_history_list[i], 0, sizeof(fce_history_list[i]) );\r
98         }\r
99 }\r
100 \r
101 int fce_handle_coalescation( char *path, int is_dir, int mode )\r
102 {\r
103         /* These two are used to eval our next index in history */\r
104         /* the history is unsorted, speed should not be a problem, length is 10 */\r
105         unsigned long oldest_entry = (unsigned long )((long)-1);\r
106         int oldest_entry_idx = -1;\r
107         struct timeval tv;\r
108 \r
109         if (coalesce == 0)\r
110                 return FALSE;\r
111 \r
112         /* After a file creation *ALWAYS* a file modification is produced */\r
113         if ((mode == FCE_FILE_CREATE) && (coalesce & FCE_COALESCE_CREATE))\r
114         return TRUE;\r
115 \r
116         /* get timestamp */\r
117         gettimeofday(&tv, 0);\r
118 \r
119         /* Now detect events in the very near history */\r
120         for (int i = 0; i < FCE_HISTORY_LEN; i++) {\r
121                 struct fce_history *fh = &fce_history_list[i];\r
122 \r
123                 /* Not inited ? */\r
124                 if (fh->tv.tv_sec == 0) {\r
125                         /* we can use it for new elements */\r
126                         oldest_entry = 0;\r
127                         oldest_entry_idx = i;\r
128                         continue;\r
129                 }\r
130 \r
131                 /* Too old ? */\r
132                 if (get_ms_difftime( &fh->tv, &tv ) > MAX_COALESCE_TIME_MS) {\r
133                         /* Invalidate entry */\r
134                         fh->tv.tv_sec = 0;\r
135                         oldest_entry = 0;\r
136                         oldest_entry_idx = i;                   \r
137                         continue;\r
138                 }\r
139 \r
140 \r
141                 /* If we find a parent dir wich was created we are done */\r
142                 if ((coalesce & FCE_COALESCE_CREATE) && (fh->mode == FCE_DIR_CREATE)) {\r
143                         /* Parent dir ? */\r
144                         if (!strncmp(fh->path, path, strlen(fh->path)))\r
145                                 return TRUE;\r
146                 }\r
147 \r
148                 /* If we find a parent dir we should be DELETED we are done */\r
149                 if ((coalesce & FCE_COALESCE_DELETE)\r
150             && fh->is_dir\r
151             && (mode == FCE_FILE_DELETE || mode == FCE_DIR_DELETE)) {\r
152                         /* Parent dir ? */\r
153                         if (!strncmp(fh->path, path, strlen(fh->path)))\r
154                                 return TRUE;\r
155                 }\r
156 \r
157                 /* Detect oldest entry for next new entry */\r
158                 if (oldest_entry_idx == -1 || fh->tv.tv_sec < oldest_entry) {\r
159                         oldest_entry = fh->tv.tv_sec;\r
160                         oldest_entry_idx = i;\r
161                 }\r
162         }\r
163 \r
164         /* We have a new entry for the history, register it */\r
165         fce_history_list[oldest_entry_idx].tv = tv;\r
166         fce_history_list[oldest_entry_idx].mode = mode;\r
167         fce_history_list[oldest_entry_idx].is_dir = is_dir;\r
168         strncpy( fce_history_list[oldest_entry_idx].path, path, MAXPATHLEN);\r
169 \r
170         /* we have to handle this event */\r
171         return FALSE;\r
172 }\r
173 \r
174 /*\r
175  * Set event coalescation to reduce number of events sent over UDP \r
176  * all|delete|create\r
177  */\r
178 \r
179 int fce_set_coalesce(char *opt)\r
180 {\r
181     char *e;\r
182     char *p;\r
183     \r
184     if (opt == NULL)\r
185         return AFPERR_PARAM;\r
186 \r
187     e = strdup(opt);\r
188 \r
189     for (p = strtok(e, ","); p; p = strtok(NULL, ",")) {\r
190         if (strcmp(p, "all") == 0) {\r
191             coalesce = FCE_COALESCE_ALL;\r
192         } else if (strcmp(p, "delete") == 0) {\r
193             coalesce = FCE_COALESCE_DELETE;\r
194         } else if (strcmp(p, "create") == 0) {\r
195             coalesce = FCE_COALESCE_CREATE;\r
196         }\r
197     }\r
198 \r
199     free(e);\r
200 }\r
201 \r
202 \r
203 \r
204 \r