diff --git a/shellinabox/session.c b/shellinabox/session.c index e699a5a..2f72dff 100644 --- a/shellinabox/session.c +++ b/shellinabox/session.c @@ -116,9 +116,11 @@ void initSession(struct Session *session, const char *sessionKey, session->http = NULL; session->done = 0; session->pty = -1; + session->ptyFirstRead = 1; session->width = 0; session->height = 0; session->buffered = NULL; + session->useLogin = 0; session->len = 0; session->pid = 0; session->cleanup = 0; diff --git a/shellinabox/session.h b/shellinabox/session.h index 73d0eb3..b6b3747 100644 --- a/shellinabox/session.h +++ b/shellinabox/session.h @@ -58,9 +58,11 @@ struct Session { HttpConnection *http; int done; int pty; + int ptyFirstRead; int width; int height; char *buffered; + int useLogin; int len; pid_t pid; int cleanup; diff --git a/shellinabox/shellinaboxd.c b/shellinabox/shellinaboxd.c index ad911cf..2362cf2 100644 --- a/shellinabox/shellinaboxd.c +++ b/shellinabox/shellinaboxd.c @@ -63,7 +63,7 @@ #include #include #include - +#include #include #ifdef HAVE_SYS_PRCTL_H @@ -291,6 +291,13 @@ static void sessionDone(void *arg) { completePendingRequest(session, "", 0, INT_MAX); } +static void delaySession(void) { + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 200 * 1000; // Delay for 0.2 ms + nanosleep(&ts, NULL); +} + static int handleSession(struct ServerConnection *connection, void *arg, short *events, short revents) { struct Session *session = (struct Session *)arg; @@ -310,7 +317,7 @@ static int handleSession(struct ServerConnection *connection, void *arg, int timedOut = serverGetTimeout(connection) < 0; if (bytes || timedOut) { if (!session->http && timedOut) { - debug("[server] Timeout. Closing session!"); + debug("[server] Timeout. Closing session %s!", session->sessionKey); session->cleanup = 1; return 0; } @@ -324,8 +331,26 @@ static int handleSession(struct ServerConnection *connection, void *arg, *events = 0; } serverSetTimeout(connection, AJAX_TIMEOUT); + session->ptyFirstRead = 0; return 1; } else { + if (revents & POLLHUP) { + if (session->useLogin && session->ptyFirstRead) { + // Workaround for random "Session closed" issues related to /bin/login + // closing and reopening our pty during initialization. This happens only + // on some systems like Fedora for example. + // Here we allow that our pty is closed by ignoring POLLHUP on first read. + // Delay is also needed so that login process has some time to reopen pty. + // Note that the issue may occur anyway but with workaround we reduce the + // chances. + debug("[server] POLLHUP received on login PTY first read!"); + session->ptyFirstRead = 0; + delaySession(); + return 1; + } + debug("[server] POLLHUP received on PTY! Closing session %s!", + session->sessionKey); + } return 0; } } @@ -402,6 +427,7 @@ static int dataHandler(HttpConnection *http, struct Service *service, goto bad_new_session; } session->http = http; + session->useLogin = service->useLogin; if (launchChild(service->id, session, rootURL && *rootURL ? rootURL : urlGetURL(url)) < 0) { abandonSession(session);