]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fce_util.c
69f6ea0dabfb9987c767eb6b8ff521bcdc3c1c9e
[netatalk.git] / etc / afpd / fce_util.c
1 /*
2  * Copyright (c) 2010 Mark Williams
3  *
4  * File change event API for netatalk
5  *
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.
15  *
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
18  * 
19  * All Rights Reserved.  See COPYRIGHT.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif /* HAVE_CONFIG_H */
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <time.h>
31 #include <stdbool.h>
32 #include <sys/param.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #include <netdb.h>
37
38 #include <atalk/adouble.h>
39 #include <atalk/vfs.h>
40 #include <atalk/logger.h>
41 #include <atalk/afp.h>
42 #include <atalk/util.h>
43 #include <atalk/cnid.h>
44 #include <atalk/unix.h>
45 #include <atalk/fce_api.h>
46 #include <atalk/globals.h>
47
48 #include "fork.h"
49 #include "file.h"
50 #include "directory.h"
51 #include "desktop.h"
52 #include "volume.h"
53
54 // ONLY USED IN THIS FILE
55 #include "fce_api_internal.h"
56
57 /* We store our connection data here */
58 static uint32_t coalesce = 0;
59 static struct fce_history fce_history_list[FCE_HISTORY_LEN];
60
61 /****
62 * With coalesce we try to reduce the events over UDP, the eventlistener would throw these 
63 * events away anyway.
64 * This works only, if the connected listener uses the events on a "per directory" base
65 * It is a very simple aproach, but saves a lot of events sent to listeners.
66 * Every "child element" event is ignored as long as its parent event is not older 
67 * than MAX_COALESCE_TIME_MS ms. If large directory trees or large files are created or deleted, 
68 * this probably will not work recursive, because the time to copy data will exceed this 
69 * event timeout. 
70
71 ****/
72
73 static long get_ms_difftime (  struct timeval *tv1, struct timeval *tv2 )
74 {
75         unsigned long s = tv2->tv_sec - tv1->tv_sec;
76         long us = tv2->tv_usec - tv1->tv_usec;
77
78         return s * 1000 + us/1000;
79 }
80
81 /******************************************************************************
82  * Public functions follow
83  ******************************************************************************/
84
85 void fce_initialize_history()
86 {
87         for (int i = 0; i < FCE_HISTORY_LEN; i++) {
88                 memset( &fce_history_list[i], 0, sizeof(fce_history_list[i]) );
89         }
90 }
91
92 bool fce_handle_coalescation(int event, const char *path, fce_obj_t type)
93 {
94         /* These two are used to eval our next index in history */
95         /* the history is unsorted, speed should not be a problem, length is 10 */
96         unsigned long oldest_entry = (unsigned long )((long)-1);
97         int oldest_entry_idx = -1;
98         struct timeval tv;
99
100         if (coalesce == 0)
101                 return false;
102
103         /* After a file creation *ALWAYS* a file modification is produced */
104         if ((event == FCE_FILE_CREATE) && (coalesce & FCE_COALESCE_CREATE))
105         return true;
106
107         /* get timestamp */
108         gettimeofday(&tv, 0);
109
110         /* Now detect events in the very near history */
111         for (int i = 0; i < FCE_HISTORY_LEN; i++) {
112                 struct fce_history *fh = &fce_history_list[i];
113
114                 /* Not inited ? */
115                 if (fh->fce_h_tv.tv_sec == 0) {
116                         /* we can use it for new elements */
117                         oldest_entry = 0;
118                         oldest_entry_idx = i;
119                         continue;
120                 }
121
122                 /* Too old ? */
123                 if (get_ms_difftime(&fh->fce_h_tv, &tv ) > MAX_COALESCE_TIME_MS) {
124                         /* Invalidate entry */
125                         fh->fce_h_tv.tv_sec = 0;
126                         oldest_entry = 0;
127                         oldest_entry_idx = i;                   
128                         continue;
129                 }
130
131
132                 /* If we find a parent dir wich was created we are done */
133                 if ((coalesce & FCE_COALESCE_CREATE) && (fh->fce_h_event == FCE_DIR_CREATE)) {
134                         /* Parent dir ? */
135                         if (!strncmp(fh->fce_h_path, path, strlen(fh->fce_h_path)))
136                                 return true;
137                 }
138
139                 /* If we find a parent dir we should be DELETED we are done */
140                 if ((coalesce & FCE_COALESCE_DELETE)
141             && fh->fce_h_type
142             && (event == FCE_FILE_DELETE || event == FCE_DIR_DELETE)) {
143                         /* Parent dir ? */
144                         if (!strncmp(fh->fce_h_path, path, strlen(fh->fce_h_path)))
145                                 return true;
146                 }
147
148                 /* Detect oldest entry for next new entry */
149                 if (oldest_entry_idx == -1 || fh->fce_h_tv.tv_sec < oldest_entry) {
150                         oldest_entry = fh->fce_h_tv.tv_sec;
151                         oldest_entry_idx = i;
152                 }
153         }
154
155         /* We have a new entry for the history, register it */
156         fce_history_list[oldest_entry_idx].fce_h_tv = tv;
157         fce_history_list[oldest_entry_idx].fce_h_event = event;
158         fce_history_list[oldest_entry_idx].fce_h_type = type;
159         strncpy(fce_history_list[oldest_entry_idx].fce_h_path, path, MAXPATHLEN);
160
161         /* we have to handle this event */
162         return false;
163 }
164
165 /*
166  * Set event coalescation to reduce number of events sent over UDP 
167  * all|delete|create
168  */
169
170 int fce_set_coalesce(const char *opt)
171 {
172     char *e;
173     char *p;
174     
175     if (opt == NULL)
176         return AFPERR_PARAM;
177
178     e = strdup(opt);
179
180     for (p = strtok(e, ","); p; p = strtok(NULL, ",")) {
181         if (strcmp(p, "all") == 0) {
182             coalesce = FCE_COALESCE_ALL;
183         } else if (strcmp(p, "delete") == 0) {
184             coalesce = FCE_COALESCE_DELETE;
185         } else if (strcmp(p, "create") == 0) {
186             coalesce = FCE_COALESCE_CREATE;
187         }
188     }
189
190     free(e);
191
192     return AFP_OK;
193 }
194
195
196
197