summaryrefslogtreecommitdiffhomepage
path: root/common-session.c
diff options
context:
space:
mode:
Diffstat (limited to 'common-session.c')
-rw-r--r--common-session.c51
1 files changed, 31 insertions, 20 deletions
diff --git a/common-session.c b/common-session.c
index 4c15391..6e1abf3 100644
--- a/common-session.c
+++ b/common-session.c
@@ -61,6 +61,12 @@ void common_session_init(int sock, char* remotehost) {
ses.connecttimeout = 0;
+ if (pipe(ses.signal_pipe) < 0) {
+ dropbear_exit("signal pipe failed");
+ }
+ setnonblocking(ses.signal_pipe[0]);
+ setnonblocking(ses.signal_pipe[1]);
+
kexfirstinitialise(); /* initialise the kex state */
ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN);
@@ -108,7 +114,6 @@ void common_session_init(int sock, char* remotehost) {
ses.allowprivport = 0;
-
TRACE(("leave session_init"))
}
@@ -132,6 +137,10 @@ void session_loop(void(*loophandler)()) {
FD_SET(ses.sock, &writefd);
}
}
+
+ /* We get woken up when signal handlers write to this pipe.
+ SIGCHLD in svr-chansession is the only one currently. */
+ FD_SET(ses.signal_pipe[0], &readfd);
/* set up for channels which require reading/writing */
if (ses.dataallowed) {
@@ -143,28 +152,30 @@ void session_loop(void(*loophandler)()) {
dropbear_exit("Terminated by signal");
}
- if (val < 0) {
- if (errno == EINTR) {
- /* This must happen even if we've been interrupted, so that
- * changed signal-handler vars can take effect etc */
- if (loophandler) {
- loophandler();
- }
- continue;
- } else {
- dropbear_exit("Error in select");
- }
+ if (val < 0 && errno != EINTR) {
+ dropbear_exit("Error in select");
}
- /* check for auth timeout, rekeying required etc */
- checktimeouts();
+ if (val <= 0) {
+ /* If we were interrupted or the select timed out, we still
+ * want to iterate over channels etc for reading, to handle
+ * server processes exiting etc.
+ * We don't want to read/write FDs. */
+ FD_ZERO(&writefd);
+ FD_ZERO(&readfd);
+ }
- if (val == 0) {
- /* timeout */
- TRACE(("select timeout"))
- continue;
+ /* We'll just empty out the pipe if required. We don't do
+ any thing with the data, since the pipe's purpose is purely to
+ wake up the select() above. */
+ if (FD_ISSET(ses.signal_pipe[0], &readfd)) {
+ char x;
+ while (read(ses.signal_pipe[0], &x, 1) > 0) {}
}
+ /* check for auth timeout, rekeying required etc */
+ checktimeouts();
+
/* process session socket's incoming/outgoing data */
if (ses.sock != -1) {
if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) {
@@ -229,7 +240,7 @@ void session_identification() {
/* write our version string, this blocks */
if (atomicio(write, ses.sock, LOCAL_IDENT "\r\n",
strlen(LOCAL_IDENT "\r\n")) == DROPBEAR_FAILURE) {
- dropbear_exit("Error writing ident string");
+ ses.remoteclosed();
}
/* If they send more than 50 lines, something is wrong */
@@ -250,7 +261,7 @@ void session_identification() {
if (!done) {
TRACE(("err: %s for '%s'\n", strerror(errno), linebuf))
- dropbear_exit("Failed to get remote version");
+ ses.remoteclosed();
} else {
/* linebuf is already null terminated */
ses.remoteident = m_malloc(len);