]> arthur.barton.de Git - netatalk.git/commitdiff
Merge sf/master
authorFrank Lahm <franklahm@googlemail.com>
Tue, 31 May 2011 13:10:58 +0000 (15:10 +0200)
committerFrank Lahm <franklahm@googlemail.com>
Tue, 31 May 2011 13:10:58 +0000 (15:10 +0200)
21 files changed:
NEWS
bin/misc/.gitignore
bin/misc/Makefile.am
bin/misc/fce.c [new file with mode: 0644]
etc/afpd/.gitignore
etc/afpd/Makefile.am
etc/afpd/afp_options.c
etc/afpd/directory.c
etc/afpd/fce_api.c [new file with mode: 0755]
etc/afpd/fce_api_internal.h [new file with mode: 0755]
etc/afpd/fce_util.c [new file with mode: 0755]
etc/afpd/file.c
etc/afpd/filedir.c
etc/afpd/fork.c
etc/afpd/fork.h
etc/afpd/ofork.c
etc/afpd/volume.c
include/atalk/Makefile.am
include/atalk/fce_api.h [new file with mode: 0755]
include/atalk/util.h
man/man5/afpd.conf.5.tmpl

diff --git a/NEWS b/NEWS
index 3f4cf570d7a4b9102271f01e792019cbe9b44bbf..fa81acd9da9ba6d08a0452f745bc4a4ba5907b13 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ Changes in 2.2
 
 * NEW: afpd: new volume option "nonetids"
 * NEW: afpd: ACL access check caching
+* NEW: afpd: FCE event notifications
 * UPD: Support for Berkeley DB 5.1
 * UPD: case-conversion is based on Unicode 6.0.0
 * UPD: cnid_metad: allow up to 4096 volumes
index 2bff10ed3a1d944593ad9ff8cea65d8cbecbe84d..84dddb390fcef45d5bc5b67288146b947a67fcce 100644 (file)
@@ -2,6 +2,7 @@ Makefile
 Makefile.in
 netacnv
 afpldaptest
+fce
 logger_test
 .deps
 .libs
index 04a53e23e8b72fa1c59ff0af9944890f9a6188af..426dc3179f25ad99371bdff4e0e341e0a3cc6386 100644 (file)
@@ -3,7 +3,7 @@
 pkgconfdir = @PKGCONFDIR@
 bin_PROGRAMS =
 
-noinst_PROGRAMS = netacnv logger_test
+noinst_PROGRAMS = netacnv logger_test fce
 
 netacnv_SOURCES = netacnv.c
 netacnv_LDADD = $(top_builddir)/libatalk/libatalk.la
@@ -11,6 +11,10 @@ netacnv_LDADD = $(top_builddir)/libatalk/libatalk.la
 logger_test_SOURCES = logger_test.c
 logger_test_LDADD = $(top_builddir)/libatalk/libatalk.la
 
+fce_SOOURCE = fce.c
+fce_LDADD = $(top_builddir)/libatalk/libatalk.la
+fce_CFLAGS = -I$(top_srcdir)/include
+
 bin_PROGRAMS += afpldaptest
 afpldaptest_SOURCES = uuidtest.c
 afpldaptest_CFLAGS = -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\"
diff --git a/bin/misc/fce.c b/bin/misc/fce.c
new file mode 100644 (file)
index 0000000..76ca0a8
--- /dev/null
@@ -0,0 +1,164 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include <sys/param.h>
+#include <sys/cdefs.h>
+
+#include <atalk/fce_api.h>
+#include <atalk/util.h>
+
+#define MAXBUFLEN 1024
+
+static char *fce_ev_names[] = {
+    "",
+    "FCE_FILE_MODIFY",
+    "FCE_FILE_DELETE",
+    "FCE_DIR_DELETE",
+    "FCE_FILE_CREATE",
+    "FCE_DIR_CREATE",
+    "FCE_TM_SIZE"
+};
+
+// get sockaddr, IPv4 or IPv6:
+static void *get_in_addr(struct sockaddr *sa)
+{
+    if (sa->sa_family == AF_INET) {
+        return &(((struct sockaddr_in*)sa)->sin_addr);
+    }
+
+    return &(((struct sockaddr_in6*)sa)->sin6_addr);
+}
+
+static int unpack_fce_packet(unsigned char *buf, struct fce_packet *packet)
+{
+    unsigned char *p = buf;
+
+    memcpy(&packet->magic[0], p, sizeof(packet->magic));
+    p += sizeof(packet->magic);
+
+    packet->version = *p;
+    p++;
+
+    packet->mode = *p;
+    p++;
+
+    memcpy(&packet->event_id, p, sizeof(packet->event_id));
+    p += sizeof(packet->event_id);
+    packet->event_id = ntohl(packet->event_id);
+
+    memcpy(&packet->datalen, p, sizeof(packet->datalen));
+    p += sizeof(packet->datalen);
+    packet->datalen = ntohs(packet->datalen);
+
+    memcpy(&packet->data[0], p, packet->datalen);
+    p += packet->datalen;
+
+    return 0;
+}
+
+int main(void)
+{
+    int sockfd;
+    struct addrinfo hints, *servinfo, *p;
+    int rv;
+    int numbytes;
+    struct sockaddr_storage their_addr;
+    char buf[MAXBUFLEN];
+    socklen_t addr_len;
+    char s[INET6_ADDRSTRLEN];
+    uint64_t tmsize;
+
+    memset(&hints, 0, sizeof hints);
+    hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
+    hints.ai_socktype = SOCK_DGRAM;
+
+    if ((rv = getaddrinfo(NULL, FCE_DEFAULT_PORT_STRING, &hints, &servinfo)) != 0) {
+        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
+        return 1;
+    }
+
+    // loop through all the results and bind to the first we can
+    for(p = servinfo; p != NULL; p = p->ai_next) {
+        if ((sockfd = socket(p->ai_family, p->ai_socktype,
+                             p->ai_protocol)) == -1) {
+            perror("listener: socket");
+            continue;
+        }
+
+        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
+            close(sockfd);
+            perror("listener: bind");
+            continue;
+        }
+
+        break;
+    }
+
+    if (p == NULL) {
+        fprintf(stderr, "listener: failed to bind socket\n");
+        return 2;
+    }
+
+    freeaddrinfo(servinfo);
+
+    printf("listener: waiting to recvfrom...\n");
+
+    addr_len = sizeof their_addr;
+
+    struct fce_packet packet;
+    while (1) {
+        if ((numbytes = recvfrom(sockfd,
+                                 buf,
+                                 MAXBUFLEN - 1,
+                                 0,
+                                 (struct sockaddr *)&their_addr,
+                                 &addr_len)) == -1) {
+            perror("recvfrom");
+            exit(1);
+        }
+
+        unpack_fce_packet(buf, &packet);
+
+        if (memcmp(packet.magic, FCE_PACKET_MAGIC, sizeof(packet.magic)) == 0) {
+
+            switch (packet.mode) {
+            case FCE_TM_SIZE:
+                memcpy(&tmsize, packet.data, sizeof(uint64_t));
+                tmsize = ntoh64(tmsize);
+                printf("ID: %" PRIu32 ", Event: %s, Volume: %s, TM used size: %" PRIu64 " \n",
+                       packet.event_id, fce_ev_names[packet.mode], packet.data + sizeof(uint64_t), tmsize);
+                break;
+
+            case FCE_CONN_START:
+                printf("FCE Start\n");
+                break;
+
+            case FCE_CONN_BROKEN:
+                printf("Broken FCE connection\n");
+                break;
+
+            default:
+                printf("ID: %" PRIu32 ", Event: %s, Path: %s\n",
+                       packet.event_id, fce_ev_names[packet.mode], packet.data);
+                break;
+            }
+        }
+    }
+
+    close(sockfd);
+    return 0;
+}
index add16d71cb657ed407fe0f06f7fdbbe8c83ba9eb..5d0c03fa85cb4a199ed3beead2cdc679b820512b 100644 (file)
@@ -1,6 +1,7 @@
 Makefile
 Makefile.in
 afpd
