diff --git a/chansession.h b/chansession.h
index 7879791b99581327c22c976e4ddea6822692ef9e..0930d9d5ed6d9ae92bdd7d707924ffa65990336b 100644
--- a/chansession.h
+++ b/chansession.h
@@ -39,7 +39,6 @@ struct ChanSess {
 	int slave;
 	unsigned char * tty;
 	unsigned char * term;
-	unsigned int termw, termh, termc, termr; /* width, height, col, rows */
 
 	/* exit details */
 	int exited;
@@ -76,6 +75,9 @@ void send_msg_chansess_exitsignal(struct Channel * channel,
 		struct ChanSess * chansess);
 void addnewvar(const char* param, const char* var);
 
+void cli_send_chansess_request();
+void cli_tty_cleanup();
+
 void svr_chansessinitialise();
 extern const struct ChanType svrchansess;
 
diff --git a/cli-auth.c b/cli-auth.c
index 37e78144e58c651ecdbbe053e31424e6d3a311bc..e08158729c002aa5df7ca9454ebf2e83c54964e1 100644
--- a/cli-auth.c
+++ b/cli-auth.c
@@ -7,6 +7,8 @@
 #include "packet.h"
 #include "runopts.h"
 
+#undef DROPBEAR_PUBKEY_AUTH
+
 void cli_authinitialise() {
 
 	memset(&ses.authstate, 0, sizeof(ses.authstate));
diff --git a/cli-chansession.c b/cli-chansession.c
index 852bffa7417f3526623b327dfd20cd0ddf9d678b..be6fc144468db3f6214a7677ec6ce8c6b1ba6ca6 100644
--- a/cli-chansession.c
+++ b/cli-chansession.c
@@ -6,6 +6,7 @@
 #include "channel.h"
 #include "ssh.h"
 #include "runopts.h"
+#include "termcodes.h"
 
 static void cli_closechansess(struct Channel *channel);
 static int cli_initchansess(struct Channel *channel);
@@ -16,7 +17,7 @@ static void send_chansess_pty_req(struct Channel *channel);
 static void send_chansess_shell_req(struct Channel *channel);
 
 static void cli_tty_setup();
-static void cli_tty_cleanup();
+void cli_tty_cleanup();
 
 static const struct ChanType clichansess = {
 	0, /* sepfds */
@@ -50,7 +51,6 @@ static void start_channel_request(struct Channel *channel,
 
 }
 
-
 /* Taken from OpenSSH's sshtty.c:
  * RCSID("OpenBSD: sshtty.c,v 1.5 2003/09/19 17:43:35 markus Exp "); */
 static void cli_tty_setup() {
@@ -91,12 +91,13 @@ static void cli_tty_setup() {
 	TRACE(("leave cli_tty_setup"));
 }
 
-static void cli_tty_cleanup() {
+void cli_tty_cleanup() {
 
 	TRACE(("enter cli_tty_cleanup"));
 
 	if (cli_ses.tty_raw_mode == 0) {
 		TRACE(("leave cli_tty_cleanup: not in raw mode"));
+		return;
 	}
 
 	if (tcsetattr(STDIN_FILENO, TCSADRAIN, &cli_ses.saved_tio) == -1) {
@@ -108,30 +109,123 @@ static void cli_tty_cleanup() {
 	TRACE(("leave cli_tty_cleanup"));
 }
 
+static void put_termcodes() {
+
+	TRACE(("enter put_termcodes"));
+
+	struct termios tio;
+	unsigned int sshcode;
+	const struct TermCode *termcode;
+	unsigned int value;
+	unsigned int mapcode;
+
+	unsigned int bufpos1, bufpos2;
+
+	if (tcgetattr(STDIN_FILENO, &tio) == -1) {
+		dropbear_log(LOG_WARNING, "Failed reading termmodes");
+		buf_putint(ses.writepayload, 1); /* Just the terminator */
+		buf_putbyte(ses.writepayload, 0); /* TTY_OP_END */
+		return;
+	}
+
+	bufpos1 = ses.writepayload->pos;
+	buf_putint(ses.writepayload, 0); /* A placeholder for the final length */
+
+	/* As with Dropbear server, we ignore baud rates for now */
+	for (sshcode = 1; sshcode < MAX_TERMCODE; sshcode++) {
+
+		termcode = &termcodes[sshcode];
+		mapcode = termcode->mapcode;
+
+		switch (termcode->type) {
+
+			case TERMCODE_NONE:
+				continue;
+
+			case TERMCODE_CONTROLCHAR:
+				value = tio.c_cc[mapcode];
+				break;
+
+			case TERMCODE_INPUT:
+				value = tio.c_iflag & mapcode;
+				break;
+
+			case TERMCODE_OUTPUT:
+				value = tio.c_oflag & mapcode;
+				break;
+
+			case TERMCODE_LOCAL:
+				value = tio.c_lflag & mapcode;
+				break;
+
+			case TERMCODE_CONTROL:
+				value = tio.c_cflag & mapcode;
+				break;
+
+			default:
+				continue;
+
+		}
+
+		/* If we reach here, we have something to say */
+		buf_putbyte(ses.writepayload, sshcode);
+		buf_putint(ses.writepayload, value);
+	}
+
+	buf_putbyte(ses.writepayload, 0); /* THE END, aka TTY_OP_END */
+
+	/* Put the string length at the start of the buffer */
+	bufpos2 = ses.writepayload->pos;
+
+	buf_setpos(ses.writepayload, bufpos1); /* Jump back */
+	buf_putint(ses.writepayload, bufpos2 - bufpos1); /* len(termcodes) */
+	buf_setpos(ses.writepayload, bufpos2); /* Back where we were */
+
+	TRACE(("leave put_termcodes"));
+}
+
+static void put_winsize() {
+
+	struct winsize ws;
+
+	if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) {
+		/* Some sane defaults */
+		ws.ws_row = 25;
+		ws.ws_col = 80;
+		ws.ws_xpixel = 0;
+		ws.ws_ypixel = 0;
+	}
+
+	buf_putint(ses.writepayload, ws.ws_col); /* Cols */
+	buf_putint(ses.writepayload, ws.ws_row); /* Rows */
+	buf_putint(ses.writepayload, ws.ws_xpixel); /* Width */
+	buf_putint(ses.writepayload, ws.ws_ypixel); /* Height */
+
+}
+
 static void send_chansess_pty_req(struct Channel *channel) {
 
-	unsigned char* termmodes = "\0";
 	unsigned char* term = NULL;
-	int termc = 80, termr = 25, termw = 0, termh = 0; /* XXX TODO matt */
 
 	TRACE(("enter send_chansess_pty_req"));
+
 	start_channel_request(channel, "pty-req");
 
+	/* Don't want replies */
+	buf_putbyte(ses.writepayload, 0);
+
+	/* Get the terminal */
 	term = getenv("TERM");
 	if (term == NULL) {
-		term = "vt100";
+		term = "vt100"; /* Seems a safe default */
 	}
-
-	/* XXX TODO */
-	buf_putbyte(ses.writepayload, 0); /* Don't want replies */
 	buf_putstring(ses.writepayload, term, strlen(term));
-	buf_putint(ses.writepayload, termc); /* Cols */
-	buf_putint(ses.writepayload, termr); /* Rows */
-	buf_putint(ses.writepayload, termw); /* Width */
-	buf_putint(ses.writepayload, termh); /* Height */
 
-	buf_putstring(ses.writepayload, termmodes, 1); /* XXX TODO */
-	//m_free(termmodes);
+	/* Window size */
+	put_winsize();
+
+	/* Terminal mode encoding */
+	put_termcodes();
 
 	encrypt_packet();
 	TRACE(("leave send_chansess_pty_req"));
@@ -171,7 +265,6 @@ static int cli_initchansess(struct Channel *channel) {
 		send_chansess_pty_req(channel);
 	}
 
-	cli_opts.cmd = "df";
 	send_chansess_shell_req(channel);
 
 	if (cli_opts.wantpty) {
diff --git a/cli-main.c b/cli-main.c
index c5d5b3e8851fbee7cfea5cc5480644b82ca498db..106cc64a53f6ac81ed5aa32b30a65be6884a0486 100644
--- a/cli-main.c
+++ b/cli-main.c
@@ -61,9 +61,12 @@ static void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
 				cli_opts.remoteport, format);
 	}
 
+	/* Do the cleanup first, since then the terminal will be reset */
+	cli_session_cleanup();
+	common_session_cleanup();
+
 	_dropbear_log(LOG_INFO, fmtbuf, param);
 
-	common_session_cleanup();
 	exit(exitcode);
 }
 
@@ -73,6 +76,6 @@ static void cli_dropbear_log(int priority, const char* format, va_list param) {
 
 	vsnprintf(printbuf, sizeof(printbuf), format, param);
 
-	fprintf(stderr, "Dropbear: %s\n", printbuf);
+	fprintf(stderr, "%s: %s\n", cli_opts.progname, printbuf);
 
 }
diff --git a/cli-runopts.c b/cli-runopts.c
index 137e0da31162351e9fed476743fdfa175bb7e223..2811ef38b2f6468a1b09102188b777b259deebcd 100644
--- a/cli-runopts.c
+++ b/cli-runopts.c
@@ -55,11 +55,12 @@ void cli_getopts(int argc, char ** argv) {
 	char* userhostarg = NULL;
 
 	/* see printhelp() for options */
+	cli_opts.progname = argv[0];
 	cli_opts.remotehost = NULL;
 	cli_opts.remoteport = NULL;
 	cli_opts.username = NULL;
 	cli_opts.cmd = NULL;
-	cli_opts.wantpty = 0;
+	cli_opts.wantpty = 1;
 	opts.nolocaltcp = 0;
 	opts.noremotetcp = 0;
 	/* not yet
diff --git a/cli-session.c b/cli-session.c
index 258d5c4db79f5a349401c873003caff6e53a7c45..42770f5319ae8be2a63dac65f5c20941e190f524 100644
--- a/cli-session.c
+++ b/cli-session.c
@@ -9,10 +9,13 @@
 #include "channel.h"
 #include "random.h"
 #include "service.h"
+#include "runopts.h"
+#include "chansession.h"
 
 static void cli_remoteclosed();
 static void cli_sessionloop();
 static void cli_session_init();
+static void cli_finished();
 
 struct clientsession cli_ses; /* GLOBAL */
 
@@ -163,6 +166,12 @@ static void cli_sessionloop() {
 			cli_ses.state = SESSION_RUNNING;
 			return;
 
+		case SESSION_RUNNING:
+			if (ses.chancount < 1) {
+				cli_finished();
+			}
+			return;
+
 		/* XXX more here needed */
 
 
@@ -174,6 +183,26 @@ static void cli_sessionloop() {
 
 }
 
+void cli_session_cleanup() {
+
+	if (!sessinitdone) {
+		return;
+	}
+	cli_tty_cleanup();
+
+}
+
+static void cli_finished() {
+
+	cli_session_cleanup();
+	common_session_cleanup();
+	fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username,
+			cli_opts.remotehost, cli_opts.remoteport);
+	exit(EXIT_SUCCESS);
+}
+
+
+
 /* called when the remote side closes the connection */
 static void cli_remoteclosed() {
 
diff --git a/dbutil.c b/dbutil.c
index 1c0648c815edaebe0dde4ed0f9b0d3c957aa3e86..a89c3c870458edc7de3d9aa14c00f3857f050de5 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -103,6 +103,7 @@ void dropbear_log(int priority, const char* format, ...) {
 #ifdef DEBUG_TRACE
 void dropbear_trace(const char* format, ...) {
 
+#if 0
 	va_list param;
 
 	va_start(param, format);
@@ -110,6 +111,7 @@ void dropbear_trace(const char* format, ...) {
 	vfprintf(stderr, format, param);
 	fprintf(stderr, "\n");
 	va_end(param);
+#endif
 }
 #endif /* DEBUG_TRACE */
 
diff --git a/options.h b/options.h
index ee0ee600a9a4f25474bca9ffeae96f5e754c3afb..1ab16c72cb2d1e1d30ed76f8c103f4a235bd53a4 100644
--- a/options.h
+++ b/options.h
@@ -111,7 +111,7 @@
 /* Authentication types to enable, at least one required.
    RFC Draft requires pubkey auth, and recommends password */
 #define DROPBEAR_PASSWORD_AUTH
-//#define DROPBEAR_PUBKEY_AUTH
+#define DROPBEAR_PUBKEY_AUTH
 
 /* Random device to use - you must specify _one only_.
  * DEV_RANDOM is recommended on hosts with a good /dev/urandom, otherwise use
diff --git a/runopts.h b/runopts.h
index b2fc1495ff7ecd30ab916a7d94e35f670efba750..26d15ef881690cb58b5ad1f83f13888db2076dd7 100644
--- a/runopts.h
+++ b/runopts.h
@@ -79,6 +79,7 @@ void svr_getopts(int argc, char ** argv);
 /* Uncompleted XXX matt */
 typedef struct cli_runopts {
 
+	char *progname;
 	char *remotehost;
 	char *remoteport;
 
diff --git a/session.h b/session.h
index 1000c5e39c2df935d9357ba7d03b0398fe374122..1ce944abe8bd9af21d3dcdb8098f540da8b5b693 100644
--- a/session.h
+++ b/session.h
@@ -55,6 +55,7 @@ void svr_dropbear_log(int priority, const char* format, va_list param);
 void cli_session(int sock, char *remotehost);
 void cli_dropbear_exit(int exitcode, const char* format, va_list param);
 void cli_dropbear_log(int priority, const char* format, va_list param);
+void cli_session_cleanup();
 
 struct key_context {
 
diff --git a/signkey.c b/signkey.c
index faf373987bb526e913ece95bf40c117503c2d303..2fd301f85edf93595a2cca823164a66a72f741d4 100644
--- a/signkey.c
+++ b/signkey.c
@@ -53,7 +53,6 @@ int buf_get_pub_key(buffer *buf, sign_key *key, int *type) {
 	unsigned int len;
 
 	TRACE(("enter buf_get_pub_key"));
-	printhex(buf_getptr(buf, 0x99), 0x99);
 
 	ident = buf_getstring(buf, &len);
 
diff --git a/svr-chansession.c b/svr-chansession.c
index 50e0a5ecab7e1d91e8034fb54f1d4eb9ab971779..9639961b15457c1efe14053be97a403d4951fa0d 100644
--- a/svr-chansession.c
+++ b/svr-chansession.c
@@ -56,6 +56,7 @@ static void chansessionrequest(struct Channel *channel);
 
 static void send_exitsignalstatus(struct Channel *channel);
 static int sesscheckclose(struct Channel *channel);
+static void get_termmodes(struct ChanSess *chansess);
 
 
 /* required to clear environment */
@@ -192,10 +193,6 @@ static int newchansess(struct Channel *channel) {
 	chansess->slave = -1;
 	chansess->tty = NULL;
 	chansess->term = NULL;
-	chansess->termw = 0;
-	chansess->termh = 0;
-	chansess->termc = 0;
-	chansess->termr = 0;
 
 	chansess->exited = 0;
 
@@ -376,22 +373,111 @@ static int sessionsignal(struct ChanSess *chansess) {
  * client. Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
 static int sessionwinchange(struct ChanSess *chansess) {
 
+	int termc, termr, termw, termh;
+
 	if (chansess->master < 0) {
 		/* haven't got a pty yet */
 		return DROPBEAR_FAILURE;
 	}
 			
-	chansess->termc = buf_getint(ses.payload);
-	chansess->termr = buf_getint(ses.payload);
-	chansess->termw = buf_getint(ses.payload);
-	chansess->termh = buf_getint(ses.payload);
+	termc = buf_getint(ses.payload);
+	termr = buf_getint(ses.payload);
+	termw = buf_getint(ses.payload);
+	termh = buf_getint(ses.payload);
 	
-	pty_change_window_size(chansess->master, chansess->termr, chansess->termc,
-		chansess->termw, chansess->termh);
+	pty_change_window_size(chansess->master, termr, termc, termw, termh);
 
 	return DROPBEAR_FAILURE;
 }
 
+static void get_termmodes(struct ChanSess *chansess) {
+
+	struct termios termio;
+	unsigned char opcode;
+	unsigned int value;
+	const struct TermCode * termcode;
+	unsigned int len;
+
+	TRACE(("enter get_termmodes"));
+
+	/* Term modes */
+	/* We'll ignore errors and continue if we can't set modes.
+	 * We're ignoring baud rates since they seem evil */
+	if (tcgetattr(chansess->master, &termio) == -1) {
+		return;
+	}
+
+	len = buf_getint(ses.payload);
+	if (len != ses.payload->len - ses.payload->pos) {
+		dropbear_exit("bad term mode string");
+	}
+
+	if (len == 0) {
+		TRACE(("leave get_termmodes: empty terminal modes string"));
+	}
+
+	while (((opcode = buf_getbyte(ses.payload)) != 0x00) && opcode <= 159) {
+
+		/* must be before checking type, so that value is consumed even if
+		 * we don't use it */
+		value = buf_getint(ses.payload);
+
+		/* handle types of code */
+		if (opcode > MAX_TERMCODE) {
+			continue;
+		}
+		termcode = &termcodes[(unsigned int)opcode];
+		
+
+		switch (termcode->type) {
+
+			case TERMCODE_NONE:
+				break;
+
+			case TERMCODE_CONTROLCHAR:
+				termio.c_cc[termcode->mapcode] = value;
+				break;
+
+			case TERMCODE_INPUT:
+				if (value) {
+					termio.c_iflag |= termcode->mapcode;
+				} else {
+					termio.c_iflag &= ~(termcode->mapcode);
+				}
+				break;
+
+			case TERMCODE_OUTPUT:
+				if (value) {
+					termio.c_oflag |= termcode->mapcode;
+				} else {
+					termio.c_oflag &= ~(termcode->mapcode);
+				}
+				break;
+
+			case TERMCODE_LOCAL:
+				if (value) {
+					termio.c_lflag |= termcode->mapcode;
+				} else {
+					termio.c_lflag &= ~(termcode->mapcode);
+				}
+				break;
+
+			case TERMCODE_CONTROL:
+				if (value) {
+					termio.c_cflag |= termcode->mapcode;
+				} else {
+					termio.c_cflag &= ~(termcode->mapcode);
+				}
+				break;
+				
+		}
+	}
+	if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) {
+		dropbear_log(LOG_INFO, "error setting terminal attributes");
+	}
+	TRACE(("leave get_termmodes"));
+}
+
 /* Set up a session pty which will be used to execute the shell or program.
  * The pty is allocated now, and kept for when the shell/program executes.
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
@@ -399,7 +485,6 @@ static int sessionpty(struct ChanSess * chansess) {
 
 	unsigned int termlen;
 	unsigned char namebuf[65];
-	struct termios termio;
 
 	TRACE(("enter sessionpty"));
 	chansess->term = buf_getstring(ses.payload, &termlen);
@@ -408,10 +493,6 @@ static int sessionpty(struct ChanSess * chansess) {
 		TRACE(("leave sessionpty: term len too long"));
 		return DROPBEAR_FAILURE;
 	}
-	chansess->termc = buf_getint(ses.payload);
-	chansess->termr = buf_getint(ses.payload);
-	chansess->termw = buf_getint(ses.payload);
-	chansess->termh = buf_getint(ses.payload);
 
 	/* allocate the pty */
 	assert(chansess->master == -1); /* haven't already got one */
@@ -426,89 +507,12 @@ static int sessionpty(struct ChanSess * chansess) {
 	}
 
 	pty_setowner(ses.authstate.pw, chansess->tty);
-	pty_change_window_size(chansess->master, chansess->termr, chansess->termc,
-			chansess->termw, chansess->termh);
 
-	/* Term modes */
-	/* We'll ignore errors and continue if we can't set modes.
-	 * We're ignoring baud rates since they seem evil */
-	if (tcgetattr(chansess->master, &termio) == 0) {
-		unsigned char opcode;
-		unsigned int value;
-		const struct TermCode * termcode;
-		unsigned int len;
-
-		len = buf_getint(ses.payload);
-		if (len != ses.payload->len - ses.payload->pos) {
-			dropbear_exit("bad term mode string");
-		}
-
-		if (len == 0) {
-			TRACE(("empty terminal modes string"));
-			return DROPBEAR_SUCCESS;
-		}
-
-		while (((opcode = buf_getbyte(ses.payload)) != 0x00) &&
-				opcode <= 159) {
+	/* Set up the rows/col counts */
+	sessionwinchange(chansess);
 
-			/* must be before checking type, so that value is consumed even if
-			 * we don't use it */
-			value = buf_getint(ses.payload);
-
-			/* handle types of code */
-			if (opcode > MAX_TERMCODE) {
-				continue;
-			}
-			termcode = &termcodes[(unsigned int)opcode];
-			
-
-			switch (termcode->type) {
-
-				case TERMCODE_NONE:
-					break;
-
-				case TERMCODE_CONTROLCHAR:
-					termio.c_cc[termcode->mapcode] = value;
-					break;
-
-				case TERMCODE_INPUT:
-					if (value) {
-						termio.c_iflag |= termcode->mapcode;
-					} else {
-						termio.c_iflag &= ~(termcode->mapcode);
-					}
-					break;
-
-				case TERMCODE_OUTPUT:
-					if (value) {
-						termio.c_oflag |= termcode->mapcode;
-					} else {
-						termio.c_oflag &= ~(termcode->mapcode);
-					}
-					break;
-
-				case TERMCODE_LOCAL:
-					if (value) {
-						termio.c_lflag |= termcode->mapcode;
-					} else {
-						termio.c_lflag &= ~(termcode->mapcode);
-					}
-					break;
-
-				case TERMCODE_CONTROL:
-					if (value) {
-						termio.c_cflag |= termcode->mapcode;
-					} else {
-						termio.c_cflag &= ~(termcode->mapcode);
-					}
-					break;
-					
-			}
-		}
-		if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) {
-			dropbear_log(LOG_INFO, "error setting terminal attributes");
-		}
-	}
+	/* Read the terminal modes */
+	get_termmodes(chansess);
 
 	TRACE(("leave sessionpty"));
 	return DROPBEAR_SUCCESS;