From: Ralph Boehme Date: Tue, 15 Jul 2014 23:16:33 +0000 (+0200) Subject: fce: FCE version 2 with new event types and new config options X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=commitdiff_plain;h=d3dff4ba4b8db3131a16641d35a6554be5fb5160 fce: FCE version 2 with new event types and new config options * new events: fmov,dmov,login and logout * removed event: tmsz * new options: "fce ignore names" and "fce notify script" Signed-off-by: Ralph Boehme --- diff --git a/NEWS b/NEWS index 212272de..0ff59489 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ Changes in 3.1.4 connections, bug #572. * NEW: afpd: new option "force xattr with sticky bit = yes|no" (default: no), FR #94 +* UPD: afpd: FCE version 2 with new event types and new config options + "fce ignore names" and "fce notify script" Changes in 3.1.3 ================ diff --git a/bin/misc/fce.c b/bin/misc/fce.c index 23101bfb..cd767e9a 100644 --- a/bin/misc/fce.c +++ b/bin/misc/fce.c @@ -27,52 +27,103 @@ static char *fce_ev_names[] = { "FCE_FILE_DELETE", "FCE_DIR_DELETE", "FCE_FILE_CREATE", - "FCE_DIR_CREATE" + "FCE_DIR_CREATE", + "FCE_FILE_MOVE", + "FCE_DIR_MOVE", + "FCE_LOGIN", + "FCE_LOGOUT" }; static int unpack_fce_packet(unsigned char *buf, struct fce_packet *packet) { unsigned char *p = buf; + uint16_t uint16; + uint32_t uint32; + uint64_t uint64; - memcpy(&packet->magic[0], p, sizeof(packet->magic)); - p += sizeof(packet->magic); + memcpy(&packet->fcep_magic[0], p, sizeof(packet->fcep_magic)); + p += sizeof(packet->fcep_magic); - packet->version = *p; - p++; + packet->fcep_version = *p++; - packet->mode = *p; - p++; + if (packet->fcep_version > 1) + packet->fcep_options = *p++; - memcpy(&packet->event_id, p, sizeof(packet->event_id)); - p += sizeof(packet->event_id); - packet->event_id = ntohl(packet->event_id); + packet->fcep_event = *p++; - memcpy(&packet->datalen, p, sizeof(packet->datalen)); - p += sizeof(packet->datalen); - packet->datalen = ntohs(packet->datalen); + if (packet->fcep_version > 1) + /* padding */ + p++; - memcpy(&packet->data[0], p, packet->datalen); - packet->data[packet->datalen] = 0; /* 0 terminate strings */ - p += packet->datalen; + if (packet->fcep_version > 1) + /* reserved */ + p += 8; + + memcpy(&packet->fcep_event_id, p, sizeof(packet->fcep_event_id)); + p += sizeof(packet->fcep_event_id); + packet->fcep_event_id = ntohl(packet->fcep_event_id); + + if (packet->fcep_options & FCE_EV_INFO_PID) { + memcpy(&packet->fcep_pid, p, sizeof(packet->fcep_pid)); + packet->fcep_pid = hton64(packet->fcep_pid); + p += sizeof(packet->fcep_pid); + } + + if (packet->fcep_options & FCE_EV_INFO_USER) { + memcpy(&packet->fcep_userlen, p, sizeof(packet->fcep_userlen)); + packet->fcep_userlen = ntohs(packet->fcep_userlen); + p += sizeof(packet->fcep_userlen); + + memcpy(&packet->fcep_user[0], p, packet->fcep_userlen); + packet->fcep_user[packet->fcep_userlen] = 0; /* 0 terminate strings */ + p += packet->fcep_userlen; + } + + /* path */ + memcpy(&packet->fcep_pathlen1, p, sizeof(packet->fcep_pathlen1)); + p += sizeof(packet->fcep_pathlen1); + packet->fcep_pathlen1 = ntohs(packet->fcep_pathlen1); + + memcpy(&packet->fcep_path1[0], p, packet->fcep_pathlen1); + packet->fcep_path1[packet->fcep_pathlen1] = 0; /* 0 terminate strings */ + p += packet->fcep_pathlen1; + + if (packet->fcep_options & FCE_EV_INFO_SRCPATH) { + memcpy(&packet->fcep_pathlen2, p, sizeof(packet->fcep_pathlen2)); + p += sizeof(packet->fcep_pathlen2); + packet->fcep_pathlen2 = ntohs(packet->fcep_pathlen2); + memcpy(&packet->fcep_path2[0], p, packet->fcep_pathlen2); + packet->fcep_path2[packet->fcep_pathlen2] = 0; /* 0 terminate strings */ + p += packet->fcep_pathlen2; + } return 0; } -int main(void) +int main(int argc, char **argv) { - int sockfd; + int sockfd, rv, c; 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]; + char *host = "localhost"; + + while ((c = getopt(argc, argv, "h:")) != -1) { + switch(c) { + case 'h': + host = strdup(optarg); + break; + } + } 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) { + if ((rv = getaddrinfo(host, FCE_DEFAULT_PORT_STRING, &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); return 1; } @@ -119,9 +170,9 @@ int main(void) unpack_fce_packet((unsigned char *)buf, &packet); - if (memcmp(packet.magic, FCE_PACKET_MAGIC, sizeof(packet.magic)) == 0) { + if (memcmp(packet.fcep_magic, FCE_PACKET_MAGIC, sizeof(packet.fcep_magic)) == 0) { - switch (packet.mode) { + switch (packet.fcep_event) { case FCE_CONN_START: printf("FCE Start\n"); break; @@ -131,8 +182,16 @@ int main(void) break; default: - printf("ID: %" PRIu32 ", Event: %s, Path: %s\n", - packet.event_id, fce_ev_names[packet.mode], packet.data); + printf("ID: %" PRIu32 ", Event: %s", packet.fcep_event_id, fce_ev_names[packet.fcep_event]); + if (packet.fcep_options & FCE_EV_INFO_PID) + printf(", pid: %" PRId64, packet.fcep_pid); + if (packet.fcep_options & FCE_EV_INFO_USER) + printf(", user: %s", packet.fcep_user); + + if (packet.fcep_options & FCE_EV_INFO_SRCPATH) + printf(", source: %s", packet.fcep_path2); + + printf(", Path: %s\n", packet.fcep_path1); break; } } diff --git a/contrib/shell_utils/Makefile.am b/contrib/shell_utils/Makefile.am index dc4c7db7..a8513227 100644 --- a/contrib/shell_utils/Makefile.am +++ b/contrib/shell_utils/Makefile.am @@ -20,4 +20,4 @@ CLEANFILES = $(GENERATED_FILES) bin_SCRIPTS = $(PERLSCRIPTS) $(GENERATED_FILES) afpstats -EXTRA_DIST = $(TEMPLATE_FILES) make-casetable.pl make-precompose.h.pl afpstats +EXTRA_DIST = $(TEMPLATE_FILES) make-casetable.pl make-precompose.h.pl afpstats fce_ev_script.sh diff --git a/contrib/shell_utils/fce_ev_script.sh b/contrib/shell_utils/fce_ev_script.sh new file mode 100644 index 00000000..8e3410c9 --- /dev/null +++ b/contrib/shell_utils/fce_ev_script.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +usage="$(basename $0) [-h] [-v version] [-e event] [-P path] [-S source path] -- FCE sample script + +where: + -h show this help text + -v version + -e event + -P path + -S source path for events like rename/move + -u username + -p pid + -i event ID +" + +while getopts ':hs:v:e:P:S:u:p:i:' option; do + case "$option" in + h) echo "$usage" + exit + ;; + v) version=$OPTARG + ;; + e) event=$OPTARG + ;; + P) path=$OPTARG + ;; + S) srcpath=$OPTARG + ;; + u) user=$OPTARG + ;; + p) pid=$OPTARG + ;; + i) evid=$OPTARG + ;; + ?) printf "illegal option: '%s'\n" "$OPTARG" >&2 + echo "$usage" >&2 + exit 1 + ;; + esac +done +shift $((OPTIND - 1)) + +printf "FCE Event: $event" >> /tmp/fce.log +if [ -n "$version" ] ; then + printf ", protocol: $version" >> /tmp/fce.log +fi +if [ -n "$evid" ] ; then + printf ", ID: $evid" >> /tmp/fce.log +fi +if [ -n "$pid" ] ; then + printf ", pid: $pid" >> /tmp/fce.log +fi +if [ -n "$user" ] ; then + echo -n ", user: $user" >> /tmp/fce.log +fi +if [ -n "$srcpath" ] ; then + echo -n ", source: $srcpath" >> /tmp/fce.log +fi +if [ -n "$path" ] ; then + echo -n ", path: $path" >> /tmp/fce.log +fi +printf "\n" >> /tmp/fce.log diff --git a/doc/manpages/man5/afp.conf.5.xml b/doc/manpages/man5/afp.conf.5.xml index 44e083c2..5842c65c 100644 --- a/doc/manpages/man5/afp.conf.5.xml +++ b/doc/manpages/man5/afp.conf.5.xml @@ -1214,6 +1214,20 @@ afpd processes notify interested listeners about certain filesystem event by UDP network datagrams. + The following FCE events are defined: + + + file modification () + file deletion () + directory deletion () + file creation () + directory creation () + file move or rename () + directory move or rename () + login () + logout () + + fce listener = host[:port] @@ -1229,7 +1243,7 @@ fce events = - fmod,fdel,ddel,fcre,dcre,tmsz + fmod,fdel,ddel,fcre,dcre,fmov,dmov,login,logout (G) @@ -1261,6 +1275,28 @@ seconds. + + + fce ignore names = NAME[/NAME2/...] + (G) + + + Slash delimited list of filenames for which FCE + events shall not be generated. Default: .DS_Store. + + + + + fce notify script = PATH + (G) + + + Script which will be executed for every FCE event, + see contrib/shell_utils/fce_ev_script.shfrom the Netatalk + sources for an example script. + + + diff --git a/etc/afpd/Makefile.am b/etc/afpd/Makefile.am index a0b210e1..77ed9d64 100644 --- a/etc/afpd/Makefile.am +++ b/etc/afpd/Makefile.am @@ -7,7 +7,7 @@ CLEANFILES = DISTCLEANFILES = sbin_PROGRAMS = afpd -noinst_PROGRAMS = hash fce spot +noinst_PROGRAMS = hash spot afpd_SOURCES = \ afp_avahi.c \ @@ -102,10 +102,6 @@ noinst_HEADERS = auth.h afp_config.h desktop.h directory.h fce_api_internal.h fi 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 - spot_SOURCES = spotlight.c spotlight_marshalling.c spot_CFLAGS = -DSPOT_TEST_MAIN spot_LDADD = $(top_builddir)/libatalk/libatalk.la diff --git a/etc/afpd/afp_config.c b/etc/afpd/afp_config.c index c7246f1f..ba5b232b 100644 --- a/etc/afpd/afp_config.c +++ b/etc/afpd/afp_config.c @@ -225,6 +225,19 @@ int configinit(AFPObj *obj) LOG(log_note, logtype_afpd, "Fce events: %s", r); fce_set_events(r); } + r = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce version", "1"); + LOG(log_debug, logtype_afpd, "Fce version: %s", r); + obj->fce_version = atoi(r); + + if ((r = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce ignore names", ".DS_Store"))) { + obj->fce_ign_names = strdup(r); + } + + if ((r = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce notify script", NULL))) { + obj->fce_notify_script = strdup(r); + } + + EC_CLEANUP: if (q) diff --git a/etc/afpd/afp_dsi.c b/etc/afpd/afp_dsi.c index 697aa435..eb7f796e 100644 --- a/etc/afpd/afp_dsi.c +++ b/etc/afpd/afp_dsi.c @@ -337,6 +337,11 @@ static void alarm_handler(int sig _U_) } } +static void child_handler(void) +{ + wait(NULL); +} + /* ----------------- if dsi->in_write is set attention, tickle (and close?) msg aren't sent. We don't care about tickle @@ -428,6 +433,17 @@ void afp_over_dsi_sighandlers(AFPObj *obj) afp_dsi_die(EXITERR_SYS); } #endif /* DEBUGGING */ + + /* SIGCLD */ + action.sa_handler = child_handler; +#ifdef SA_NOCLDWAIT +/* this enhancement simplifies things for Solaris, it also improves performance */ + action.sa_flags |= SA_NOCLDWAIT; +#endif + if (sigaction(SIGCLD, &action, NULL) < 0 ) { + LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) ); + afp_dsi_die(EXITERR_SYS); + } } /* ------------------------------------------- diff --git a/etc/afpd/afprun.c b/etc/afpd/afprun.c index 74a732e6..ed412476 100644 --- a/etc/afpd/afprun.c +++ b/etc/afpd/afprun.c @@ -259,3 +259,62 @@ int afprun(int root, char *cmd, int *outfd) exit(82); return 1; } + +/* + * Run a command in the background without waiting, + * being careful about uid/gid handling + */ +int afprun_bg(int root, char *cmd) +{ + pid_t pid; + uid_t uid = geteuid(); + gid_t gid = getegid(); + int fd, fdlimit = sysconf(_SC_OPEN_MAX); + + LOG(log_debug, logtype_afpd, "running %s as user %d", cmd, root ? 0 : uid); + + /* in this method we will exec /bin/sh with the correct + arguments, after first setting stdout to point at the file */ + + if ((pid = fork()) < 0) { + LOG(log_error, logtype_afpd, "afprun: fork failed with error %s", strerror(errno) ); + return errno; + } + + if (pid) + /* parent, just return */ + return 0; + + /* we are in the child. we exec /bin/sh to do the work for us. we + don't directly exec the command we want because it may be a + pipeline or anything else the config file specifies */ + + if (chdir("/") < 0) { + LOG(log_error, logtype_afpd, "afprun: can't change directory to \"/\" %s", strerror(errno) ); + exit(83); + } + + /* now completely lose our privileges. This is a fairly paranoid + way of doing it, but it does work on all systems that I know of */ + if (root) { + become_user_permanently(0, 0); + uid = gid = 0; + } else { + become_user_permanently(uid, gid); + } + + if (getuid() != uid || geteuid() != uid || getgid() != gid || getegid() != gid) { + /* we failed to lose our privileges - do not execute the command */ + exit(81); + } + + fd = 3; + while (fd < fdlimit) + close(fd++); + + execl("/bin/sh","sh","-c", cmd, NULL); + + /* not reached */ + exit(82); + return 1; +} diff --git a/etc/afpd/auth.c b/etc/afpd/auth.c index 8011b56b..7a7c41e5 100644 --- a/etc/afpd/auth.c +++ b/etc/afpd/auth.c @@ -39,6 +39,7 @@ extern void afp_get_cmdline( int *ac, char ***av ); #include #include #include +#include #include #include @@ -313,6 +314,9 @@ static int login(AFPObj *obj, struct passwd *pwd, void (*logout)(void), int expi /* Some PAM module might have reset our signal handlers and timer, so we need to reestablish them */ afp_over_dsi_sighandlers(obj); + /* Send FCE login event */ + fce_register(obj, FCE_LOGIN, "", NULL); + return( AFP_OK ); } @@ -831,6 +835,10 @@ int afp_logout(AFPObj *obj, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_ close_all_vol(obj); dsi->flags = DSI_AFP_LOGGED_OUT; *rbuflen = 0; + + /* Send FCE login event */ + fce_register(obj, FCE_LOGOUT, "", NULL); + return AFP_OK; } diff --git a/etc/afpd/directory.c b/etc/afpd/directory.c index 07a2bb7d..e83e5e76 100644 --- a/etc/afpd/directory.c +++ b/etc/afpd/directory.c @@ -2214,7 +2214,7 @@ 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(FCE_DIR_CREATE, bdata(curdir->d_fullpath), NULL, fce_dir); + fce_register(obj, FCE_DIR_CREATE, bdata(curdir->d_fullpath), NULL); ad_flush(&ad); ad_close(&ad, ADFLAGS_HF); diff --git a/etc/afpd/fce_api.c b/etc/afpd/fce_api.c index 46203789..8c7f4849 100644 --- a/etc/afpd/fce_api.c +++ b/etc/afpd/fce_api.c @@ -56,6 +56,8 @@ // ONLY USED IN THIS FILE #include "fce_api_internal.h" +extern int afprun_bg(int root, char *cmd); + /* We store our connection data here */ static struct udp_entry udp_socket_list[FCE_MAX_UDP_SOCKS]; static int udp_sockets = 0; @@ -65,15 +67,17 @@ static unsigned long fce_ev_enabled = (1 << FCE_FILE_DELETE) | (1 << FCE_DIR_DELETE) | (1 << FCE_FILE_CREATE) | - (1 << FCE_DIR_CREATE); + (1 << FCE_DIR_CREATE) | + (1 << FCE_FILE_MOVE) | + (1 << FCE_DIR_MOVE) | + (1 << FCE_LOGIN) | + (1 << FCE_LOGOUT); + +static uint8_t fce_ev_info; /* flags of additional info to send in events */ -#define MAXIOBUF 1024 +#define MAXIOBUF 4096 static unsigned char iobuf[MAXIOBUF]; -static const char *skip_files[] = -{ - ".DS_Store", - NULL -}; +static const char **skip_files; static struct fce_close_event last_close_event; static char *fce_event_names[] = { @@ -82,7 +86,11 @@ static char *fce_event_names[] = { "FCE_FILE_DELETE", "FCE_DIR_DELETE", "FCE_FILE_CREATE", - "FCE_DIR_CREATE" + "FCE_DIR_CREATE", + "FCE_FILE_MOVE", + "FCE_DIR_MOVE", + "FCE_LOGIN", + "FCE_LOGOUT" }; /* @@ -161,98 +169,201 @@ void fce_cleanup() /* * Construct a UDP packet for our listeners and return packet size * */ -static ssize_t build_fce_packet( struct fce_packet *packet, const char *path, int event, uint32_t event_id ) +static ssize_t build_fce_packet(const AFPObj *obj, + char *iobuf, + fce_ev_t event, + const char *path, + const char *oldpath, + pid_t pid, + const char *user, + uint32_t event_id) { - size_t pathlen = 0; - ssize_t data_len = 0; - - /* Set content of packet */ - memcpy(packet->magic, FCE_PACKET_MAGIC, sizeof(packet->magic) ); - packet->version = FCE_PACKET_VERSION; - packet->mode = event; - - packet->event_id = event_id; - - pathlen = strlen(path); /* exclude 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; - - packet->datalen = pathlen; - - /* This is the payload len. Means: the packet has len bytes more until packet is finished */ - data_len = FCE_PACKET_HEADER_SIZE + pathlen; - - memcpy(packet->data, path, pathlen); + char *p = iobuf; + size_t pathlen; + ssize_t datalen = 0; + uint16_t uint16; + uint32_t uint32; + uint64_t uint64; + uint8_t packet_info = fce_ev_info; + + /* FCE magic */ + memcpy(p, FCE_PACKET_MAGIC, 8); + p += 8; + datalen += 8; + + /* version */ + *p = FCE_PACKET_VERSION; + p += 1; + datalen += 1; + + /* optional: options */ + if (FCE_PACKET_VERSION > 1) { + if (oldpath) + packet_info |= FCE_EV_INFO_SRCPATH; + *p = packet_info; + p += 1; + datalen += 1; + } - /* return the packet len */ - return data_len; -} + /* event */ + *p = event; + p += 1; + datalen += 1; -/* - * Handle Endianess and write into buffer w/o padding - **/ -static void pack_fce_packet(struct fce_packet *packet, unsigned char *buf, int maxlen) -{ - unsigned char *p = buf; + /* optional: padding */ + if (FCE_PACKET_VERSION > 1) { + p += 1; + datalen += 1; + } - memcpy(p, &packet->magic[0], sizeof(packet->magic)); - p += sizeof(packet->magic); + /* optional: reserved */ + if (FCE_PACKET_VERSION > 1) { + p += 8; + datalen += 8; + } - *p = packet->version; - p++; - - *p = packet->mode; - p++; - - uint32_t *id = (uint32_t*)p; - *id = htonl(packet->event_id); - p += sizeof(packet->event_id); + /* event ID */ + uint32 = htonl(event_id); + memcpy(p, &uint32, sizeof(uint32)); + p += sizeof(uint32); + datalen += sizeof(uint32); + + /* optional: pid */ + if (packet_info & FCE_EV_INFO_PID) { + uint64 = pid; + uint64 = hton64(uint64); + memcpy(p, &uint64, sizeof(uint64)); + p += sizeof(uint64); + datalen += sizeof(uint64); + } - uint16_t *l = ( uint16_t *)p; - *l = htons(packet->datalen); - p += sizeof(packet->datalen); + /* optional: username */ + if (packet_info & FCE_EV_INFO_USER) { + uint16 = strlen(user); + uint16 = htons(uint16); + memcpy(p, &uint16, sizeof(uint16)); + p += sizeof(uint16); + datalen += sizeof(uint16); + memcpy(p, user, strlen(user)); + p += strlen(user); + datalen += strlen(user); + } - if (((p - buf) + packet->datalen) < maxlen) { - memcpy(p, &packet->data[0], packet->datalen); + /* path */ + if ((pathlen = strlen(path)) >= MAXPATHLEN) + pathlen = MAXPATHLEN - 1; + uint16 = pathlen; + uint16 = htons(uint16); + memcpy(p, &uint16, sizeof(uint16)); + p += sizeof(uint16); + datalen += sizeof(uint16); + memcpy(p, path, pathlen); + p += pathlen; + datalen += pathlen; + + /* optional: source path */ + if (packet_info & FCE_EV_INFO_SRCPATH) { + if ((pathlen = strlen(oldpath)) >= MAXPATHLEN) + pathlen = MAXPATHLEN - 1; + uint16 = pathlen; + uint16 = htons(uint16); + memcpy(p, &uint16, sizeof(uint16)); + p += sizeof(uint16); + datalen += sizeof(uint16); + memcpy(p, oldpath, pathlen); + p += pathlen; + datalen += pathlen; } + + /* return the packet len */ + return datalen; } /* * 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(const char *path, int event) +static void send_fce_event(const AFPObj *obj, int event, const char *path, const char *oldpath) { static bool first_event = true; - - struct fce_packet 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 */ + static char *user; time_t now = time(NULL); - - LOG(log_debug, logtype_fce, "send_fce_event: start"); + ssize_t data_len; /* initialized ? */ if (first_event == true) { first_event = false; + + struct passwd *pwd = getpwuid(obj->uid); + user = strdup(pwd->pw_name); + + switch (obj->fce_version) { + case 1: + /* fce_ev_info unused */ + break; + case 2: + fce_ev_info = FCE_EV_INFO_PID | FCE_EV_INFO_USER; + break; + default: + fce_ev_info = 0; + LOG(log_error, logtype_fce, "Unsupported FCE protocol version %d", obj->fce_version); + break; + } + fce_init_udp(); /* Notify listeners the we start from the beginning */ - send_fce_event( "", FCE_CONN_START ); + send_fce_event(obj, FCE_CONN_START, "", NULL); } - /* build our data packet */ - ssize_t data_len = build_fce_packet( &packet, path, event, ++event_id ); - pack_fce_packet(&packet, iobuf, MAXIOBUF); + /* run script */ + if (obj->fce_notify_script) { + static bstring quote = NULL; + static bstring quoterep = NULL; + static bstring slash = NULL; + static bstring slashrep = NULL; + + if (!quote) { + quote = bfromcstr("'"); + quoterep = bfromcstr("'\\''"); + slash = bfromcstr("\\"); + slashrep = bfromcstr("\\\\"); + } - for (int i = 0; i < udp_sockets; i++) - { + bstring cmd = bformat("%s -v %d -e %s -i %" PRIu32 "", + obj->fce_notify_script, + FCE_PACKET_VERSION, + fce_event_names[event], + event_id); + + if (path[0]) { + bstring bpath = bfromcstr(path); + bfindreplace(bpath, slash, slashrep, 0); + bfindreplace(bpath, quote, quoterep, 0); + bformata(cmd, " -P '%s'", bdata(bpath)); + bdestroy(bpath); + } + if (fce_ev_info | FCE_EV_INFO_PID) + bformata(cmd, " -p %" PRIu64 "", (uint64_t)getpid()); + if (fce_ev_info | FCE_EV_INFO_USER) + bformata(cmd, " -u %s", user); + if (oldpath) { + bstring boldpath = bfromcstr(oldpath); + bfindreplace(boldpath, slash, slashrep, 0); + bfindreplace(boldpath, quote, quoterep, 0); + bformata(cmd, " -S '%s'", bdata(boldpath)); + bdestroy(boldpath); + } + (void)afprun_bg(1, bdata(cmd)); + bdestroy(cmd); + } + + 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) - { + if (udp_entry->sock == -1) { /* We still have to wait ?*/ if (now < udp_entry->next_try_on_error) continue; @@ -273,8 +384,7 @@ static void send_fce_event(const char *path, int event) 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, MAXIOBUF); + data_len = build_fce_packet(obj, iobuf, FCE_CONN_BROKEN, "", NULL, getpid(), user, 0); sendto(udp_entry->sock, iobuf, @@ -282,12 +392,11 @@ static void send_fce_event(const char *path, int event) 0, (struct sockaddr *)&udp_entry->sockaddr, udp_entry->addrinfo.ai_addrlen); - - /* Rebuild our original data packet */ - data_len = build_fce_packet(&packet, path, event, event_id); - pack_fce_packet(&packet, iobuf, MAXIOBUF); } + /* build our data packet */ + data_len = build_fce_packet(obj, iobuf, event, path, oldpath, getpid(), user, ++event_id); + sent_data = sendto(udp_entry->sock, iobuf, data_len, @@ -330,7 +439,7 @@ static int add_udp_socket(const char *target_ip, const char *target_port ) return AFP_OK; } -static void save_close_event(const char *path) +static void save_close_event(const AFPObj *obj, const char *path) { time_t now = time(NULL); @@ -338,7 +447,7 @@ static void save_close_event(const char *path) 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); + send_fce_event(obj, FCE_FILE_MODIFY,last_close_event.path, NULL); } LOG(log_debug, logtype_fce, "save_close_event: %s", path); @@ -347,12 +456,36 @@ static void save_close_event(const char *path) strncpy(last_close_event.path, path, MAXPATHLEN); } +static void fce_init_ign_names(const char *ignores) +{ + int count = 0; + char *names = strdup(ignores); + char *p; + int i = 0; + + while (names[i]) { + count++; + for (; names[i] && names[i] != '/'; i++) + ; + if (!names[i]) + break; + i++; + } + + skip_files = calloc(count + 1, sizeof(char *)); + + for (i = 0, p = strtok(names, "/"); p ; p = strtok(NULL, "/")) + skip_files[i++] = strdup(p); + + free(names); +} + /* * * Dispatcher for all incoming file change events * * */ -int fce_register(fce_ev_t event, const char *path, const char *oldpath, fce_obj_t type) +int fce_register(const AFPObj *obj, fce_ev_t event, const char *path, const char *oldpath) { static bool first_event = true; const char *bname; @@ -363,55 +496,56 @@ int fce_register(fce_ev_t event, const char *path, const char *oldpath, fce_obj_ AFP_ASSERT(event >= FCE_FIRST_EVENT && event <= FCE_LAST_EVENT); AFP_ASSERT(path); - LOG(log_debug, logtype_fce, "register_fce(path: %s, type: %s, event: %s", - path, type == fce_dir ? "dir" : "file", fce_event_names[event]); + LOG(log_debug, logtype_fce, "register_fce(path: %s, event: %s)", + path, fce_event_names[event]); bname = basename_safe(path); - if (udp_sockets == 0) + if ((udp_sockets == 0) && (obj->fce_notify_script == NULL)) { /* No listeners configured */ return AFP_OK; - + } /* do some initialization on the fly the first time */ if (first_event) { fce_initialize_history(); + fce_init_ign_names(obj->fce_ign_names); first_event = false; } /* handle files which should not cause events (.DS_Store atc. ) */ - for (int i = 0; skip_files[i] != NULL; i++) { - if (strcmp(bname, skip_files[i]) == 0) + for (int i = 0; skip_files[i] != NULL; i++) { + if (strcmp(bname, skip_files[i]) == 0) return AFP_OK; } /* Can we ignore this event based on type or history? */ - if (fce_handle_coalescation(event, path, type)) { + if (fce_handle_coalescation(event, path)) { LOG(log_debug9, logtype_fce, "Coalesced fc event <%d> for <%s>", event, path); return AFP_OK; } switch (event) { case FCE_FILE_MODIFY: - save_close_event(path); + save_close_event(obj, path); break; default: - send_fce_event(path, event); + send_fce_event(obj, event, path, oldpath); break; } return AFP_OK; } -static void check_saved_close_events(int fmodwait) +static void check_saved_close_events(const AFPObj *obj) { time_t now = time(NULL); /* check if configured holdclose time has passed */ - if (last_close_event.time && ((last_close_event.time + fmodwait) < now)) { + if (last_close_event.time && ((last_close_event.time + obj->options.fce_fmodwait) < now)) { LOG(log_debug, logtype_fce, "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); + send_fce_event(obj, FCE_FILE_MODIFY, &last_close_event.path[0], NULL); last_close_event.path[0] = 0; last_close_event.time = 0; } @@ -422,11 +556,11 @@ static void check_saved_close_events(int fmodwait) /* * API-Calls for file change api, called form outside (file.c directory.c ofork.c filedir.c) * */ -void fce_pending_events(AFPObj *obj) +void fce_pending_events(const AFPObj *obj) { if (!udp_sockets) return; - check_saved_close_events(obj->options.fce_fmodwait); + check_saved_close_events(obj); } /* @@ -472,6 +606,14 @@ int fce_set_events(const char *events) fce_ev_enabled |= (1 << FCE_FILE_CREATE); } else if (strcmp(p, "dcre") == 0) { fce_ev_enabled |= (1 << FCE_DIR_CREATE); + } else if (strcmp(p, "fmov") == 0) { + fce_ev_enabled |= (1 << FCE_FILE_MOVE); + } else if (strcmp(p, "dmov") == 0) { + fce_ev_enabled |= (1 << FCE_DIR_MOVE); + } else if (strcmp(p, "login") == 0) { + fce_ev_enabled |= (1 << FCE_LOGIN); + } else if (strcmp(p, "logout") == 0) { + fce_ev_enabled |= (1 << FCE_LOGOUT); } } @@ -479,84 +621,3 @@ int fce_set_events(const char *events) return AFP_OK; } - -#ifdef FCE_TEST_MAIN - - -void shortsleep( unsigned int us ) -{ - usleep( us ); -} -int main( int argc, char*argv[] ) -{ - int c; - - 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; - - fce_register(event_code, path, NULL, 0); - ev_cnt++; - - - shortsleep( delay_between_events ); - } -} -#endif /* TESTMAIN*/ diff --git a/etc/afpd/fce_api_internal.h b/etc/afpd/fce_api_internal.h index fb5e58e3..1c226436 100644 --- a/etc/afpd/fce_api_internal.h +++ b/etc/afpd/fce_api_internal.h @@ -14,7 +14,6 @@ #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 */ @@ -33,7 +32,6 @@ struct udp_entry { struct fce_history { fce_ev_t fce_h_event; - fce_obj_t fce_h_type; char fce_h_path[MAXPATHLEN + 1]; struct timeval fce_h_tv; }; @@ -45,7 +43,7 @@ struct fce_close_event { #define PACKET_HDR_LEN (sizeof(struct fce_packet) - FCE_MAX_PATH_LEN) -bool fce_handle_coalescation(int event, const char *path, fce_obj_t type); +bool fce_handle_coalescation(int event, const char *path); void fce_initialize_history(); diff --git a/etc/afpd/fce_util.c b/etc/afpd/fce_util.c index 69f6ea0d..8586a40b 100644 --- a/etc/afpd/fce_util.c +++ b/etc/afpd/fce_util.c @@ -89,7 +89,7 @@ void fce_initialize_history() } } -bool fce_handle_coalescation(int event, const char *path, fce_obj_t type) +bool fce_handle_coalescation(int event, const char *path) { /* These two are used to eval our next index in history */ /* the history is unsorted, speed should not be a problem, length is 10 */ @@ -138,7 +138,6 @@ bool fce_handle_coalescation(int event, const char *path, fce_obj_t type) /* If we find a parent dir we should be DELETED we are done */ if ((coalesce & FCE_COALESCE_DELETE) - && fh->fce_h_type && (event == FCE_FILE_DELETE || event == FCE_DIR_DELETE)) { /* Parent dir ? */ if (!strncmp(fh->fce_h_path, path, strlen(fh->fce_h_path))) @@ -155,8 +154,7 @@ bool fce_handle_coalescation(int event, const char *path, fce_obj_t type) /* We have a new entry for the history, register it */ fce_history_list[oldest_entry_idx].fce_h_tv = tv; fce_history_list[oldest_entry_idx].fce_h_event = event; - fce_history_list[oldest_entry_idx].fce_h_type = type; - strncpy(fce_history_list[oldest_entry_idx].fce_h_path, path, MAXPATHLEN); + strncpy(fce_history_list[oldest_entry_idx].fce_h_path, path, MAXPATHLEN); /* we have to handle this event */ return false; diff --git a/etc/afpd/file.c b/etc/afpd/file.c index 9c78b1b2..881640da 100644 --- a/etc/afpd/file.c +++ b/etc/afpd/file.c @@ -764,7 +764,7 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, createfile_iderr: ad_flush(&ad); ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF ); - fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file); + fce_register(obj, FCE_FILE_CREATE, fullpathname(upath), NULL); sl_index_file(path); curdir->d_offcnt++; diff --git a/etc/afpd/filedir.c b/etc/afpd/filedir.c index f35edfc9..7adbeed6 100644 --- a/etc/afpd/filedir.c +++ b/etc/afpd/filedir.c @@ -219,7 +219,8 @@ int check_name(const struct vol *vol, char *name) move and rename sdir:oldname to curdir:newname in volume vol special care is needed for lock */ -static int moveandrename(struct vol *vol, +static int moveandrename(const AFPObj *obj, + struct vol *vol, struct dir *sdir, int sdir_fd, char *oldname, @@ -238,10 +239,6 @@ static int moveandrename(struct vol *vol, cnid_t id; int cwd_fd = -1; - LOG(log_debug, logtype_afpd, - "moveandrename: [\"%s\"/\"%s\"] -> \"%s\"", - cfrombstr(sdir->d_u_name), oldname, newname); - ad_init(&ad, vol); adp = &ad; adflags = 0; @@ -326,6 +323,15 @@ static int moveandrename(struct vol *vol, goto exit; } + if (isdir) + LOG(log_debug, logtype_afpd, + "moveandrename(\"%s\" -> \"%s/%s\")", + oldunixname, bdata(curdir->d_fullpath), upath); + else + LOG(log_debug, logtype_afpd, + "moveandrename(\"%s/%s\" -> \"%s/%s\")", + bdata(sdir->d_fullpath), oldunixname, bdata(curdir->d_fullpath), upath); + /* source == destination. we just silently accept this. */ if ((!isdir && curdir == sdir) || (isdir && curdir->d_did == sdir->d_pdid)) { if (strcmp(oldname, newname) == 0) { @@ -384,6 +390,15 @@ static int moveandrename(struct vol *vol, AFP_CNID_START("cnid_update"); cnid_update(vol->v_cdb, id, st, curdir->d_did, upath, strlen(upath)); AFP_CNID_DONE(); + + /* Send FCE event */ + if (isdir) { + fce_register(obj, FCE_DIR_MOVE, fullpathname(upath), oldunixname); + } else { + bstring srcpath = bformat("%s/%s", bdata(sdir->d_fullpath), oldunixname); + fce_register(obj, FCE_FILE_MOVE, fullpathname(upath), bdata(srcpath)); + bdestroy(srcpath); + } } exit: @@ -461,7 +476,7 @@ int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size return AFP_OK; /* newname == oldname same dir */ } - rc = moveandrename(vol, sdir, -1, oldname, newname, isdir); + rc = moveandrename(obj, vol, sdir, -1, oldname, newname, isdir); if ( rc == AFP_OK ) { setvoltime(obj, vol ); } @@ -615,7 +630,7 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size cnid_delete(vol->v_cdb, delcnid); AFP_CNID_DONE(); } - fce_register(FCE_DIR_DELETE, fullpathname(upath), NULL, fce_dir); + fce_register(obj, FCE_DIR_DELETE, fullpathname(upath), NULL); } else { /* we have to cache this, the structs are lost in deletcurdir*/ /* but we need the positive returncode to send our event */ @@ -623,7 +638,7 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size if ((dname = bstrcpy(curdir->d_u_name)) == NULL) return AFPERR_MISC; if ((rc = deletecurdir(vol)) == AFP_OK) - fce_register(FCE_DIR_DELETE, fullpathname(cfrombstr(dname)), NULL, fce_dir); + fce_register(obj, FCE_DIR_DELETE, fullpathname(cfrombstr(dname)), NULL); bdestroy(dname); } } else if (of_findname(vol, s_path)) { @@ -637,7 +652,7 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size rc = AFPERR_NOOBJ; } else { if ((rc = deletefile(vol, -1, upath, 1)) == AFP_OK) { - fce_register(FCE_FILE_DELETE, fullpathname(upath), NULL, fce_file); + fce_register(obj, FCE_FILE_DELETE, fullpathname(upath), NULL); if (vol->v_tm_used < s_path->st.st_size) vol->v_tm_used = 0; else @@ -782,7 +797,7 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U /* This does the work */ LOG(log_debug, logtype_afpd, "afp_move(oldname:'%s', newname:'%s', isdir:%u)", oldname, newname, isdir); - rc = moveandrename(vol, sdir, sdir_fd, oldname, newname, isdir); + rc = moveandrename(obj, vol, sdir, sdir_fd, oldname, newname, isdir); if ( rc == AFP_OK ) { char *upath = mtoupath(vol, newname, pdid, utf8_encoding(obj)); diff --git a/etc/afpd/ofork.c b/etc/afpd/ofork.c index 9d389bb2..59e9f153 100644 --- a/etc/afpd/ofork.c +++ b/etc/afpd/ofork.c @@ -420,7 +420,7 @@ int of_closefork(const AFPObj *obj, 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) && (forkpath)) { - fce_register(FCE_FILE_MODIFY, bdata(forkpath), NULL, fce_file); + fce_register(obj, FCE_FILE_MODIFY, bdata(forkpath), NULL); } ad_unlock(ofork->of_ad, ofork->of_refnum, ofork->of_flags & AFPFORK_ERROR ? 0 : 1); diff --git a/etc/spotlight/slmod_sparql_parser.c b/etc/spotlight/slmod_sparql_parser.c index ea7152c4..3d32af62 100644 --- a/etc/spotlight/slmod_sparql_parser.c +++ b/etc/spotlight/slmod_sparql_parser.c @@ -2038,7 +2038,7 @@ int main(int argc, char **argv) struct vol *vol = talloc_zero(ssp_slq, struct vol); vol->v_path = "/Volumes/test"; ssp_slq->slq_vol = vol; - ssp_slq->slq_allow_expr = false; + ssp_slq->slq_allow_expr = true; sparqlvar = 'a'; s = yy_scan_string(argv[1]); diff --git a/include/atalk/fce_api.h b/include/atalk/fce_api.h index d11a2d49..4608a95c 100755 --- a/include/atalk/fce_api.h +++ b/include/atalk/fce_api.h @@ -12,12 +12,18 @@ #include +#define FCE_PACKET_VERSION 2 + /* 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_FILE_MOVE 6 +#define FCE_DIR_MOVE 7 +#define FCE_LOGIN 8 +#define FCE_LOGOUT 9 #define FCE_CONN_START 42 #define FCE_CONN_BROKEN 99 @@ -27,30 +33,96 @@ /* 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. +/* flags for "fce_ev_info" of additional info to send in events */ +#define FCE_EV_INFO_PID (1 << 0) +#define FCE_EV_INFO_USER (1 << 1) +#define FCE_EV_INFO_SRCPATH (1 << 2) + +/* + * Network payload of an FCE packet, version 1 + * + * 1 2 3 4 5 6 7 8 + * +---------+---------+---------+---------+---------+---------+----------+----------+ + * | FCE magic | + * +---------+---------+---------+---------+---------+---------+----------+----------+ + * | version | + * +---------+ + * | event | + * +---------+-----------------------------+ + * | event ID | + * +-------------------+-------------------+ . . . . + * | pathlen | path + * +-------------------+------ . . . . . . . . . . . + * + * + * Network payload of an FCE packet, version 2 + * + * 1 2 3 4 5 6 7 8 + * +---------+---------+---------+---------+---------+---------+----------+----------+ + * | FCE magic | + * +---------+---------+---------+---------+---------+---------+----------+----------+ + * | version | + * +---------+ + * | options | + * +---------+ + * | event | + * +---------+ + * | padding | + * +---------+---------+---------+---------+---------+---------+----------+----------+ + * | reserved | + * +---------+---------+---------+---------+---------+---------+----------+----------+ + * | event ID | + * +---------+---------+---------+---------+ + * ... optional: + * +---------+---------+---------+---------+---------+---------+----------+----------+ + * | pid | + * +---------+---------+---------+---------+---------+---------+----------+----------+ + * ... + * ... optional: + * +-------------------+---------- . . . . + * | username length | username + * +-------------------+---------- . . . . + * ... + * +-------------------+------ . . . . . . + * | pathlen | path + * +-------------------+------ . . . . . . + * ... optional: + * +-------------------+------------- . . . + * | pathlen | source path + * +-------------------+------------- . . . + * + * version = 2 + * options = bitfield: + * 0: pid present + * 1: username present + * 2: source path present + * pid = optional pid + * username = optional username + * source path = optional source path */ -#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 fce_packet { + char fcep_magic[8]; + unsigned char fcep_version; + unsigned char fcep_options; + unsigned char fcep_event; + uint32_t fcep_event_id; + uint64_t fcep_pid; + uint16_t fcep_userlen; + char fcep_user[MAXPATHLEN]; + uint16_t fcep_pathlen1; + char fcep_path1[MAXPATHLEN]; + uint16_t fcep_pathlen2; + char fcep_path2[MAXPATHLEN]; }; typedef uint32_t fce_ev_t; -typedef enum { fce_file, fce_dir } fce_obj_t; struct path; struct ofork; -void fce_pending_events(AFPObj *obj); -int fce_register(fce_ev_t event, const char *path, const char *oldpath, fce_obj_t type); +void fce_pending_events(const AFPObj *obj); +int fce_register(const AFPObj *obj, fce_ev_t event, const char *path, const char *oldpath); int fce_add_udp_socket(const char *target ); // IP or IP:Port int fce_set_coalesce(const char *coalesce_opt ); // all|delete|create int fce_set_events(const char *events); /* fmod,fdel,ddel,fcre,dcre */ diff --git a/include/atalk/globals.h b/include/atalk/globals.h index 603b5caa..ed450520 100644 --- a/include/atalk/globals.h +++ b/include/atalk/globals.h @@ -157,6 +157,9 @@ typedef struct AFPObj { void (*exit)(int); int (*reply)(void *, int); int (*attention)(void *, AFPUserBytes); + int fce_version; + char *fce_ign_names; + char *fce_notify_script; } AFPObj; /* typedef for AFP functions handlers */ diff --git a/man/man5/afp.conf.5.in b/man/man5/afp.conf.5.in index 5356bdf2..ed56e93c 100644 --- a/man/man5/afp.conf.5.in +++ b/man/man5/afp.conf.5.in @@ -808,6 +808,107 @@ Both logtype and loglevels are case insensitive\&. .PP Netatalk includes a nifty filesystem change event mechanism where afpd processes notify interested listeners about certain filesystem event by UDP network datagrams\&. .PP +The following FCE events are defined: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +file modification (\fBfmod\fR) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +file deletion (\fBfdel\fR) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +directory deletion (\fBddel\fR) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +file creation (\fBfcre\fR) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +directory creation (\fBdcre\fR) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +file move or rename (\fBfmov\fR) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +directory move or rename (\fBdmov\fR) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +login (\fBlogin\fR) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +logout (\fBlogout\fR) +.RE +.PP fce listener = \fIhost[:port]\fR \fB(G)\fR .RS 4 Enables sending FCE events to the specified @@ -816,7 +917,7 @@ Enables sending FCE events to the specified is 12250 if not specified\&. Specifying multiple listeners is done by having this option once for each of them\&. .RE .PP -fce events = \fIfmod,fdel,ddel,fcre,dcre,tmsz\fR \fB(G)\fR +fce events = \fIfmod,fdel,ddel,fcre,dcre,fmov,dmov,login,logout\fR \fB(G)\fR .RS 4 Specifies which FCE events are active, default is \fIfmod,fdel,ddel,fcre,dcre\fR\&. @@ -831,6 +932,16 @@ fce holdfmod = \fIseconds\fR \fB(G)\fR .RS 4 This determines the time delay in seconds which is always waited if another file modification for the same file is done by a client before sending an FCE file modification event (fmod)\&. For example saving a file in Photoshop would generate multiple events by itself because the application is opening, modifying and closing a file multiple times for every "save"\&. Default: 60 seconds\&. .RE +.PP +fce ignore names = \fINAME[/NAME2/\&.\&.\&.]\fR \fB(G)\fR +.RS 4 +Slash delimited list of filenames for which FCE events shall not be generated\&. Default: \&.DS_Store\&. +.RE +.PP +fce notify script = \fIPATH\fR \fB(G)\fR +.RS 4 +Script which will be executed for every FCE event, see contrib/shell_utils/fce_ev_script\&.shfrom the Netatalk sources for an example script\&. +.RE .SS "Debug Parameters" .PP These options are useful for debugging only\&.