From 38be99d9e96e4af9ba50ab7c33f152431224c24b Mon Sep 17 00:00:00 2001 From: Frank Lahm Date: Sat, 13 Aug 2011 22:21:12 +0200 Subject: [PATCH] Add a configurable hold time option to FCE file modification event generation, default is 60 s. New option "fceholdfmod" to change it. --- NEWS | 2 ++ etc/afpd/afp_dsi.c | 2 +- etc/afpd/afp_options.c | 4 +++ etc/afpd/fce_api.c | 69 ++++++++++++++++++++++++++++--------- etc/afpd/fce_api_internal.h | 5 +++ include/atalk/fce_api.h | 4 +++ include/atalk/globals.h | 1 + 7 files changed, 70 insertions(+), 17 deletions(-) diff --git a/NEWS b/NEWS index 06d9875a..4a67eca9 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,8 @@ Changes in 2.2.1 The previous behaviour was to enable ACL support for a volume if 1) it was compiled in and 2) the volume supported ACLs. There was no way to disable ACLs for a volume. +* UPD: afpd: add a configurable hold time option to FCE file modification event + generation, default is 60 s, new option "fceholdfmod" to change it * FIX: afpd: increase BerkeleyDB locks and lockobjs * FIX: afpd: create special folder as root * FIX: afpd: fix compilation error if --enable-ddp is used diff --git a/etc/afpd/afp_dsi.c b/etc/afpd/afp_dsi.c index c59272ff..295e03df 100644 --- a/etc/afpd/afp_dsi.c +++ b/etc/afpd/afp_dsi.c @@ -697,7 +697,7 @@ void afp_over_dsi(AFPObj *obj) } pending_request(dsi); - vol_fce_tm_event(); + fce_pending_events(obj); } /* error */ diff --git a/etc/afpd/afp_options.c b/etc/afpd/afp_options.c index 028a1efb..b40f61de 100644 --- a/etc/afpd/afp_options.c +++ b/etc/afpd/afp_options.c @@ -193,6 +193,7 @@ void afp_options_init(struct afp_options *options) options->tcp_rcvbuf = 0; /* 0 means don't change OS default */ options->dsireadbuf = 12; options->mimicmodel = NULL; + options->fce_fmodwait = 60; /* put fmod events 60 seconds on hold */ } /* parse an afpd.conf line. i'm doing it this way because it's @@ -502,6 +503,9 @@ int afp_options_parseline(char *buf, struct afp_options *options) fce_set_events(c); } + if ((c = getoption(buf, "-fceholdfmod"))) + options->fce_fmodwait = atoi(c); + if ((c = getoption(buf, "-mimicmodel")) && (opt = strdup(c))) options->mimicmodel = opt; diff --git a/etc/afpd/fce_api.c b/etc/afpd/fce_api.c index b2dead29..00a04248 100644 --- a/etc/afpd/fce_api.c +++ b/etc/afpd/fce_api.c @@ -80,6 +80,7 @@ static const char *skip_files[] = ".DS_Store", NULL }; +static struct fce_close_event last_close_event; /* * @@ -230,13 +231,22 @@ static int pack_fce_packet(struct fce_packet *packet, unsigned char *buf) * */ 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"); - time_t now = time(NULL); + /* 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 ); @@ -327,6 +337,23 @@ static int add_udp_socket(const char *target_ip, const char *target_port ) 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 @@ -334,6 +361,8 @@ static int add_udp_socket(const char *target_ip, const char *target_port ) * */ 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; @@ -341,11 +370,10 @@ static int register_fce(const char *u_name, int is_dir, int mode) if (u_name == NULL) return AFPERR_PARAM; - static int first_event = FCE_TRUE; - /* 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. ) */ @@ -384,26 +412,29 @@ static int register_fce(const char *u_name, int is_dir, int mode) LOG(log_debug9, logtype_afpd, "Detected fc event <%d> for <%s>", mode, full_path_buffer ); - - /* we do initilization on the fly, no blocking calls in here - * (except when using FQDN in broken DNS environment) - */ - if (first_event == FCE_TRUE) - { - fce_init_udp(); - - /* Notify listeners the we start from the beginning */ - send_fce_event( "", FCE_CONN_START ); - - first_event = FCE_FALSE; + if (mode & FCE_FILE_MODIFY) { + save_close_event(full_path_buffer); + return AFP_OK; } - /* Handle UDP transport */ 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 **************************/ @@ -412,6 +443,12 @@ static int register_fce(const char *u_name, int is_dir, int mode) * */ #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; diff --git a/etc/afpd/fce_api_internal.h b/etc/afpd/fce_api_internal.h index a23cfdf8..ef3255e0 100644 --- a/etc/afpd/fce_api_internal.h +++ b/etc/afpd/fce_api_internal.h @@ -36,6 +36,11 @@ struct fce_history 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 ); diff --git a/include/atalk/fce_api.h b/include/atalk/fce_api.h index b6111e4c..9175afe9 100755 --- a/include/atalk/fce_api.h +++ b/include/atalk/fce_api.h @@ -10,6 +10,8 @@ #ifndef _FCE_API_H #define _FCE_API_H +#include + /* fce_packet.mode */ #define FCE_FILE_MODIFY 1 #define FCE_FILE_DELETE 2 @@ -42,6 +44,8 @@ struct fce_packet struct path; struct ofork; +void fce_pending_events(AFPObj *obj); + int fce_register_delete_file( struct path *path ); int fce_register_delete_dir( char *name ); int fce_register_new_dir( struct path *path ); diff --git a/include/atalk/globals.h b/include/atalk/globals.h index 14dab835..299a5bf5 100644 --- a/include/atalk/globals.h +++ b/include/atalk/globals.h @@ -66,6 +66,7 @@ struct afp_options { int connections, transports, tickleval, timeout, server_notif, flags, dircachesize; int sleep; /* Maximum time allowed to sleep (in tickles) */ int disconnected; /* Maximum time in disconnected state (in tickles) */ + int fce_fmodwait; /* number of seconds FCE file mod events are put on hold */ unsigned int tcp_sndbuf, tcp_rcvbuf; unsigned char passwdbits, passwdminlen, loginmaxfail; u_int32_t server_quantum; -- 2.39.2