]> arthur.barton.de Git - netatalk.git/commitdiff
not CRLF
authorHAT <hat@fa2.so-net.ne.jp>
Mon, 15 Aug 2011 08:24:54 +0000 (17:24 +0900)
committerHAT <hat@fa2.so-net.ne.jp>
Mon, 15 Aug 2011 08:24:54 +0000 (17:24 +0900)
etc/afpd/fce_api.c
etc/afpd/fce_api_internal.h
etc/afpd/fce_util.c

index 00a04248406cfab1f1e90676fc3d5c93cf8bc75f..fb7535b6c485e68b2dd8bfe269d2aabb8d89eca8 100644 (file)
-/*\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
-#include <atalk/globals.h>\r
-\r
-#include "fork.h"\r
-#include "file.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
-static struct fce_close_event last_close_event;\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
-    static int first_event = FCE_TRUE;\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
-    time_t now = time(NULL);\r
-\r
-    LOG(log_debug, logtype_afpd, "send_fce_event: start");\r
-\r
-    /* initialized ? */\r
-    if (first_event == FCE_TRUE) {\r
-        first_event = FCE_FALSE;\r
-        fce_init_udp();\r
-        /* Notify listeners the we start from the beginning */\r
-        send_fce_event( "", FCE_CONN_START );\r
-    }\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
-static void save_close_event(const char *path)\r
-{\r
-    time_t now = time(NULL);\r
-\r
-    /* Check if it's a close for the same event as the last one */\r
-    if (last_close_event.time   /* is there any saved event ? */\r
-        && (strcmp(path, last_close_event.path) != 0)) {\r
-        /* no, so send the saved event out now */\r
-        send_fce_event(last_close_event.path, FCE_FILE_MODIFY);\r
-    }\r
-\r
-    LOG(log_debug, logtype_afpd, "save_close_event: %s", path);\r
-\r
-    last_close_event.time = now;\r
-    strncpy(last_close_event.path, path, MAXPATHLEN);\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
-    static int first_event = FCE_TRUE;\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
-       /* do some initialization on the fly the first time */\r
-       if (first_event) {\r
-               fce_initialize_history();\r
-        first_event = FCE_FALSE;\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
-    if (mode & FCE_FILE_MODIFY) {\r
-        save_close_event(full_path_buffer);\r
-        return AFP_OK;\r
-    }\r
-\r
-    send_fce_event( full_path_buffer, mode );\r
-\r
-    return AFP_OK;\r
-}\r
-\r
-static void check_saved_close_events(int fmodwait)\r
-{\r
-    time_t now = time(NULL);\r
-\r
-    /* check if configured holdclose time has passed */\r
-    if (last_close_event.time && ((last_close_event.time + fmodwait) < now)) {\r
-        LOG(log_debug, logtype_afpd, "check_saved_close_events: sending event: %s", last_close_event.path);\r
-        /* yes, send event */\r
-        send_fce_event(&last_close_event.path[0], FCE_FILE_MODIFY);\r
-        last_close_event.path[0] = 0;\r
-        last_close_event.time = 0;\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
-void fce_pending_events(AFPObj *obj)\r
-{\r
-    vol_fce_tm_event();\r
-    check_saved_close_events(obj->options.fce_fmodwait);\r
-}\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
+/*
+ * 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 <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <netatalk/at.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"
+
+#define FCE_TRUE 1
+#define FCE_FALSE 0
+
+/* We store our connection data here */
+static struct udp_entry udp_socket_list[FCE_MAX_UDP_SOCKS];
+static int udp_sockets = 0;
+static int udp_initialized = FCE_FALSE;
+static unsigned long fce_ev_enabled =
+    (1 << FCE_FILE_MODIFY) |
+    (1 << FCE_FILE_DELETE) |
+    (1 << FCE_DIR_DELETE) |
+    (1 << FCE_FILE_CREATE) |
+    (1 << FCE_DIR_CREATE);
+
+static uint64_t tm_used;          /* used for passing to event handler */
+#define MAXIOBUF 1024
+static char iobuf[MAXIOBUF];
+static const char *skip_files[] = 
+{
+       ".DS_Store",
+       NULL
+};
+static struct fce_close_event last_close_event;
+
+/*
+ *
+ * Initialize network structs for any listeners
+ * We dont give return code because all errors are handled internally (I hope..)
+ *
+ * */
+void fce_init_udp()
+{
+    int rv;
+    struct addrinfo hints, *servinfo, *p;
+
+    if (udp_initialized == FCE_TRUE)
+        return;
+
+    memset(&hints, 0, sizeof hints);
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_DGRAM;
+
+    for (int i = 0; i < udp_sockets; i++) {
+        struct udp_entry *udp_entry = udp_socket_list + i;
+
+        /* Close any pending sockets */
+        if (udp_entry->sock != -1)
+            close(udp_entry->sock);
+
+        if ((rv = getaddrinfo(udp_entry->addr, udp_entry->port, &hints, &servinfo)) != 0) {
+            LOG(log_error, logtype_afpd, "fce_init_udp: getaddrinfo(%s:%s): %s",
+                udp_entry->addr, udp_entry->port, gai_strerror(rv));
+            continue;
+        }
+
+        /* loop through all the results and make a socket */
+        for (p = servinfo; p != NULL; p = p->ai_next) {
+            if ((udp_entry->sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
+                LOG(log_error, logtype_afpd, "fce_init_udp: socket(%s:%s): %s",
+                    udp_entry->addr, udp_entry->port, strerror(errno));
+                continue;
+            }
+            break;
+        }
+
+        if (p == NULL) {
+            LOG(log_error, logtype_afpd, "fce_init_udp: no socket for %s:%s",
+                udp_entry->addr, udp_entry->port);
+        }
+        udp_entry->addrinfo = *p;
+        memcpy(&udp_entry->addrinfo, p, sizeof(struct addrinfo));
+        memcpy(&udp_entry->sockaddr, p->ai_addr, sizeof(struct sockaddr_storage));
+        freeaddrinfo(servinfo);
+    }
+
+    udp_initialized = FCE_TRUE;
+}
+
+void fce_cleanup()
+{
+    if (udp_initialized == FCE_FALSE )
+        return;
+
+    for (int i = 0; i < udp_sockets; i++)
+    {
+        struct udp_entry *udp_entry = udp_socket_list + i;
+
+        /* Close any pending sockets */
+        if (udp_entry->sock != -1)
+        {
+            close( udp_entry->sock );
+            udp_entry->sock = -1;
+        }
+    }
+    udp_initialized = FCE_FALSE;
+}
+
+
+/*
+ * Construct a UDP packet for our listeners and return packet size
+ * */
+static ssize_t build_fce_packet( struct fce_packet *packet, char *path, int mode, uint32_t event_id )
+{
+    size_t pathlen;
+    ssize_t data_len = 0;
+
+    strncpy(packet->magic, FCE_PACKET_MAGIC, sizeof(packet->magic) );
+    packet->version = FCE_PACKET_VERSION;
+    packet->mode = mode;
+    packet->event_id = event_id;
+
+    pathlen = strlen(path) + 1; /* include string terminator */
+
+    /* This should never happen, but before we bust this server, we send nonsense, fce listener has to cope */
+    if (pathlen >= MAXPATHLEN)
+        pathlen = MAXPATHLEN - 1;
+
+    /* This is the payload len. Means: the stream has len bytes more until packet is finished */
+    /* A server should read the first 16 byte, decode them and then fetch the rest */
+    data_len = FCE_PACKET_HEADER_SIZE + pathlen;
+    packet->datalen = pathlen;
+
+    switch (mode) {
+    case FCE_TM_SIZE:
+        tm_used = hton64(tm_used);
+        memcpy(packet->data, &tm_used, sizeof(tm_used));
+        strncpy(packet->data + sizeof(tm_used), path, pathlen);
+
+        packet->datalen += sizeof(tm_used);
+        data_len += sizeof(tm_used);
+        break;
+    default:
+        strncpy(packet->data, path, pathlen);
+        break;
+    }
+
+    /* return the packet len */
+    return data_len;
+}
+
+static int pack_fce_packet(struct fce_packet *packet, unsigned char *buf)
+{
+    unsigned char *p = buf;
+
+    memcpy(p, &packet->magic[0], sizeof(packet->magic));
+    p += sizeof(packet->magic);
+
+    *p = packet->version;
+    p++;
+    
+    *p = packet->mode;
+    p++;
+    
+    uint32_t id = htonl(packet->event_id);
+    memcpy(p, &id, sizeof(id));
+    p += sizeof(packet->event_id);
+
+    uint16_t l = htons(packet->datalen);
+    memcpy(p, &l, sizeof(l));
+    p += sizeof(l);
+
+    memcpy(p, &packet->data[0], packet->datalen);
+    p += packet->datalen;
+
+    return 0;
+}
+
+/*
+ * Send the fce information to all (connected) listeners
+ * We dont give return code because all errors are handled internally (I hope..)
+ * */
+static void send_fce_event( char *path, int mode )
+{    
+    static int first_event = FCE_TRUE;
+
+    struct fce_packet packet;
+    void *data = &packet;
+    static uint32_t event_id = 0; /* the unique packet couter to detect packet/data loss. Going from 0xFFFFFFFF to 0x0 is a valid increment */
+    time_t now = time(NULL);
+
+    LOG(log_debug, logtype_afpd, "send_fce_event: start");
+
+    /* initialized ? */
+    if (first_event == FCE_TRUE) {
+        first_event = FCE_FALSE;
+        fce_init_udp();
+        /* Notify listeners the we start from the beginning */
+        send_fce_event( "", FCE_CONN_START );
+    }
+
+    /* build our data packet */
+    ssize_t data_len = build_fce_packet( &packet, path, mode, ++event_id );
+    pack_fce_packet(&packet, iobuf);
+
+    for (int i = 0; i < udp_sockets; i++)
+    {
+        int sent_data = 0;
+        struct udp_entry *udp_entry = udp_socket_list + i;
+
+        /* we had a problem earlier ? */
+        if (udp_entry->sock == -1)
+        {
+            /* We still have to wait ?*/
+            if (now < udp_entry->next_try_on_error)
+                continue;
+
+            /* Reopen socket */
+            udp_entry->sock = socket(udp_entry->addrinfo.ai_family,
+                                     udp_entry->addrinfo.ai_socktype,
+                                     udp_entry->addrinfo.ai_protocol);
+            
+            if (udp_entry->sock == -1) {
+                /* failed again, so go to rest again */
+                LOG(log_error, logtype_afpd, "Cannot recreate socket for fce UDP connection: errno %d", errno  );
+
+                udp_entry->next_try_on_error = now + FCE_SOCKET_RETRY_DELAY_S;
+                continue;
+            }
+
+            udp_entry->next_try_on_error = 0;
+
+            /* Okay, we have a running socket again, send server that we had a problem on our side*/
+            data_len = build_fce_packet( &packet, "", FCE_CONN_BROKEN, 0 );
+            pack_fce_packet(&packet, iobuf);
+
+            sendto(udp_entry->sock,
+                   iobuf,
+                   data_len,
+                   0,
+                   (struct sockaddr *)&udp_entry->sockaddr,
+                   udp_entry->addrinfo.ai_addrlen);
+
+            /* Rebuild our original data packet */
+            data_len = build_fce_packet( &packet, path, mode, event_id );
+            pack_fce_packet(&packet, iobuf);
+        }
+
+        sent_data = sendto(udp_entry->sock,
+                           iobuf,
+                           data_len,
+                           0,
+                           (struct sockaddr *)&udp_entry->sockaddr,
+                           udp_entry->addrinfo.ai_addrlen);
+
+        /* Problems ? */
+        if (sent_data != data_len) {
+            /* Argh, socket broke, we close and retry later */
+            LOG(log_error, logtype_afpd, "send_fce_event: error sending packet to %s:%s, transfered %d of %d: %s",
+                udp_entry->addr, udp_entry->port, sent_data, data_len, strerror(errno));
+
+            close( udp_entry->sock );
+            udp_entry->sock = -1;
+            udp_entry->next_try_on_error = now + FCE_SOCKET_RETRY_DELAY_S;
+        }
+    }
+}
+
+static int add_udp_socket(const char *target_ip, const char *target_port )
+{
+    if (target_port == NULL)
+        target_port = FCE_DEFAULT_PORT_STRING;
+
+    if (udp_sockets >= FCE_MAX_UDP_SOCKS) {
+        LOG(log_error, logtype_afpd, "Too many file change api UDP connections (max %d allowed)", FCE_MAX_UDP_SOCKS );
+        return AFPERR_PARAM;
+    }
+
+    udp_socket_list[udp_sockets].addr = strdup(target_ip);
+    udp_socket_list[udp_sockets].port = strdup(target_port);
+    udp_socket_list[udp_sockets].sock = -1;
+    memset(&udp_socket_list[udp_sockets].addrinfo, 0, sizeof(struct addrinfo));
+    memset(&udp_socket_list[udp_sockets].sockaddr, 0, sizeof(struct sockaddr_storage));
+    udp_socket_list[udp_sockets].next_try_on_error = 0;
+
+    udp_sockets++;
+
+    return AFP_OK;
+}
+
+static void save_close_event(const char *path)
+{
+    time_t now = time(NULL);
+
+    /* Check if it's a close for the same event as the last one */
+    if (last_close_event.time   /* is there any saved event ? */
+        && (strcmp(path, last_close_event.path) != 0)) {
+        /* no, so send the saved event out now */
+        send_fce_event(last_close_event.path, FCE_FILE_MODIFY);
+    }
+
+    LOG(log_debug, logtype_afpd, "save_close_event: %s", path);
+
+    last_close_event.time = now;
+    strncpy(last_close_event.path, path, MAXPATHLEN);
+}
+
+/*
+ *
+ * Dispatcher for all incoming file change events
+ *
+ * */
+static int register_fce(const char *u_name, int is_dir, int mode)
+{
+    static int first_event = FCE_TRUE;
+
+    if (udp_sockets == 0)
+        /* No listeners configured */
+        return AFP_OK;
+
+    if (u_name == NULL)
+        return AFPERR_PARAM;
+
+       /* do some initialization on the fly the first time */
+       if (first_event) {
+               fce_initialize_history();
+        first_event = FCE_FALSE;
+       }
+
+       /* handle files which should not cause events (.DS_Store atc. ) */
+       for (int i = 0; skip_files[i] != NULL; i++)
+       {
+               if (!strcmp( u_name, skip_files[i]))
+                       return AFP_OK;
+       }
+
+
+       char full_path_buffer[MAXPATHLEN + 1] = {""};
+       const char *cwd = getcwdpath();
+
+    if (mode == FCE_TM_SIZE) {
+        strlcpy(full_path_buffer, u_name, MAXPATHLEN);
+    } else if (!is_dir || mode == FCE_DIR_DELETE) {
+               if (strlen( cwd ) + strlen( u_name) + 1 >= MAXPATHLEN) {
+                       LOG(log_error, logtype_afpd, "FCE file name too long: %s/%s", cwd, u_name );
+                       return AFPERR_PARAM;
+               }
+               sprintf( full_path_buffer, "%s/%s", cwd, u_name );
+       } else {
+               if (strlen( cwd ) >= MAXPATHLEN) {
+                       LOG(log_error, logtype_afpd, "FCE directory name too long: %s", cwd);
+                       return AFPERR_PARAM;
+               }
+               strcpy( full_path_buffer, cwd);
+       }
+
+       /* Can we ignore this event based on type or history? */
+       if (!(mode & FCE_TM_SIZE) && fce_handle_coalescation( full_path_buffer, is_dir, mode ))
+       {
+               LOG(log_debug9, logtype_afpd, "Coalesced fc event <%d> for <%s>", mode, full_path_buffer );
+               return AFP_OK;
+       }
+
+       LOG(log_debug9, logtype_afpd, "Detected fc event <%d> for <%s>", mode, full_path_buffer );
+
+    if (mode & FCE_FILE_MODIFY) {
+        save_close_event(full_path_buffer);
+        return AFP_OK;
+    }
+
+    send_fce_event( full_path_buffer, mode );
+
+    return AFP_OK;
+}
+
+static void check_saved_close_events(int fmodwait)
+{
+    time_t now = time(NULL);
+
+    /* check if configured holdclose time has passed */
+    if (last_close_event.time && ((last_close_event.time + fmodwait) < now)) {
+        LOG(log_debug, logtype_afpd, "check_saved_close_events: sending event: %s", last_close_event.path);
+        /* yes, send event */
+        send_fce_event(&last_close_event.path[0], FCE_FILE_MODIFY);
+        last_close_event.path[0] = 0;
+        last_close_event.time = 0;
+    }
+}
+
+/******************** External calls start here **************************/
+
+/*
+ * API-Calls for file change api, called form outside (file.c directory.c ofork.c filedir.c)
+ * */
+#ifndef FCE_TEST_MAIN
+
+void fce_pending_events(AFPObj *obj)
+{
+    vol_fce_tm_event();
+    check_saved_close_events(obj->options.fce_fmodwait);
+}
+
+int fce_register_delete_file( struct path *path )
+{
+    int ret = AFP_OK;
+
+    if (path == NULL)
+        return AFPERR_PARAM;
+
+    if (!(fce_ev_enabled & (1 << FCE_FILE_DELETE)))
+        return ret;
+       
+    ret = register_fce( path->u_name, FALSE, FCE_FILE_DELETE );
+
+    return ret;
+}
+int fce_register_delete_dir( char *name )
+{
+    int ret = AFP_OK;
+
+    if (name == NULL)
+        return AFPERR_PARAM;
+
+    if (!(fce_ev_enabled & (1 << FCE_DIR_DELETE)))
+        return ret;
+       
+    ret = register_fce( name, TRUE, FCE_DIR_DELETE);
+
+    return ret;
+}
+
+int fce_register_new_dir( struct path *path )
+{
+    int ret = AFP_OK;
+
+    if (path == NULL)
+        return AFPERR_PARAM;
+
+    if (!(fce_ev_enabled & (1 << FCE_DIR_CREATE)))
+        return ret;
+
+    ret = register_fce( path->u_name, TRUE, FCE_DIR_CREATE );
+
+    return ret;
+}
+
+
+int fce_register_new_file( struct path *path )
+{
+    int ret = AFP_OK;
+
+    if (path == NULL)
+        return AFPERR_PARAM;
+
+    if (!(fce_ev_enabled & (1 << FCE_FILE_CREATE)))
+        return ret;
+
+    ret = register_fce( path->u_name, FALSE, FCE_FILE_CREATE );
+
+    return ret;
+}
+
+int fce_register_file_modification( struct ofork *ofork )
+{
+    char *u_name = NULL;
+    struct vol *vol;
+    int ret = AFP_OK;
+
+    if (ofork == NULL || ofork->of_vol == NULL)
+        return AFPERR_PARAM;
+
+    if (!(fce_ev_enabled & (1 << FCE_FILE_MODIFY)))
+        return ret;
+
+    vol = ofork->of_vol;
+
+    if (NULL == (u_name = mtoupath(vol, of_name(ofork), ofork->of_did, utf8_encoding()))) 
+    {
+        return AFPERR_MISC;
+    }
+    
+    ret = register_fce( u_name, FALSE, FCE_FILE_MODIFY );
+    
+    return ret;    
+}
+
+int fce_register_tm_size(const char *vol, size_t used)
+{
+    int ret = AFP_OK;
+
+    if (vol == NULL)
+        return AFPERR_PARAM;
+
+    if (!(fce_ev_enabled & (1 << FCE_TM_SIZE)))
+        return ret;
+
+    tm_used = used;             /* oh what a hack */
+    ret = register_fce(vol, FALSE, FCE_TM_SIZE);
+
+    return ret;
+}
+#endif
+
+/*
+ *
+ * Extern connect to afpd parameter, can be called multiple times for multiple listeners (up to MAX_UDP_SOCKS times)
+ *
+ * */
+int fce_add_udp_socket(const char *target)
+{
+       const char *port = FCE_DEFAULT_PORT_STRING;
+       char target_ip[256] = {""};
+
+       strncpy(target_ip, target, sizeof(target_ip) -1);
+
+       char *port_delim = strchr( target_ip, ':' );
+       if (port_delim) {
+               *port_delim = 0;
+               port = port_delim + 1;
+       }
+       return add_udp_socket(target_ip, port);
+}
+
+int fce_set_events(const char *events)
+{
+    char *e;
+    char *p;
+    
+    if (events == NULL)
+        return AFPERR_PARAM;
+
+    e = strdup(events);
+
+    fce_ev_enabled = 0;
+
+    for (p = strtok(e, ","); p; p = strtok(NULL, ",")) {
+        if (strcmp(p, "fmod") == 0) {
+            fce_ev_enabled |= (1 << FCE_FILE_MODIFY);
+        } else if (strcmp(p, "fdel") == 0) {
+            fce_ev_enabled |= (1 << FCE_FILE_DELETE);
+        } else if (strcmp(p, "ddel") == 0) {
+            fce_ev_enabled |= (1 << FCE_DIR_DELETE);
+        } else if (strcmp(p, "fcre") == 0) {
+            fce_ev_enabled |= (1 << FCE_FILE_CREATE);
+        } else if (strcmp(p, "dcre") == 0) {
+            fce_ev_enabled |= (1 << FCE_DIR_CREATE);
+        } else if (strcmp(p, "tmsz") == 0) {
+            fce_ev_enabled |= (1 << FCE_TM_SIZE);
+        }
+    }
+
+    free(e);
+}
+
+#ifdef FCE_TEST_MAIN
+
+
+void shortsleep( unsigned int us )
+{    
+    usleep( us );
+}
+int main( int argc, char*argv[] )
+{
+    int c,ret;
+
+    char *port = FCE_DEFAULT_PORT_STRING;
+    char *host = "localhost";
+    int delay_between_events = 1000;
+    int event_code = FCE_FILE_MODIFY;
+    char pathbuff[1024];
+    int duration_in_seconds = 0; // TILL ETERNITY
+    char target[256];
+    char *path = getcwd( pathbuff, sizeof(pathbuff) );
+
+    // FULLSPEED TEST IS "-s 1001" -> delay is 0 -> send packets without pause
+
+    while ((c = getopt(argc, argv, "d:e:h:p:P:s:")) != -1) {
+        switch(c) {
+        case '?':
+            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]);
+            exit(1);
+            break;
+        case 'd':
+            duration_in_seconds = atoi(optarg);
+            break;
+        case 'e':
+            event_code = atoi(optarg);
+            break;
+        case 'h':
+            host = strdup(optarg);
+            break;
+        case 'p':
+            port = strdup(optarg);
+            break;
+        case 'P':
+            path = strdup(optarg);
+            break;
+        case 's':
+            delay_between_events = atoi(optarg);
+            break;
+        }
+    }
+
+    sprintf(target, "%s:%s", host, port);
+    if (fce_add_udp_socket(target) != 0)
+        return 1;
+
+    int ev_cnt = 0;
+    time_t start_time = time(NULL);
+    time_t end_time = 0;
+
+    if (duration_in_seconds)
+        end_time = start_time + duration_in_seconds;
+
+    while (1)
+    {
+        time_t now = time(NULL);
+        if (now > start_time)
+        {
+            start_time = now;
+            fprintf( stdout, "%d events/s\n", ev_cnt );
+            ev_cnt = 0;
+        }
+        if (end_time && now >= end_time)
+            break;
+
+        register_fce( path, 0, event_code );
+        ev_cnt++;
+
+        
+        shortsleep( delay_between_events );
+    }
+}
+#endif /* TESTMAIN*/
index ef3255e004a6dade81e675509de0fd81a9dbf2b0..ff92fdc0187024cbed69307b81134c2466ab8340 100644 (file)
@@ -1,51 +1,51 @@
-/* \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
-#define FCE_COALESCE_CREATE (1 << 0)\r
-#define FCE_COALESCE_DELETE (1 << 1)\r
-#define FCE_COALESCE_ALL    (FCE_COALESCE_CREATE | FCE_COALESCE_DELETE)\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
-struct fce_close_event {\r
-    time_t time;\r
-       char path[MAXPATHLEN + 1];\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
+/* 
+ * File:   fce_api_internal.h
+ * Author: mw
+ *
+ * Created on 1. Oktober 2010, 23:48
+ */
+
+#ifndef _FCE_API_INTERNAL_H
+#define        _FCE_API_INTERNAL_H
+
+#define FCE_MAX_UDP_SOCKS 5     /* Allow a maximum of udp listeners for file change events */
+#define FCE_SOCKET_RETRY_DELAY_S 600 /* Pause this time in s after socket was broken */
+#define FCE_PACKET_VERSION  1
+#define FCE_HISTORY_LEN 10  /* This is used to coalesce events */
+#define MAX_COALESCE_TIME_MS 1000  /* Events oldeer than this are not coalesced */
+
+#define FCE_COALESCE_CREATE (1 << 0)
+#define FCE_COALESCE_DELETE (1 << 1)
+#define FCE_COALESCE_ALL    (FCE_COALESCE_CREATE | FCE_COALESCE_DELETE)
+
+struct udp_entry
+{
+    int sock;
+    char *addr;
+    char *port;
+    struct addrinfo addrinfo;
+    struct sockaddr_storage sockaddr;
+    time_t next_try_on_error;      /* In case of error set next timestamp to retry */
+};
+
+struct fce_history
+{
+    unsigned char mode;
+       int is_dir;
+       char path[MAXPATHLEN + 1];
+       struct timeval tv;
+};
+
+struct fce_close_event {
+    time_t time;
+       char path[MAXPATHLEN + 1];
+};
+
+#define PACKET_HDR_LEN (sizeof(struct fce_packet) - FCE_MAX_PATH_LEN)
+
+int fce_handle_coalescation( char *path, int is_dir, int mode );
+void fce_initialize_history();
+
+
+#endif /* _FCE_API_INTERNAL_H */
+
index f185530531d2c795ab633517cb7ff6b8f01e3b65..ad6026f79a9e5aabdeb6746380009250d5d3eb94 100644 (file)
-/*\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
-#include <atalk/globals.h>\r
-\r
-#include "fork.h"\r
-#include "file.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 uint32_t coalesce = 0;\r
-static struct fce_history fce_history_list[FCE_HISTORY_LEN];\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
-\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
-/******************************************************************************\r
- * Public functions follow\r
- ******************************************************************************/\r
-\r
-void fce_initialize_history()\r
-{\r
-       for (int i = 0; i < FCE_HISTORY_LEN; i++) {\r
-               memset( &fce_history_list[i], 0, sizeof(fce_history_list[i]) );\r
-       }\r
-}\r
-\r
-int fce_handle_coalescation( char *path, int is_dir, int mode )\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
-       struct timeval tv;\r
-\r
-       if (coalesce == 0)\r
-               return FALSE;\r
-\r
-       /* After a file creation *ALWAYS* a file modification is produced */\r
-       if ((mode == FCE_FILE_CREATE) && (coalesce & FCE_COALESCE_CREATE))\r
-        return TRUE;\r
-\r
-       /* get timestamp */\r
-       gettimeofday(&tv, 0);\r
-\r
-       /* Now detect events in the very near history */\r
-       for (int i = 0; i < FCE_HISTORY_LEN; i++) {\r
-               struct fce_history *fh = &fce_history_list[i];\r
-\r
-               /* Not inited ? */\r
-               if (fh->tv.tv_sec == 0) {\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
-                       /* Invalidate entry */\r
-                       fh->tv.tv_sec = 0;\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 & FCE_COALESCE_CREATE) && (fh->mode == FCE_DIR_CREATE)) {\r
-                       /* Parent dir ? */\r
-                       if (!strncmp(fh->path, path, strlen(fh->path)))\r
-                               return TRUE;\r
-               }\r
-\r
-               /* If we find a parent dir we should be DELETED we are done */\r
-               if ((coalesce & FCE_COALESCE_DELETE)\r
-            && fh->is_dir\r
-            && (mode == FCE_FILE_DELETE || mode == FCE_DIR_DELETE)) {\r
-                       /* Parent dir ? */\r
-                       if (!strncmp(fh->path, path, strlen(fh->path)))\r
-                               return TRUE;\r
-               }\r
-\r
-               /* Detect oldest entry for next new entry */\r
-               if (oldest_entry_idx == -1 || fh->tv.tv_sec < oldest_entry) {\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
- * Set event coalescation to reduce number of events sent over UDP \r
- * all|delete|create\r
- */\r
-\r
-int fce_set_coalesce(char *opt)\r
-{\r
-    char *e;\r
-    char *p;\r
-    \r
-    if (opt == NULL)\r
-        return AFPERR_PARAM;\r
-\r
-    e = strdup(opt);\r
-\r
-    for (p = strtok(e, ","); p; p = strtok(NULL, ",")) {\r
-        if (strcmp(p, "all") == 0) {\r
-            coalesce = FCE_COALESCE_ALL;\r
-        } else if (strcmp(p, "delete") == 0) {\r
-            coalesce = FCE_COALESCE_DELETE;\r
-        } else if (strcmp(p, "create") == 0) {\r
-            coalesce = FCE_COALESCE_CREATE;\r
-        }\r
-    }\r
-\r
-    free(e);\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 <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <netatalk/at.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"
+
+#define FCE_TRUE 1
+#define FCE_FALSE 0
+
+/* 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]) );
+       }
+}
+
+int fce_handle_coalescation( char *path, int is_dir, int mode )
+{
+       /* 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 ((mode == 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->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->tv, &tv ) > MAX_COALESCE_TIME_MS) {
+                       /* Invalidate entry */
+                       fh->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->mode == FCE_DIR_CREATE)) {
+                       /* Parent dir ? */
+                       if (!strncmp(fh->path, path, strlen(fh->path)))
+                               return TRUE;
+               }
+
+               /* If we find a parent dir we should be DELETED we are done */
+               if ((coalesce & FCE_COALESCE_DELETE)
+            && fh->is_dir
+            && (mode == FCE_FILE_DELETE || mode == FCE_DIR_DELETE)) {
+                       /* Parent dir ? */
+                       if (!strncmp(fh->path, path, strlen(fh->path)))
+                               return TRUE;
+               }
+
+               /* Detect oldest entry for next new entry */
+               if (oldest_entry_idx == -1 || fh->tv.tv_sec < oldest_entry) {
+                       oldest_entry = fh->tv.tv_sec;
+                       oldest_entry_idx = i;
+               }
+       }
+
+       /* We have a new entry for the history, register it */
+       fce_history_list[oldest_entry_idx].tv = tv;
+       fce_history_list[oldest_entry_idx].mode = mode;
+       fce_history_list[oldest_entry_idx].is_dir = is_dir;
+       strncpy( fce_history_list[oldest_entry_idx].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(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);
+}
+
+
+
+