From: Frank Lahm Date: Thu, 17 Feb 2011 16:07:19 +0000 (+0100) Subject: Better handling of connection errors and disconnect state X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=commitdiff_plain;h=181e8305cdc7c058e674046650b4da8769bc5e46 Better handling of connection errors and disconnect state --- diff --git a/etc/afpd/afp_config.c b/etc/afpd/afp_config.c index 28fe7cbe..52592564 100644 --- a/etc/afpd/afp_config.c +++ b/etc/afpd/afp_config.c @@ -230,7 +230,6 @@ static afp_child_t *dsi_start(AFPConfig *config, AFPConfig *configs, /* we've forked. */ if (parent_or_child == 1) { configfree(configs, config); - LOG(log_error, logtype_afpd, "dsi_start: IPC fd: %i", child->ipc_fds[1]); config->obj.ipc_fd = child->ipc_fds[1]; close(child->ipc_fds[0]); /* Close parent IPC fd */ free(child); diff --git a/etc/afpd/afp_dsi.c b/etc/afpd/afp_dsi.c index c90c8a70..3b4c01b0 100644 --- a/etc/afpd/afp_dsi.c +++ b/etc/afpd/afp_dsi.c @@ -42,12 +42,6 @@ #include "uid.h" #endif /* FORCE_UIDGID */ -#define CHILD_DIE (1 << 0) -#define CHILD_RUNNING (1 << 1) -#define CHILD_SLEEPING (1 << 2) -#define CHILD_DATA (1 << 3) -#define CHILD_DISCONNECTED (1 << 4) - /* * We generally pass this from afp_over_dsi to all afp_* funcs, so it should already be * available everywhere. Unfortunately some funcs (eg acltoownermode) need acces to it @@ -143,7 +137,7 @@ static void afp_dsi_transfer_session(int sig _U_) int socket; DSI *dsi = (DSI *)child.obj->handle; - LOG(log_note, logtype_afpd, "afp_dsi_transfer_session: got SIGURG, trying to receive session fd"); + LOG(log_note, logtype_afpd, "afp_dsi_transfer_session: got SIGURG, trying to receive session"); if (readt(child.obj->ipc_fd, &dsiID, 2, 0, 2) != 2) { LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive DSI id, goodbye"); @@ -157,7 +151,8 @@ static void afp_dsi_transfer_session(int sig _U_) exit(EXITERR_SYS); } - close(dsi->socket); + if (dsi->socket != -1) + close(dsi->socket); dsi->socket = socket; dsi->header.dsi_requestID = dsiID; dsi->header.dsi_len = 0; @@ -165,13 +160,16 @@ static void afp_dsi_transfer_session(int sig _U_) dsi->header.dsi_command = DSIFUNC_CMD; dsi->header.dsi_flags = DSIFL_REPLY; + /* + * The session transfer happens in the middle of FPDisconnect old session, thus we + * have to send the reply now. + */ if (!dsi_cmdreply(dsi, AFP_OK)) { LOG(log_error, logtype_afpd, "dsi_cmdreply: %s", strerror(errno) ); afp_dsi_close(child.obj); exit(EXITERR_CLNT); } - LOG(log_note, logtype_afpd, "afp_dsi_transfer_session: succesfull primary reconnect"); /* * Now returning from this signal handler return to dsi_receive which should start @@ -183,7 +181,6 @@ static void afp_dsi_transfer_session(int sig _U_) /* */ static void afp_dsi_sleep(void) { - child.flags |= CHILD_SLEEPING; dsi_sleep(child.obj->handle, 1); } @@ -192,8 +189,8 @@ static void afp_dsi_timedown(int sig _U_) { struct sigaction sv; struct itimerval it; - - child.flags |= CHILD_DIE; + DSI *dsi = (DSI *)child.obj->handle; + dsi->flags |= DSI_DIE; /* shutdown and don't reconnect. server going down in 5 minutes. */ setmessage("The server is going down for maintenance."); if (dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT | @@ -269,38 +266,46 @@ static void alarm_handler(int sig _U_) int err; DSI *dsi = (DSI *) child.obj->handle; - if (child.flags & CHILD_DISCONNECTED) { - LOG(log_note, logtype_afpd, "afp_alarm: no reconnect within 10 hours, goodbye"); - afp_dsi_die(EXITERR_CLNT); - } - /* we have to restart the timer because some libraries may use alarm() */ setitimer(ITIMER_REAL, &dsi->timer, NULL); - /* we got some traffic from the client since the previous timer - * tick. */ - if ((child.flags & CHILD_DATA)) { - child.flags &= ~CHILD_DATA; - child.flags &= ~CHILD_DISCONNECTED; + /* we got some traffic from the client since the previous timer tick. */ + if ((dsi->flags & DSI_DATA)) { + dsi->flags &= ~DSI_DATA; + dsi->flags &= ~DSI_DISCONNECTED; return; } - /* if we're in the midst of processing something, - don't die. */ - if ((child.flags & CHILD_SLEEPING) && child.tickle++ < child.obj->options.sleep) { + child.tickle++; + + if (dsi->flags & DSI_SLEEPING) { + if (child.tickle < child.obj->options.sleep) { + LOG(log_error, logtype_afpd, "afp_alarm: sleep time ended"); + afp_dsi_die(EXITERR_CLNT); + } return; } - - if ((child.flags & CHILD_RUNNING) || (child.tickle++ < child.obj->options.timeout)) { - if (!(err = pollvoltime(child.obj))) - err = dsi_tickle(child.obj->handle); - if (err <= 0) + + if (dsi->flags & DSI_DISCONNECTED) { + if (child.tickle > child.obj->options.disconnected) { + LOG(log_error, logtype_afpd, "afp_alarm: no reconnect within 10 minutes, goodbye"); afp_dsi_die(EXITERR_CLNT); - } else { /* didn't receive a tickle, enter disconnected state */ + } + return; + } + + /* if we're in the midst of processing something, don't die. */ + if ( !(dsi->flags & DSI_RUNNING) && (child.tickle > child.obj->options.timeout)) { LOG(log_error, logtype_afpd, "afp_alarm: child timed out, entering disconnected state"); - struct itimerval t = {{0, 60 * 60 * 10}, {0, 0}}; /* 10 hours */ - setitimer(ITIMER_REAL, &t, NULL); - child.flags |= CHILD_DISCONNECTED; + dsi->flags |= DSI_DISCONNECTED; + return; + } + + if ((err = pollvoltime(child.obj)) == 0) + err = dsi_tickle(child.obj->handle); + if (err <= 0) { + LOG(log_error, logtype_afpd, "afp_alarm: connection problem, entering disconnected state"); + dsi->flags |= DSI_DISCONNECTED; } } @@ -450,7 +455,7 @@ void afp_over_dsi(AFPObj *obj) #ifdef SERVERTEXT sigaddset(&action.sa_mask, SIGUSR2); #endif - action.sa_flags = SA_RESTART; + action.sa_flags = SA_RESTART; /* dont restart, otherwi */ if ((sigaction(SIGALRM, &action, NULL) < 0) || (setitimer(ITIMER_REAL, &dsi->timer, NULL) < 0)) { afp_dsi_die(EXITERR_SYS); @@ -461,9 +466,17 @@ void afp_over_dsi(AFPObj *obj) afp_dsi_die(EXITERR_SYS); /* get stuck here until the end */ - while ((cmd = dsi_receive(dsi))) { + while (1) { + cmd = dsi_receive(dsi); + if (cmd == 0) { + /* Some error on the client connection, enter disconnected state */ + dsi->flags |= DSI_DISCONNECTED; + pause(); /* gets interrupted by SIGALARM or SIGURG tickle */ + continue; /* continue receiving until disconnect timer expires + * or a primary reconnect succeeds */ + } + child.tickle = 0; - child.flags &= ~CHILD_SLEEPING; dsi_sleep(dsi, 0); /* wake up */ if (reload_request) { @@ -494,13 +507,13 @@ void afp_over_dsi(AFPObj *obj) if (cmd == DSIFUNC_TICKLE) { /* timer is not every 30 seconds anymore, so we don't get killed on the client side. */ - if ((child.flags & CHILD_DIE)) + if ((dsi->flags & DSI_DIE)) dsi_tickle(dsi); pending_request(dsi); continue; } - child.flags |= CHILD_DATA; + dsi->flags |= DSI_DATA; switch(cmd) { case DSIFUNC_CLOSE: afp_dsi_close(obj); @@ -535,7 +548,7 @@ void afp_over_dsi(AFPObj *obj) * of the fact that we're a stream-based protocol. */ if (afp_switch[function]) { dsi->datalen = DSI_DATASIZ; - child.flags |= CHILD_RUNNING; + dsi->flags |= DSI_RUNNING; LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function)); @@ -553,7 +566,7 @@ void afp_over_dsi(AFPObj *obj) if (obj->force_uid) restore_uidgid ( &obj->uidgid ); #endif /* FORCE_UIDGID */ - child.flags &= ~CHILD_RUNNING; + dsi->flags &= ~DSI_RUNNING; /* Add result to the AFP replay cache */ replaycache[rc_idx].DSIreqID = dsi->clientID; @@ -582,7 +595,7 @@ void afp_over_dsi(AFPObj *obj) function = (u_char) dsi->commands[0]; if ( afp_switch[ function ] != NULL ) { dsi->datalen = DSI_DATASIZ; - child.flags |= CHILD_RUNNING; + dsi->flags |= DSI_RUNNING; LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function)); @@ -593,7 +606,7 @@ void afp_over_dsi(AFPObj *obj) LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s", AfpNum2name(function), AfpErr2name(err)); - child.flags &= ~CHILD_RUNNING; + dsi->flags &= ~DSI_RUNNING; #ifdef FORCE_UIDGID /* bring everything back to old euid, egid */ if (obj->force_uid) diff --git a/etc/afpd/afp_options.c b/etc/afpd/afp_options.c index 407a594a..b8e5130b 100644 --- a/etc/afpd/afp_options.c +++ b/etc/afpd/afp_options.c @@ -159,8 +159,9 @@ void afp_options_init(struct afp_options *options) options->transports = AFPTRANS_TCP; /* TCP only */ options->passwdfile = _PATH_AFPDPWFILE; options->tickleval = 30; - options->timeout = 4; + options->timeout = 4; /* 4 tickles = 2 minutes */ options->sleep = 10* 120; /* 10 h in 30 seconds tick */ + options->disconnected = 20; /* 20 * 30 s (default tickleval) = 10 minutes */ options->server_notif = 1; options->authprintdir = NULL; options->signatureopt = "auto"; diff --git a/etc/afpd/globals.h b/etc/afpd/globals.h index 0910c351..ddd23937 100644 --- a/etc/afpd/globals.h +++ b/etc/afpd/globals.h @@ -57,6 +57,8 @@ struct afp_volume_name { 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) */ unsigned char passwdbits, passwdminlen, loginmaxfail; u_int32_t server_quantum; char hostname[MAXHOSTNAMELEN + 1], *server, *ipaddr, *port, *configfile; @@ -78,7 +80,6 @@ struct afp_options { charset_t maccharset, unixcharset; mode_t umask; mode_t save_mask; - int sleep; #ifdef ADMIN_GRP gid_t admingid; #endif /* ADMIN_GRP */ diff --git a/include/atalk/dsi.h b/include/atalk/dsi.h index f3a79e9f..376e7fc6 100644 --- a/include/atalk/dsi.h +++ b/include/atalk/dsi.h @@ -64,9 +64,8 @@ typedef struct DSI { struct itimerval timer; - int in_write; /* in the middle of writing multiple packets, signal handlers - * can't write to the socket - */ + int in_write; /* in the middle of writing multiple packets, + signal handlers can't write to the socket */ int msg_request; /* pending message to the client */ int down_request; /* pending SIGUSR1 down in 5 mn */ @@ -77,7 +76,8 @@ typedef struct DSI { size_t statuslen; size_t datalen, cmdlen; off_t read_count, write_count; - int asleep; /* client won't reply AFP 0x7a ? */ +// int asleep; /* client won't reply AFP 0x7a ? */ + uint32_t flags; /* DSI flags like DSI_SLEEPING, DSI_DISCONNECTED */ /* noreply = send reply? */ char noreply; const char *program; @@ -149,6 +149,13 @@ typedef struct DSI { /* default port number */ #define DSI_AFPOVERTCP_PORT 548 +/* DSI session State flags */ +#define DSI_DATA (1 << 0) /* we have received a DSI command */ +#define DSI_RUNNING (1 << 1) /* we have received a AFP command */ +#define DSI_SLEEPING (1 << 2) /* we're sleeping after FPZzz */ +#define DSI_DISCONNECTED (1 << 3) /* we're in diconnected state after a socket error */ +#define DSI_DIE (1 << 4) /* SIGUSR1, going down in 5 minutes */ + /* basic initialization: dsi_init.c */ extern DSI *dsi_init (const dsi_proto /*protocol*/, const char * /*program*/, diff --git a/libatalk/dsi/dsi_attn.c b/libatalk/dsi/dsi_attn.c index 8a9eadde..9aef79b5 100644 --- a/libatalk/dsi/dsi_attn.c +++ b/libatalk/dsi/dsi_attn.c @@ -34,7 +34,7 @@ int dsi_attention(DSI *dsi, AFPUserBytes flags) u_int32_t len, nlen; u_int16_t id; - if (dsi->asleep) + if (dsi->flags & DSI_SLEEPING) return 1; if (dsi->in_write) { diff --git a/libatalk/dsi/dsi_close.c b/libatalk/dsi/dsi_close.c index 23c9cf46..d409814f 100644 --- a/libatalk/dsi/dsi_close.c +++ b/libatalk/dsi/dsi_close.c @@ -17,7 +17,7 @@ void dsi_close(DSI *dsi) { /* server generated. need to set all the fields. */ - if (!dsi->asleep) { + if (!(dsi->flags & DSI_SLEEPING)) { dsi->header.dsi_flags = DSIFL_REQUEST; dsi->header.dsi_command = DSIFUNC_CLOSE; dsi->header.dsi_requestID = htons(dsi_serverID(dsi)); diff --git a/libatalk/dsi/dsi_stream.c b/libatalk/dsi/dsi_stream.c index ee595bbd..87c559c7 100644 --- a/libatalk/dsi/dsi_stream.c +++ b/libatalk/dsi/dsi_stream.c @@ -313,7 +313,9 @@ size_t dsi_stream_read(DSI *dsi, void *data, const size_t length) else { /* eof or error */ /* don't log EOF error if it's just after connect (OSX 10.3 probe) */ if (len || stored || dsi->read_count) { - LOG(log_error, logtype_dsi, "dsi_stream_read(%d): %s", len, (len < 0)?strerror(errno):"unexpected EOF"); + if (! (dsi->flags & DSI_DISCONNECTED) +) + LOG(log_error, logtype_dsi, "dsi_stream_read(%d): %s", len, (len < 0)?strerror(errno):"unexpected EOF"); } break; } @@ -357,7 +359,10 @@ static size_t dsi_buffered_stream_read(DSI *dsi, u_int8_t *data, const size_t le */ void dsi_sleep(DSI *dsi, const int state) { - dsi->asleep = state; + if (state) + dsi->flags |= DSI_SLEEPING; + else + dsi->flags &= ~DSI_SLEEPING; } /* --------------------------------------- diff --git a/libatalk/dsi/dsi_tickle.c b/libatalk/dsi/dsi_tickle.c index 02aa0536..2a996539 100644 --- a/libatalk/dsi/dsi_tickle.c +++ b/libatalk/dsi/dsi_tickle.c @@ -23,9 +23,8 @@ int dsi_tickle(DSI *dsi) { char block[DSI_BLOCKSIZ]; u_int16_t id; - int ret; - if (dsi->asleep || dsi->in_write) + if ((dsi->flags & DSI_SLEEPING) || dsi->in_write) return 1; id = htons(dsi_serverID(dsi)); @@ -36,11 +35,6 @@ int dsi_tickle(DSI *dsi) memcpy(block + 2, &id, sizeof(id)); /* code = len = reserved = 0 */ - ret = dsi_stream_write(dsi, block, DSI_BLOCKSIZ, DSI_NOWAIT); - /* we don't really care if we can't send a tickle, it will fail - * elsewhere - */ - ret = (ret == -1 || ret == DSI_BLOCKSIZ); - return ret; + return dsi_stream_write(dsi, block, DSI_BLOCKSIZ, DSI_NOWAIT); }