]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/fce_util.c
fce: FCE version 2 with new event types and new config options
[netatalk.git] / etc / afpd / fce_util.c
old mode 100755 (executable)
new mode 100644 (file)
index c1377ee..8586a40
-/*\r
- * $Id: fce_api.c,v 0.01 2010-10-01 00:00:0 mw Exp $\r
- *\r
- * Copyright (c) 2010 Mark Williams\r
- *\r
- * File change event API for netatalk\r
- *\r
- * for every detected filesystem change a UDP packet is sent to an arbitrary list\r
- * of listeners. Each packet contains unix path of modified filesystem element,\r
- * event reason, and a consecutive event id (32 bit). Technically we are UDP client and are sending\r
- * out packets synchronuosly as they are created by the afp functions. This should not affect\r
- * performance measurably. The only delaying calls occur during initialization, if we have to\r
- * resolve non-IP hostnames to IP. All numeric data inside the packet is network byte order, so use\r
- * ntohs / ntohl to resolve length and event id. Ideally a listener receives every packet with\r
- * no gaps in event ids, starting with event id 1 and mode FCE_CONN_START followed by\r
- * data events from id 2 up to 0xFFFFFFFF, followed by 0 to 0xFFFFFFFF and so on.\r
- *\r
- * A gap or not starting with 1 mode FCE_CONN_START or receiving mode FCE_CONN_BROKEN means that\r
- * the listener has lost at least one filesystem event\r
- * \r
- * All Rights Reserved.  See COPYRIGHT.\r
- */\r
-\r
-#ifdef HAVE_CONFIG_H\r
-#include "config.h"\r
-#endif /* HAVE_CONFIG_H */\r
-\r
-#include <stdio.h>\r
-\r
-#include <string.h>\r
-#include <stdlib.h>\r
-#include <errno.h>\r
-#include <time.h>\r
-\r
-\r
-#include <sys/param.h>\r
-#include <sys/socket.h>\r
-#include <netinet/in.h>\r
-#include <arpa/inet.h>\r
-#include <netdb.h>\r
-\r
-#include <netatalk/at.h>\r
-\r
-#include <atalk/adouble.h>\r
-#include <atalk/vfs.h>\r
-#include <atalk/logger.h>\r
-#include <atalk/afp.h>\r
-#include <atalk/util.h>\r
-#include <atalk/cnid.h>\r
-#include <atalk/unix.h>\r
-#include <atalk/fce_api.h>\r
-\r
-#include "fork.h"\r
-#include "file.h"\r
-#include "globals.h"\r
-#include "directory.h"\r
-#include "desktop.h"\r
-#include "volume.h"\r
-\r
-// ONLY USED IN THIS FILE\r
-#include "fce_api_internal.h"\r
-\r
-#define FCE_TRUE 1\r
-#define FCE_FALSE 0\r
-\r
-/* We store our connection data here */\r
-static char coalesce[80] = {""};\r
-static struct fce_history fce_history_list[FCE_HISTORY_LEN];\r
-\r
-\r
-\r
-\r
-/****\r
-* With coalesce we try to reduce the events over UDP, the eventlistener would throw these \r
-* events away anyway.\r
-* This works only, if the connected listener uses the events on a "per directory" base\r
-* It is a very simple aproach, but saves a lot of events sent to listeners.\r
-* Every "child element" event is ignored as long as its parent event is not older \r
-* than MAX_COALESCE_TIME_MS ms. If large directory trees or large files are created or deleted, \r
-* this probably will not work recursive, because the time to copy data will exceed this \r
-* event timeout. \r
-* \r
-****/\r
-static int coalesce_none()\r
-{\r
-       return coalesce[0] == 0;\r
-}\r
-static int coalesce_all()\r
-{\r
-       return !strcmp( coalesce, "all" );\r
-}\r
-static int coalesce_create()\r
-{\r
-       return !strcmp( coalesce, "create" ) || coalesce_all();\r
-}\r
-static int coalesce_delete()\r
-{\r
-       return !strcmp( coalesce, "delete" ) || coalesce_all();\r
-}\r
-\r
-void fce_initialize_history()\r
-{\r
-       for (int i = 0; i < FCE_HISTORY_LEN; i++)\r
-       {\r
-               memset( &fce_history_list[i], 0, sizeof(fce_history_list[i]) );\r
-       }\r
-}\r
-\r
-static long get_ms_difftime (  struct timeval *tv1, struct timeval *tv2 )\r
-{\r
-       unsigned long s = tv2->tv_sec - tv1->tv_sec;\r
-       long us = tv2->tv_usec - tv1->tv_usec;\r
-\r
-       return s * 1000 + us/1000;\r
-}\r
-\r
-int fce_handle_coalescation( char *path, int is_dir, int mode )\r
-{\r
-       if (coalesce_none())\r
-               return FALSE;\r
-\r
-               \r
-\r
-       // First one:\r
-       // After a file creation *ALWAYS* a file modification is produced\r
-       if (mode == FCE_FILE_CREATE)\r
-       {\r
-               if (coalesce_create())\r
-               {\r
-                       return TRUE;\r
-               }\r
-       }\r
-\r
-       /* get timestamp */\r
-       struct timeval tv;\r
-       gettimeofday(&tv, 0);\r
-\r
-\r
-       /* These two are used to eval our next index in history */\r
-       /* the history is unsorted, speed should not be a problem, length is 10 */\r
-       unsigned long oldest_entry = (unsigned long )((long)-1);\r
-       int oldest_entry_idx = -1;\r
-\r
-       /* Now detect events in the very near history */\r
-       for (int i = 0; i < FCE_HISTORY_LEN; i++)\r
-       {\r
-               struct fce_history *fh = &fce_history_list[i];\r
-\r
-               //* Not inited ? */\r
-               if (fh->tv.tv_sec == 0)\r
-               {\r
-                       /* we can use it for new elements */\r
-                       oldest_entry = 0;\r
-                       oldest_entry_idx = i;\r
-                       continue;\r
-               }\r
-\r
-               //* Too old ? */\r
-               if (get_ms_difftime( &fh->tv, &tv ) > MAX_COALESCE_TIME_MS)\r
-               {\r
-                       /* Invalidate entry */\r
-                       fh->tv.tv_sec = 0;\r
-\r
-                       oldest_entry = 0;\r
-                       oldest_entry_idx = i;                   \r
-                       continue;\r
-               }\r
-\r
-\r
-               /* If we find a parent dir wich was created we are done */\r
-               if (coalesce_create() && fh->mode == FCE_DIR_CREATE)\r
-               {\r
-                       //* Parent dir ? */\r
-                       if (!strncmp( fh->path, path, strlen( fh->path ) ) )\r
-                       {\r
-                               return TRUE;\r
-                       }\r
-               }\r
-\r
-               /* If we find a parent dir we should be DELETED we are done */\r
-               if (coalesce_delete() && fh->is_dir && (mode == FCE_FILE_DELETE || mode == FCE_DIR_DELETE))\r
-               {\r
-                       //* Parent dir ? */\r
-                       if (!strncmp( fh->path, path, strlen( fh->path ) ) )\r
-                       {\r
-                               return TRUE;\r
-                       }\r
-               }\r
-\r
-               //* Detect oldest entry for next new entry */\r
-               if (oldest_entry_idx == -1 || fh->tv.tv_sec < oldest_entry)\r
-               {\r
-                       oldest_entry = fh->tv.tv_sec;\r
-                       oldest_entry_idx = i;\r
-               }\r
-       }\r
-\r
-       /* We have a new entry for the history, register it */\r
-       fce_history_list[oldest_entry_idx].tv = tv;\r
-       fce_history_list[oldest_entry_idx].mode = mode;\r
-       fce_history_list[oldest_entry_idx].is_dir = is_dir;\r
-       strncpy( fce_history_list[oldest_entry_idx].path, path, FCE_MAX_PATH_LEN );\r
-\r
-       /* we have to handle this event */\r
-       return FALSE;\r
-\r
-}\r
-\r
-\r
-\r
-/*\r
- *\r
- * Set event coalescation to reduce number of events sent over UDP \r
- * all|delete|create\r
- *\r
- *\r
- * */\r
-\r
-int fce_set_coalesce( char *coalesce_opt )\r
-{\r
-       strncpy( coalesce, coalesce_opt, sizeof(coalesce) - 1 ); \r
-}\r
-\r
-\r
-\r
-\r
+/*
+ * Copyright (c) 2010 Mark Williams
+ *
+ * File change event API for netatalk
+ *
+ * for every detected filesystem change a UDP packet is sent to an arbitrary list
+ * of listeners. Each packet contains unix path of modified filesystem element,
+ * event reason, and a consecutive event id (32 bit). Technically we are UDP client and are sending
+ * out packets synchronuosly as they are created by the afp functions. This should not affect
+ * performance measurably. The only delaying calls occur during initialization, if we have to
+ * resolve non-IP hostnames to IP. All numeric data inside the packet is network byte order, so use
+ * ntohs / ntohl to resolve length and event id. Ideally a listener receives every packet with
+ * no gaps in event ids, starting with event id 1 and mode FCE_CONN_START followed by
+ * data events from id 2 up to 0xFFFFFFFF, followed by 0 to 0xFFFFFFFF and so on.
+ *
+ * A gap or not starting with 1 mode FCE_CONN_START or receiving mode FCE_CONN_BROKEN means that
+ * the listener has lost at least one filesystem event
+ * 
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <stdbool.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
+#include <atalk/logger.h>
+#include <atalk/afp.h>
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/unix.h>
+#include <atalk/fce_api.h>
+#include <atalk/globals.h>
+
+#include "fork.h"
+#include "file.h"
+#include "directory.h"
+#include "desktop.h"
+#include "volume.h"
+
+// ONLY USED IN THIS FILE
+#include "fce_api_internal.h"
+
+/* We store our connection data here */
+static uint32_t coalesce = 0;
+static struct fce_history fce_history_list[FCE_HISTORY_LEN];
+
+/****
+* With coalesce we try to reduce the events over UDP, the eventlistener would throw these 
+* events away anyway.
+* This works only, if the connected listener uses the events on a "per directory" base
+* It is a very simple aproach, but saves a lot of events sent to listeners.
+* Every "child element" event is ignored as long as its parent event is not older 
+* than MAX_COALESCE_TIME_MS ms. If large directory trees or large files are created or deleted, 
+* this probably will not work recursive, because the time to copy data will exceed this 
+* event timeout. 
+* 
+****/
+
+static long get_ms_difftime (  struct timeval *tv1, struct timeval *tv2 )
+{
+       unsigned long s = tv2->tv_sec - tv1->tv_sec;
+       long us = tv2->tv_usec - tv1->tv_usec;
+
+       return s * 1000 + us/1000;
+}
+
+/******************************************************************************
+ * Public functions follow
+ ******************************************************************************/
+
+void fce_initialize_history()
+{
+       for (int i = 0; i < FCE_HISTORY_LEN; i++) {
+               memset( &fce_history_list[i], 0, sizeof(fce_history_list[i]) );
+       }
+}
+
+bool fce_handle_coalescation(int event, const char *path)
+{
+       /* These two are used to eval our next index in history */
+       /* the history is unsorted, speed should not be a problem, length is 10 */
+       unsigned long oldest_entry = (unsigned long )((long)-1);
+       int oldest_entry_idx = -1;
+       struct timeval tv;
+
+       if (coalesce == 0)
+               return false;
+
+       /* After a file creation *ALWAYS* a file modification is produced */
+       if ((event == FCE_FILE_CREATE) && (coalesce & FCE_COALESCE_CREATE))
+        return true;
+
+       /* get timestamp */
+       gettimeofday(&tv, 0);
+
+       /* Now detect events in the very near history */
+       for (int i = 0; i < FCE_HISTORY_LEN; i++) {
+               struct fce_history *fh = &fce_history_list[i];
+
+               /* Not inited ? */
+               if (fh->fce_h_tv.tv_sec == 0) {
+                       /* we can use it for new elements */
+                       oldest_entry = 0;
+                       oldest_entry_idx = i;
+                       continue;
+               }
+
+               /* Too old ? */
+               if (get_ms_difftime(&fh->fce_h_tv, &tv ) > MAX_COALESCE_TIME_MS) {
+                       /* Invalidate entry */
+                       fh->fce_h_tv.tv_sec = 0;
+                       oldest_entry = 0;
+                       oldest_entry_idx = i;                   
+                       continue;
+               }
+
+
+               /* If we find a parent dir wich was created we are done */
+               if ((coalesce & FCE_COALESCE_CREATE) && (fh->fce_h_event == FCE_DIR_CREATE)) {
+                       /* Parent dir ? */
+                       if (!strncmp(fh->fce_h_path, path, strlen(fh->fce_h_path)))
+                               return true;
+               }
+
+               /* If we find a parent dir we should be DELETED we are done */
+               if ((coalesce & FCE_COALESCE_DELETE)
+            && (event == FCE_FILE_DELETE || event == FCE_DIR_DELETE)) {
+                       /* Parent dir ? */
+                       if (!strncmp(fh->fce_h_path, path, strlen(fh->fce_h_path)))
+                               return true;
+               }
+
+               /* Detect oldest entry for next new entry */
+               if (oldest_entry_idx == -1 || fh->fce_h_tv.tv_sec < oldest_entry) {
+                       oldest_entry = fh->fce_h_tv.tv_sec;
+                       oldest_entry_idx = i;
+               }
+       }
+
+       /* We have a new entry for the history, register it */
+       fce_history_list[oldest_entry_idx].fce_h_tv = tv;
+       fce_history_list[oldest_entry_idx].fce_h_event = event;
+    strncpy(fce_history_list[oldest_entry_idx].fce_h_path, path, MAXPATHLEN);
+
+       /* we have to handle this event */
+       return false;
+}
+
+/*
+ * Set event coalescation to reduce number of events sent over UDP 
+ * all|delete|create
+ */
+
+int fce_set_coalesce(const char *opt)
+{
+    char *e;
+    char *p;
+    
+    if (opt == NULL)
+        return AFPERR_PARAM;
+
+    e = strdup(opt);
+
+    for (p = strtok(e, ","); p; p = strtok(NULL, ",")) {
+        if (strcmp(p, "all") == 0) {
+            coalesce = FCE_COALESCE_ALL;
+        } else if (strcmp(p, "delete") == 0) {
+            coalesce = FCE_COALESCE_DELETE;
+        } else if (strcmp(p, "create") == 0) {
+            coalesce = FCE_COALESCE_CREATE;
+        }
+    }
+
+    free(e);
+
+    return AFP_OK;
+}
+
+
+
+