+fce
 hash
 test_parse_mtab
 .deps
index 5dd97e438638f52877994e32fb35d54ed6c848b3..bcc8a17d460d32511dd029c742c472bcde882d8a 100644 (file)
@@ -3,7 +3,7 @@
 pkgconfdir = @PKGCONFDIR@
 
 sbin_PROGRAMS = afpd
-noinst_PROGRAMS = hash
+noinst_PROGRAMS = hash fce
 
 afpd_SOURCES = \
        afp_asp.c \
@@ -23,6 +23,8 @@ afpd_SOURCES = \
        directory.c \
        enumerate.c \
        extattrs.c \
+       fce_api.c \
+       fce_util.c \
        file.c \
        filedir.c \
        fork.c \
@@ -75,3 +77,7 @@ noinst_HEADERS = auth.h afp_config.h desktop.h directory.h file.h \
 
 hash_SOURCES = hash.c
 hash_CFLAGS = -DKAZLIB_TEST_MAIN -I$(top_srcdir)/include
+
+fce_SOURCES = fce_api.c fce_util.c
+fce_CFLAGS = -DFCE_TEST_MAIN -I$(top_srcdir)/include
+fce_LDADD = $(top_builddir)/libatalk/libatalk.la
index 5cb205dbe7199eb2d6966533490d4538af8ee75e..22cb2d3389be6e008439be6a3649137e3c9167ff 100644 (file)
@@ -35,6 +35,7 @@
 #include <atalk/util.h>
 #include <atalk/compat.h>
 #include <atalk/globals.h>
+#include <atalk/fce_api.h>
 
 #include "status.h"
 #include "auth.h"
@@ -481,6 +482,19 @@ int afp_options_parseline(char *buf, struct afp_options *options)
     if ((c = getoption(buf, "-tcprcvbuf")))
         options->tcp_rcvbuf = atoi(c);
 
+       if ((c = getoption(buf, "-fcelistener"))) {
+               LOG(log_note, logtype_afpd, "Adding fce listener \"%s\"", c);
+               fce_add_udp_socket(c);
+       }
+       if ((c = getoption(buf, "-fcecoalesce"))) {
+               LOG(log_note, logtype_afpd, "Fce coalesce: %s", c);
+               fce_set_coalesce(c);
+       }
+       if ((c = getoption(buf, "-fceevents"))) {
+               LOG(log_note, logtype_afpd, "Fce events: %s", c);
+               fce_set_events(c);
+       }
+
     return 1;
 }
 
index fc0c56321ec14c39b0b7f3b09cc4740af689e457..fe628e0c61b9fc692420bed1c87171b7d9b2f2cf 100644 (file)
@@ -30,6 +30,7 @@
 #include <atalk/bstradd.h>
 #include <atalk/errchk.h>
 #include <atalk/globals.h>
+#include <atalk/fce_api.h>
 
 #include "directory.h"
 #include "dircache.h"
@@ -2275,6 +2276,8 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
     ad_setname(&ad, s_path->m_name);
     ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
 
+    fce_register_new_dir(s_path);
+
     ad_flush( &ad);
     ad_close_metadata( &ad);
 
diff --git a/etc/afpd/fce_api.c b/etc/afpd/fce_api.c
new file mode 100755 (executable)
index 0000000..2b00142
--- /dev/null
@@ -0,0 +1,646 @@
+/*\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 struct udp_entry udp_socket_list[FCE_MAX_UDP_SOCKS];\r
+static int udp_sockets = 0;\r
+static int udp_initialized = FCE_FALSE;\r
+static unsigned long fce_ev_enabled =\r
+    (1 << FCE_FILE_MODIFY) |\r
+    (1 << FCE_FILE_DELETE) |\r
+    (1 << FCE_DIR_DELETE) |\r
+    (1 << FCE_FILE_CREATE) |\r
+    (1 << FCE_DIR_CREATE);\r
+\r
+static uint64_t tm_used;          /* used for passing to event handler */\r
+#define MAXIOBUF 1024\r
+static char iobuf[MAXIOBUF];\r
+static const char *skip_files[] = \r
+{\r
+       ".DS_Store",\r
+       NULL\r
+};\r
+\r
+/*\r
+ *\r
+ * Initialize network structs for any listeners\r
+ * We dont give return code because all errors are handled internally (I hope..)\r
+ *\r
+ * */\r
+void fce_init_udp()\r
+{\r
+    int rv;\r
+    struct addrinfo hints, *servinfo, *p;\r
+\r
+    if (udp_initialized == FCE_TRUE)\r
+        return;\r
+\r
+    memset(&hints, 0, sizeof hints);\r
+    hints.ai_family = AF_UNSPEC;\r
+    hints.ai_socktype = SOCK_DGRAM;\r
+\r
+    for (int i = 0; i < udp_sockets; i++) {\r
+        struct udp_entry *udp_entry = udp_socket_list + i;\r
+\r
+        /* Close any pending sockets */\r
+        if (udp_entry->sock != -1)\r
+            close(udp_entry->sock);\r
+\r
+        if ((rv = getaddrinfo(udp_entry->addr, udp_entry->port, &hints, &servinfo)) != 0) {\r
+            LOG(log_error, logtype_afpd, "fce_init_udp: getaddrinfo(%s:%s): %s",\r
+                udp_entry->addr, udp_entry->port, gai_strerror(rv));\r
+            continue;\r
+        }\r
+\r
+        /* loop through all the results and make a socket */\r
+        for (p = servinfo; p != NULL; p = p->ai_next) {\r
+            if ((udp_entry->sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {\r
+                LOG(log_error, logtype_afpd, "fce_init_udp: socket(%s:%s): %s",\r
+                    udp_entry->addr, udp_entry->port, strerror(errno));\r
+                continue;\r
+            }\r
+            break;\r
+        }\r
+\r
+        if (p == NULL) {\r
+            LOG(log_error, logtype_afpd, "fce_init_udp: no socket for %s:%s",\r
+                udp_entry->addr, udp_entry->port);\r
+        }\r
+        udp_entry->addrinfo = *p;\r
+        memcpy(&udp_entry->addrinfo, p, sizeof(struct addrinfo));\r
+        memcpy(&udp_entry->sockaddr, p->ai_addr, sizeof(struct sockaddr_storage));\r
+        freeaddrinfo(servinfo);\r
+    }\r
+\r
+    udp_initialized = FCE_TRUE;\r
+}\r
+\r
+void fce_cleanup()\r
+{\r
+    if (udp_initialized == FCE_FALSE )\r
+        return;\r
+\r
+    for (int i = 0; i < udp_sockets; i++)\r
+    {\r
+        struct udp_entry *udp_entry = udp_socket_list + i;\r
+\r
+        /* Close any pending sockets */\r
+        if (udp_entry->sock != -1)\r
+        {\r
+            close( udp_entry->sock );\r
+            udp_entry->sock = -1;\r
+        }\r
+    }\r
+    udp_initialized = FCE_FALSE;\r
+}\r
+\r
+\r
+/*\r
+ * Construct a UDP packet for our listeners and return packet size\r
+ * */\r
+static ssize_t build_fce_packet( struct fce_packet *packet, char *path, int mode, uint32_t event_id )\r
+{\r
+    size_t pathlen;\r
+    ssize_t data_len = 0;\r
+\r
+    strncpy(packet->magic, FCE_PACKET_MAGIC, sizeof(packet->magic) );\r
+    packet->version = FCE_PACKET_VERSION;\r
+    packet->mode = mode;\r
+    packet->event_id = event_id;\r
+\r
+    pathlen = strlen(path) + 1; /* include string terminator */\r
+\r
+    /* This should never happen, but before we bust this server, we send nonsense, fce listener has to cope */\r
+    if (pathlen >= MAXPATHLEN)\r
+        pathlen = MAXPATHLEN - 1;\r
+\r
+    /* This is the payload len. Means: the stream has len bytes more until packet is finished */\r
+    /* A server should read the first 16 byte, decode them and then fetch the rest */\r
+    data_len = FCE_PACKET_HEADER_SIZE + pathlen;\r
+    packet->datalen = pathlen;\r
+\r
+    switch (mode) {\r
+    case FCE_TM_SIZE:\r
+        tm_used = hton64(tm_used);\r
+        memcpy(packet->data, &tm_used, sizeof(tm_used));\r
+        strncpy(packet->data + sizeof(tm_used), path, pathlen);\r
+\r
+        packet->datalen += sizeof(tm_used);\r
+        data_len += sizeof(tm_used);\r
+        break;\r
+    default:\r
+        strncpy(packet->data, path, pathlen);\r
+        break;\r
+    }\r
+\r
+    /* return the packet len */\r
+    return data_len;\r
+}\r
+\r
+static int pack_fce_packet(struct fce_packet *packet, unsigned char *buf)\r
+{\r
+    unsigned char *p = buf;\r
+\r
+    memcpy(p, &packet->magic[0], sizeof(packet->magic));\r
+    p += sizeof(packet->magic);\r
+\r
+    *p = packet->version;\r
+    p++;\r
+    \r
+    *p = packet->mode;\r
+    p++;\r
+    \r
+    uint32_t id = htonl(packet->event_id);\r
+    memcpy(p, &id, sizeof(id));\r
+    p += sizeof(packet->event_id);\r
+\r
+    uint16_t l = htons(packet->datalen);\r
+    memcpy(p, &l, sizeof(l));\r
+    p += sizeof(l);\r
+\r
+    memcpy(p, &packet->data[0], packet->datalen);\r
+    p += packet->datalen;\r
+\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Send the fce information to all (connected) listeners\r
+ * We dont give return code because all errors are handled internally (I hope..)\r
+ * */\r
+static void send_fce_event( char *path, int mode )\r
+{    \r
+    struct fce_packet packet;\r
+    void *data = &packet;\r
+    static uint32_t event_id = 0; /* the unique packet couter to detect packet/data loss. Going from 0xFFFFFFFF to 0x0 is a valid increment */\r
+\r
+    LOG(log_debug, logtype_afpd, "send_fce_event: start");\r
+\r
+    time_t now = time(NULL);\r
+\r
+    /* build our data packet */\r
+    ssize_t data_len = build_fce_packet( &packet, path, mode, ++event_id );\r
+    pack_fce_packet(&packet, iobuf);\r
+\r
+    for (int i = 0; i < udp_sockets; i++)\r
+    {\r
+        int sent_data = 0;\r
+        struct udp_entry *udp_entry = udp_socket_list + i;\r
+\r
+        /* we had a problem earlier ? */\r
+        if (udp_entry->sock == -1)\r
+        {\r
+            /* We still have to wait ?*/\r
+            if (now < udp_entry->next_try_on_error)\r
+                continue;\r
+\r
+            /* Reopen socket */\r
+            udp_entry->sock = socket(udp_entry->addrinfo.ai_family,\r
+                                     udp_entry->addrinfo.ai_socktype,\r
+                                     udp_entry->addrinfo.ai_protocol);\r
+            \r
+            if (udp_entry->sock == -1) {\r
+                /* failed again, so go to rest again */\r
+                LOG(log_error, logtype_afpd, "Cannot recreate socket for fce UDP connection: errno %d", errno  );\r
+\r
+                udp_entry->next_try_on_error = now + FCE_SOCKET_RETRY_DELAY_S;\r
+                continue;\r
+            }\r
+\r
+            udp_entry->next_try_on_error = 0;\r
+\r
+            /* Okay, we have a running socket again, send server that we had a problem on our side*/\r
+            data_len = build_fce_packet( &packet, "", FCE_CONN_BROKEN, 0 );\r
+            pack_fce_packet(&packet, iobuf);\r
+\r
+            sendto(udp_entry->sock,\r
+                   iobuf,\r
+                   data_len,\r
+                   0,\r
+                   (struct sockaddr *)&udp_entry->sockaddr,\r
+                   udp_entry->addrinfo.ai_addrlen);\r
+\r
+            /* Rebuild our original data packet */\r
+            data_len = build_fce_packet( &packet, path, mode, event_id );\r
+            pack_fce_packet(&packet, iobuf);\r
+        }\r
+\r
+        sent_data = sendto(udp_entry->sock,\r
+                           iobuf,\r
+                           data_len,\r
+                           0,\r
+                           (struct sockaddr *)&udp_entry->sockaddr,\r
+                           udp_entry->addrinfo.ai_addrlen);\r
+\r
+        /* Problems ? */\r
+        if (sent_data != data_len) {\r
+            /* Argh, socket broke, we close and retry later */\r
+            LOG(log_error, logtype_afpd, "send_fce_event: error sending packet to %s:%s, transfered %d of %d: %s",\r
+                udp_entry->addr, udp_entry->port, sent_data, data_len, strerror(errno));\r
+\r
+            close( udp_entry->sock );\r
+            udp_entry->sock = -1;\r
+            udp_entry->next_try_on_error = now + FCE_SOCKET_RETRY_DELAY_S;\r
+        }\r
+    }\r
+}\r
+\r
+static int add_udp_socket(const char *target_ip, const char *target_port )\r
+{\r
+    if (target_port == NULL)\r
+        target_port = FCE_DEFAULT_PORT_STRING;\r
+\r
+    if (udp_sockets >= FCE_MAX_UDP_SOCKS) {\r
+        LOG(log_error, logtype_afpd, "Too many file change api UDP connections (max %d allowed)", FCE_MAX_UDP_SOCKS );\r
+        return AFPERR_PARAM;\r
+    }\r
+\r
+    udp_socket_list[udp_sockets].addr = strdup(target_ip);\r
+    udp_socket_list[udp_sockets].port = strdup(target_port);\r
+    udp_socket_list[udp_sockets].sock = -1;\r
+    memset(&udp_socket_list[udp_sockets].addrinfo, 0, sizeof(struct addrinfo));\r
+    memset(&udp_socket_list[udp_sockets].sockaddr, 0, sizeof(struct sockaddr_storage));\r
+    udp_socket_list[udp_sockets].next_try_on_error = 0;\r
+\r
+    udp_sockets++;\r
+\r
+    return AFP_OK;\r
+}\r
+\r
+/*\r
+ *\r
+ * Dispatcher for all incoming file change events\r
+ *\r
+ * */\r
+static int register_fce(const char *u_name, int is_dir, int mode)\r
+{\r
+    if (udp_sockets == 0)\r
+        /* No listeners configured */\r
+        return AFP_OK;\r
+\r
+    if (u_name == NULL)\r
+        return AFPERR_PARAM;\r
+\r
+    static int first_event = FCE_TRUE;\r
+\r
+       /* do some initialization on the fly the first time */\r
+       if (first_event) {\r
+               fce_initialize_history();\r
+       }\r
+\r
+       /* handle files which should not cause events (.DS_Store atc. ) */\r
+       for (int i = 0; skip_files[i] != NULL; i++)\r
+       {\r
+               if (!strcmp( u_name, skip_files[i]))\r
+                       return AFP_OK;\r
+       }\r
+\r
+\r
+       char full_path_buffer[MAXPATHLEN + 1] = {""};\r
+       const char *cwd = getcwdpath();\r
+\r
+    if (mode == FCE_TM_SIZE) {\r
+        strlcpy(full_path_buffer, u_name, MAXPATHLEN);\r
+    } else if (!is_dir || mode == FCE_DIR_DELETE) {\r
+               if (strlen( cwd ) + strlen( u_name) + 1 >= MAXPATHLEN) {\r
+                       LOG(log_error, logtype_afpd, "FCE file name too long: %s/%s", cwd, u_name );\r
+                       return AFPERR_PARAM;\r
+               }\r
+               sprintf( full_path_buffer, "%s/%s", cwd, u_name );\r
+       } else {\r
+               if (strlen( cwd ) >= MAXPATHLEN) {\r
+                       LOG(log_error, logtype_afpd, "FCE directory name too long: %s", cwd);\r
+                       return AFPERR_PARAM;\r
+               }\r
+               strcpy( full_path_buffer, cwd);\r
+       }\r
+\r
+       /* Can we ignore this event based on type or history? */\r
+       if (!(mode & FCE_TM_SIZE) && fce_handle_coalescation( full_path_buffer, is_dir, mode ))\r
+       {\r
+               LOG(log_debug9, logtype_afpd, "Coalesced fc event <%d> for <%s>", mode, full_path_buffer );\r
+               return AFP_OK;\r
+       }\r
+\r
+       LOG(log_debug9, logtype_afpd, "Detected fc event <%d> for <%s>", mode, full_path_buffer );\r
+\r
+\r
+    /* we do initilization on the fly, no blocking calls in here \r
+     * (except when using FQDN in broken DNS environment)\r
+     */\r
+    if (first_event == FCE_TRUE)\r
+    {\r
+        fce_init_udp();\r
+        \r
+        /* Notify listeners the we start from the beginning */\r
+        send_fce_event( "", FCE_CONN_START );\r
+        \r
+        first_event = FCE_FALSE;\r
+    }\r
+\r
+       /* Handle UDP transport */\r
+    send_fce_event( full_path_buffer, mode );\r
+\r
+    return AFP_OK;\r
+}\r
+\r
+\r
+/******************** External calls start here **************************/\r
+\r
+/*\r
+ * API-Calls for file change api, called form outside (file.c directory.c ofork.c filedir.c)\r
+ * */\r
+#ifndef FCE_TEST_MAIN\r
+\r
+int fce_register_delete_file( struct path *path )\r
+{\r
+    int ret = AFP_OK;\r
+\r
+    if (path == NULL)\r
+        return AFPERR_PARAM;\r
+\r
+    if (!(fce_ev_enabled & (1 << FCE_FILE_DELETE)))\r
+        return ret;\r
+       \r
+    ret = register_fce( path->u_name, FALSE, FCE_FILE_DELETE );\r
+\r
+    return ret;\r
+}\r
+int fce_register_delete_dir( char *name )\r
+{\r
+    int ret = AFP_OK;\r
+\r
+    if (name == NULL)\r
+        return AFPERR_PARAM;\r
+\r
+    if (!(fce_ev_enabled & (1 << FCE_DIR_DELETE)))\r
+        return ret;\r
+       \r
+    ret = register_fce( name, TRUE, FCE_DIR_DELETE);\r
+\r
+    return ret;\r
+}\r
+\r
+int fce_register_new_dir( struct path *path )\r
+{\r
+    int ret = AFP_OK;\r
+\r
+    if (path == NULL)\r
+        return AFPERR_PARAM;\r
+\r
+    if (!(fce_ev_enabled & (1 << FCE_DIR_CREATE)))\r
+        return ret;\r
+\r
+    ret = register_fce( path->u_name, TRUE, FCE_DIR_CREATE );\r
+\r
+    return ret;\r
+}\r
+\r
+\r
+int fce_register_new_file( struct path *path )\r
+{\r
+    int ret = AFP_OK;\r
+\r
+    if (path == NULL)\r
+        return AFPERR_PARAM;\r
+\r
+    if (!(fce_ev_enabled & (1 << FCE_FILE_CREATE)))\r
+        return ret;\r
+\r
+    ret = register_fce( path->u_name, FALSE, FCE_FILE_CREATE );\r
+\r
+    return ret;\r
+}\r
+\r
+int fce_register_file_modification( struct ofork *ofork )\r
+{\r
+    char *u_name = NULL;\r
+    struct vol *vol;\r
+    int ret = AFP_OK;\r
+\r
+    if (ofork == NULL || ofork->of_vol == NULL)\r
+        return AFPERR_PARAM;\r
+\r
+    if (!(fce_ev_enabled & (1 << FCE_FILE_MODIFY)))\r
+        return ret;\r
+\r
+    vol = ofork->of_vol;\r
+\r
+    if (NULL == (u_name = mtoupath(vol, of_name(ofork), ofork->of_did, utf8_encoding()))) \r
+    {\r
+        return AFPERR_MISC;\r
+    }\r
+    \r
+    ret = register_fce( u_name, FALSE, FCE_FILE_MODIFY );\r
+    \r
+    return ret;    \r
+}\r
+\r
+int fce_register_tm_size(const char *vol, size_t used)\r
+{\r
+    int ret = AFP_OK;\r
+\r
+    if (vol == NULL)\r
+        return AFPERR_PARAM;\r
+\r
+    if (!(fce_ev_enabled & (1 << FCE_TM_SIZE)))\r
+        return ret;\r
+\r
+    tm_used = used;             /* oh what a hack */\r
+    ret = register_fce(vol, FALSE, FCE_TM_SIZE);\r
+\r
+    return ret;\r
+}\r
+#endif\r
+\r
+/*\r
+ *\r
+ * Extern connect to afpd parameter, can be called multiple times for multiple listeners (up to MAX_UDP_SOCKS times)\r
+ *\r
+ * */\r
+int fce_add_udp_socket(const char *target)\r
+{\r
+       const char *port = FCE_DEFAULT_PORT_STRING;\r
+       char target_ip[256] = {""};\r
+\r
+       strncpy(target_ip, target, sizeof(target_ip) -1);\r
+\r
+       char *port_delim = strchr( target_ip, ':' );\r
+       if (port_delim) {\r
+               *port_delim = 0;\r
+               port = port_delim + 1;\r
+       }\r
+       return add_udp_socket(target_ip, port);\r
+}\r
+\r
+int fce_set_events(const char *events)\r
+{\r
+    char *e;\r
+    char *p;\r
+    \r
+    if (events == NULL)\r
+        return AFPERR_PARAM;\r
+\r
+    e = strdup(events);\r
+\r
+    fce_ev_enabled = 0;\r
+\r
+    for (p = strtok(e, ","); p; p = strtok(NULL, ",")) {\r
+        if (strcmp(p, "fmod") == 0) {\r
+            fce_ev_enabled |= (1 << FCE_FILE_MODIFY);\r
+        } else if (strcmp(p, "fdel") == 0) {\r
+            fce_ev_enabled |= (1 << FCE_FILE_DELETE);\r
+        } else if (strcmp(p, "ddel") == 0) {\r
+            fce_ev_enabled |= (1 << FCE_DIR_DELETE);\r
+        } else if (strcmp(p, "fcre") == 0) {\r
+            fce_ev_enabled |= (1 << FCE_FILE_CREATE);\r
+        } else if (strcmp(p, "dcre") == 0) {\r
+            fce_ev_enabled |= (1 << FCE_DIR_CREATE);\r
+        } else if (strcmp(p, "tmsz") == 0) {\r
+            fce_ev_enabled |= (1 << FCE_TM_SIZE);\r
+        }\r
+    }\r
+\r
+    free(e);\r
+}\r
+\r
+#ifdef FCE_TEST_MAIN\r
+\r
+\r
+void shortsleep( unsigned int us )\r
+{    \r
+    usleep( us );\r
+}\r
+int main( int argc, char*argv[] )\r
+{\r
+    int c,ret;\r
+\r
+    char *port = FCE_DEFAULT_PORT_STRING;\r
+    char *host = "localhost";\r
+    int delay_between_events = 1000;\r
+    int event_code = FCE_FILE_MODIFY;\r
+    char pathbuff[1024];\r
+    int duration_in_seconds = 0; // TILL ETERNITY\r
+    char target[256];\r
+    char *path = getcwd( pathbuff, sizeof(pathbuff) );\r
+\r
+    // FULLSPEED TEST IS "-s 1001" -> delay is 0 -> send packets without pause\r
+\r
+    while ((c = getopt(argc, argv, "d:e:h:p:P:s:")) != -1) {\r
+        switch(c) {\r
+        case '?':\r
+            fprintf(stdout, "%s: [ -p Port -h Listener1 [ -h Listener2 ...] -P path -s Delay_between_events_in_us -e event_code -d Duration ]\n", argv[0]);\r
+            exit(1);\r
+            break;\r
+        case 'd':\r
+            duration_in_seconds = atoi(optarg);\r
+            break;\r
+        case 'e':\r
+            event_code = atoi(optarg);\r
+            break;\r
+        case 'h':\r
+            host = strdup(optarg);\r
+            break;\r
+        case 'p':\r
+            port = strdup(optarg);\r
+            break;\r
+        case 'P':\r
+            path = strdup(optarg);\r
+            break;\r
+        case 's':\r
+            delay_between_events = atoi(optarg);\r
+            break;\r
+        }\r
+    }\r
+\r
+    sprintf(target, "%s:%s", host, port);\r
+    if (fce_add_udp_socket(target) != 0)\r
+        return 1;\r
+\r
+    int ev_cnt = 0;\r
+    time_t start_time = time(NULL);\r
+    time_t end_time = 0;\r
+\r
+    if (duration_in_seconds)\r
+        end_time = start_time + duration_in_seconds;\r
+\r
+    while (1)\r
+    {\r
+        time_t now = time(NULL);\r
+        if (now > start_time)\r
+        {\r
+            start_time = now;\r
+            fprintf( stdout, "%d events/s\n", ev_cnt );\r
+            ev_cnt = 0;\r
+        }\r
+        if (end_time && now >= end_time)\r
+            break;\r
+\r
+        register_fce( path, 0, event_code );\r
+        ev_cnt++;\r
+\r
+        \r
+        shortsleep( delay_between_events );\r
+    }\r
+}\r
+#endif /* TESTMAIN*/\r
diff --git a/etc/afpd/fce_api_internal.h b/etc/afpd/fce_api_internal.h
new file mode 100755 (executable)
index 0000000..d73b775
--- /dev/null
@@ -0,0 +1,42 @@
+/* \r
+ * File:   fce_api_internal.h\r
+ * Author: mw\r
+ *\r
+ * Created on 1. Oktober 2010, 23:48\r
+ */\r
+\r
+#ifndef _FCE_API_INTERNAL_H\r
+#define        _FCE_API_INTERNAL_H\r
+\r
+#define FCE_MAX_UDP_SOCKS 5     /* Allow a maximum of udp listeners for file change events */\r
+#define FCE_SOCKET_RETRY_DELAY_S 600 /* Pause this time in s after socket was broken */\r
+#define FCE_PACKET_VERSION  1\r
+#define FCE_HISTORY_LEN 10  /* This is used to coalesce events */\r
+#define MAX_COALESCE_TIME_MS 1000  /* Events oldeer than this are not coalesced */\r
+\r
+struct udp_entry\r
+{\r
+    int sock;\r
+    char *addr;\r
+    char *port;\r
+    struct addrinfo addrinfo;\r
+    struct sockaddr_storage sockaddr;\r
+    time_t next_try_on_error;      /* In case of error set next timestamp to retry */\r
+};\r
+\r
+struct fce_history\r
+{\r
+    unsigned char mode;\r
+       int is_dir;\r
+       char path[MAXPATHLEN + 1];\r
+       struct timeval tv;\r
+};\r
+\r
+#define PACKET_HDR_LEN (sizeof(struct fce_packet) - FCE_MAX_PATH_LEN)\r
+\r
+int fce_handle_coalescation( char *path, int is_dir, int mode );\r
+void fce_initialize_history();\r
+\r
+\r
+#endif /* _FCE_API_INTERNAL_H */\r
+\r
diff --git a/etc/afpd/fce_util.c b/etc/afpd/fce_util.c
new file mode 100755 (executable)
index 0000000..6340110
--- /dev/null
@@ -0,0 +1,223 @@
+/*\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, MAXPATHLEN);\r
+\r
+       /* we have to handle this event */\r
+       return FALSE;\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
index 323642f9ce92f305e7b9b905fdf102c726119656..ec85f37a52e0dfdefb79335b05e447bb970138ea 100644 (file)
@@ -38,6 +38,7 @@ char *strchr (), *strrchr ();
 #include <atalk/cnid.h>
 #include <atalk/unix.h>
 #include <atalk/globals.h>
+#include <atalk/fce_api.h>
 
 #include "directory.h"
 #include "dircache.h"
@@ -771,6 +772,9 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
     (void)get_id(vol, adp, &st, dir->d_did, upath, strlen(upath));
 
     ad_flush( adp);
+
+    fce_register_new_file(s_path);
+
     ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
 
 createfile_done:
index 356d82104cdadb69d910b651b92ff13632eab369..7166c125d36c8af3cae56270fec3924a8e31037e 100644 (file)
@@ -41,6 +41,7 @@ char *strchr (), *strrchr ();
 #include <atalk/bstradd.h>
 #include <atalk/acl.h>
 #include <atalk/globals.h>
+#include <atalk/fce_api.h>
 
 #include "directory.h"
 #include "dircache.h"
@@ -601,10 +602,18 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
 
     upath = s_path->u_name;
     if ( path_isadir( s_path) ) {
-        if (*s_path->m_name != '\0' || curdir->d_did == DIRDID_ROOT)
+        if (*s_path->m_name != '\0' || curdir->d_did == DIRDID_ROOT) {
             rc = AFPERR_ACCESS;
-        else
-            rc = deletecurdir( vol);
+        } else {
+            /* we have to cache this, the structs are lost in deletcurdir*/
+            /* but we need the positive returncode to send our event */
+            bstring dname;
+            if ((dname = bstrcpy(curdir->d_u_name)) == NULL)
+                return AFPERR_MISC;
+            if ((rc = deletecurdir(vol)) == AFP_OK)
+                fce_register_delete_dir(cfrombstr(dname));
+            bdestroy(dname);
+        }
     } else if (of_findname(s_path)) {
         rc = AFPERR_BUSY;
     } else {
@@ -614,9 +623,9 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
          */
         if (s_path->st_valid && s_path->st_errno == ENOENT) {
             rc = AFPERR_NOOBJ;
-        }
-        else {
-            rc = deletefile(vol, -1, upath, 1);
+        } else {
+            if ((rc = deletefile(vol, -1, upath, 1)) == AFP_OK)
+                               fce_register_delete_file( s_path );
 
             struct dir *cachedfile;
             if ((cachedfile = dircache_search_by_name(vol, dir, upath, strlen(upath)))) {
index b867e4dd0f9501d96728082809dee95d653a7beb..cf9d8b841b643289be6466225569801f70b11b34 100644 (file)
@@ -1336,6 +1336,9 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
     if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */
         ofork->of_flags |= AFPFORK_DIRTY;
 
+    /* we have modified any fork, remember until close_fork */
+    ofork->of_flags |= AFPFORK_MODIFIED;
+
     *rbuflen = set_off_t (offset, rbuf, is64);
     return( AFP_OK );
 
index 7ea7a8a6edc56e52424897ce07616847224f4886..ae22b8f7f6b52c1b161808ac027b07948680af97 100644 (file)
@@ -46,6 +46,7 @@ struct ofork {
 #define AFPFORK_ACCRD   (1<<4)
 #define AFPFORK_ACCWR   (1<<5)
 #define AFPFORK_ACCMASK (AFPFORK_ACCRD | AFPFORK_ACCWR)
+#define AFPFORK_MODIFIED (1<<6) /* used in FCE for modified files */
 
 #ifdef AFS
 extern struct ofork *writtenfork;
index 6182fcff540f71d2bbbb1f32b33338a80b51af09..2d2600895cb5cc37a730a84a5bb1bdb1b9c6380e 100644 (file)
@@ -22,6 +22,7 @@
 #include <atalk/bstrlib.h>
 #include <atalk/bstradd.h>
 #include <atalk/globals.h>
+#include <atalk/fce_api.h>
 
 #include "volume.h"
 #include "directory.h"
@@ -431,6 +432,12 @@ int of_closefork(struct ofork *ofork)
             }
         }
     }
+
+    /* Somone has used write_fork, we assume file was changed, register it to file change event api */
+    if (ofork->of_flags & AFPFORK_MODIFIED) {
+        fce_register_file_modification(ofork);
+    }
+
     ret = 0;
     if ( ad_close( ofork->of_ad, adflags ) < 0 ) {
         ret = -1;
index 3404048a520e0c1400bd6a8646ebb6f6f474f50a..0c810b82bd43c43d0b2fd76f206ff0c04b372c4c 100644 (file)
@@ -71,17 +71,6 @@ extern int afprun(int root, char *cmd, int *outfd);
 #define MIN(a, b) ((a) < (b) ? (a) : (b))
 #endif /* ! MIN */
 
-#ifndef NO_LARGE_VOL_SUPPORT
-#if BYTE_ORDER == BIG_ENDIAN
-#define hton64(x)       (x)
-#define ntoh64(x)       (x)
-#else /* BYTE_ORDER == BIG_ENDIAN */
-#define hton64(x)       ((u_int64_t) (htonl(((x) >> 32) & 0xffffffffLL)) | \
-                         (u_int64_t) ((htonl(x) & 0xffffffffLL) << 32))
-#define ntoh64(x)       (hton64(x))
-#endif /* BYTE_ORDER == BIG_ENDIAN */
-#endif /* ! NO_LARGE_VOL_SUPPORT */
-
 #ifndef UUID_PRINTABLE_STRING_LENGTH
 #define UUID_PRINTABLE_STRING_LENGTH 37
 #endif
index a75839c27f201bb943818c53e12a1b41b9c84091..e4939e29082a5954c7f5cac4eeac6da43f658d24 100644 (file)
@@ -8,4 +8,4 @@ atalkinclude_HEADERS = \
        server_ipc.h tdb.h uam.h unicode.h util.h uuid.h volinfo.h \
        zip.h ea.h acl.h unix.h directory.h hash.h volume.h
 
-noinst_HEADERS = cnid_dbd_private.h cnid_private.h bstradd.h bstrlib.h errchk.h ftw.h globals.h
+noinst_HEADERS = cnid_dbd_private.h cnid_private.h bstradd.h bstrlib.h errchk.h ftw.h globals.h fce_api.h
diff --git a/include/atalk/fce_api.h b/include/atalk/fce_api.h
new file mode 100755 (executable)
index 0000000..b6111e4
--- /dev/null
@@ -0,0 +1,60 @@
+/* 
+ * File:   fce_api.h
+ * Author: mw
+ *
+ * Created on 1. Oktober 2010, 21:35
+ *
+ * API calls for file change event api
+ */
+
+#ifndef _FCE_API_H
+#define        _FCE_API_H
+
+/* fce_packet.mode */
+#define FCE_FILE_MODIFY     1
+#define FCE_FILE_DELETE     2
+#define FCE_DIR_DELETE      3
+#define FCE_FILE_CREATE     4
+#define FCE_DIR_CREATE      5
+#define FCE_TM_SIZE         6
+#define FCE_CONN_START     42
+#define FCE_CONN_BROKEN    99
+
+
+/* fce_packet.fce_magic */
+#define FCE_PACKET_MAGIC  "at_fcapi"
+
+/* This packet goes over the network, so we want to
+ * be shure about datastructs and type sizes between platforms.
+ * Format is network byte order.
+ */
+#define FCE_PACKET_HEADER_SIZE 8+1+1+4+2
+struct fce_packet
+{
+    char magic[8];
+    unsigned char version;
+    unsigned char mode;
+    uint32_t event_id;
+    uint16_t datalen;
+    char data[MAXPATHLEN];
+};
+
+struct path;
+struct ofork;
+
+int fce_register_delete_file( struct path *path );
+int fce_register_delete_dir( char *name );
+int fce_register_new_dir( struct path *path );
+int fce_register_new_file( struct path *path );
+int fce_register_file_modification( struct ofork *ofork );
+int fce_register_tm_size(const char *vol, size_t used);
+
+int fce_add_udp_socket(const char *target );  // IP or IP:Port
+int fce_set_coalesce( char *coalesce_opt ); // all|delete|create
+int fce_set_events(const char *events);     /* fmod,fdel,ddel,fcre,dcre,tmsz (default is all except tmsz) */
+
+#define FCE_DEFAULT_PORT 12250
+#define FCE_DEFAULT_PORT_STRING "12250"
+
+#endif /* _FCE_API_H */
+
index 94edbb82b4896e7c098c0efb8304920bbcef5ea9..277ac98975d7d430d50d999cfe6eb57ba7447bbb 100644 (file)
 
 #define STRCMP(a,b,c) (strcmp(a,c) b 0)
 
+#if BYTE_ORDER == BIG_ENDIAN
+#define hton64(x)       (x)
+#define ntoh64(x)       (x)
+#else /* BYTE_ORDER == BIG_ENDIAN */
+#define hton64(x)       ((uint64_t) (htonl(((x) >> 32) & 0xffffffffLL)) | \
+                         (uint64_t) ((htonl(x) & 0xffffffffLL) << 32))
+#define ntoh64(x)       (hton64(x))
+#endif /* BYTE_ORDER == BIG_ENDIAN */
+
 #ifdef WITH_SENDFILE
 extern ssize_t sys_sendfile (int __out_fd, int __in_fd, off_t *__offset,size_t __count);
 #endif
index b3f099ff1641d7680581c5cecb5c7a7aa5045b2c..a6c29e61162b03f8f96e6e223df5b028e26ccf63 100644 (file)
@@ -386,6 +386,25 @@ Maximum possible entries in the directory cache\&. The cache stores directories
 Default size is 8192, maximum size is 131072\&. Given value is rounded up to nearest power of 2\&. Each entry takes about 100 bytes, which is not much, but remember that every afpd child process for every connected user has its cache\&.
 .RE
 .PP
+\-fcelistener \fIhost[:port]\fR
+.RS 4
+Enables sending FCE events to the specified
+\fIhost\fR, default
+\fIport\fR
+is 12250 if not specified\&. Specifying mutliple listeners is done by having this option once for each of them\&.
+.RE
+.PP
+\-fceevents \fIfmod,fdel,ddel,fcre,dcre,tmsz\fR
+.RS 4
+Speficies which FCE events are active, default is
+\fIfmod,fdel,ddel,fcre,dcre\fR\&.
+.RE
+.PP
+\-fcecoalesce \fIall|delete|create\fR
+.RS 4
+Coalesce FCE events\&.
+.RE
+.PP
 \-guestname \fI[name]\fR
 .RS 4
 Specifies the user that guests should use (default is "nobody")\&. The name should be quoted\&.