diff --git a/libmish/src/mish_capture_select.c b/libmish/src/mish_capture_select.c
index 98ebc4b..065a2eb 100644
--- a/libmish/src/mish_capture_select.c
+++ b/libmish/src/mish_capture_select.c
@@ -9,9 +9,33 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <signal.h>
+#include <unistd.h>
 #include "mish_priv.h"
 #include "mish.h"
 
+/*
+ * We now use a thread to run the commands; this solve the problem of
+ * command generating a lot of output, deadlocking the select() thread,
+ * as the command write() would fill up the pipe buffer and block.
+ */
+void *
+_mish_cmd_runner_thread(
+		void *param)
+{
+	mish_p m = param;
+
+	printf("%s\n", __func__);
+	while (!(m->flags & MISH_QUIT)) {
+		sem_wait(&m->runner_block);
+		_mish_cmd_flush(0);
+	};
+	printf("Exiting %s\n", __func__);
+	m->cmd_runner = 0;
+	sem_destroy(&m->runner_block);
+	return NULL;
+}
+
 /*
  * This is a select() based capture thread, it's not /ideal/ in terms of
  * performances, but it's portable and should work on OSX/BSD Linux etc.
@@ -68,6 +92,12 @@ _mish_capture_select(
 			_mish_input_read(m, &r, &c->input);
 			if (c->input.fd == -1 || (c->flags & MISH_CLIENT_DELETE))
 				mish_client_delete(m, c);
+			if (c->flags & MISH_CLIENT_HAS_CMD) {
+			//	printf("Waking up cmd_runner\n");
+				c->flags &= ~MISH_CLIENT_HAS_CMD;
+				// wake up the command runner
+				sem_post(&m->runner_block);
+			}
 		}
 
 		unsigned int max_lines = m->backlog.max_lines;
@@ -112,8 +142,9 @@ _mish_capture_select(
 	mish_client_p c;
 	while ((c = TAILQ_FIRST(&m->clients)) != NULL)
 		mish_client_delete(m, c);
-	m->flags &= ~MISH_QUIT;
+//	m->flags &= ~MISH_QUIT;
 	m->capture = 0;	// mark the thread done
+//	printf("Exiting %s\n", __func__);
 	exit(0);	// this calls mish_terminate, on main thread
 //	return NULL;
 }
diff --git a/libmish/src/mish_client.c b/libmish/src/mish_client.c
index 1cd19cc..d783e95 100644
--- a/libmish/src/mish_client.c
+++ b/libmish/src/mish_client.c
@@ -354,7 +354,7 @@ _mish_cmd_history(
 MISH_CMD_NAMES(history, "history");
 MISH_CMD_HELP(history,
 		"Display the history of commands.");
-MISH_CMD_REGISTER(history, _mish_cmd_history);
+MISH_CMD_REGISTER_KIND(history, _mish_cmd_history, 0, MISH_CLIENT_CMD_KIND);
 
 
 static void
@@ -380,5 +380,5 @@ _mish_cmd_disconnect(
 MISH_CMD_NAMES(disconnect, "dis", "disconnect", "logout");
 MISH_CMD_HELP(disconnect,
 		"Disconnect this telnet session. If appropriate");
-MISH_CMD_REGISTER(disconnect, _mish_cmd_disconnect);
+MISH_CMD_REGISTER_KIND(disconnect, _mish_cmd_disconnect, 0, MISH_CLIENT_CMD_KIND);
 
diff --git a/libmish/src/mish_client_input.c b/libmish/src/mish_client_input.c
index b297fda..83a4a84 100644
--- a/libmish/src/mish_client_input.c
+++ b/libmish/src/mish_client_input.c
@@ -195,7 +195,10 @@ kb_end:
 						fprintf(stdout, "%s", c->cmd->line + c->cmd->done+1);
 					fprintf(stdout, "'\n");
 				}
-				mish_cmd_call(c->cmd->line, c);
+				// if we have a non-safe command, we need to signal the
+				// cmd execution thread, so mark the client as having a command
+				if (mish_cmd_call(c->cmd->line, c) == 1)
+					c->flags |= MISH_CLIENT_HAS_CMD;
 				c->cmd = NULL;	// new one
 				{	// reuse the last empty one
 					mish_line_p last = TAILQ_LAST(&in->backlog, mish_line_queue_t);
diff --git a/libmish/src/mish_cmd.c b/libmish/src/mish_cmd.c
index e5d07b6..d033ccc 100644
--- a/libmish/src/mish_cmd.c
+++ b/libmish/src/mish_cmd.c
@@ -46,7 +46,7 @@ DECLARE_FIFO(mish_cmd_call_t, mish_call_queue, 4);
 DEFINE_FIFO(mish_cmd_call_t, mish_call_queue);
 
 static TAILQ_HEAD(,mish_cmd_t) _cmd_list = TAILQ_HEAD_INITIALIZER(_cmd_list);
-static mish_call_queue_t _cmd_fifo = {0};
+static mish_call_queue_t 	_cmd_fifo[2] = {0};
 
 void __attribute__((weak))
 mish_register_cmd_kind(
@@ -146,7 +146,7 @@ mish_argv_make(
 	_mish_argv_t * r = calloc(1, sizeof(*r));
 	r->line = strdup(line);
 	char *dup = r->line;
-	char quote;
+	char quote = 0;
 	enum { s_newarg = 0, s_startarg, s_copyquote, s_skip, s_copy };
 	int state = s_newarg;
 	do {
@@ -232,39 +232,52 @@ mish_cmd_call(
 	int ac = 0;
 	char ** av = mish_argv_make(cmd_line, &ac);
 
-	if (cmd->flags.safe) {
-		if (!mish_call_queue_isfull(&_cmd_fifo)) {
-			mish_cmd_call_t fe = {
-					.cmd = cmd,
-					.argv = av,
-					.argc = ac,
-			};
-			mish_call_queue_write(&_cmd_fifo, fe);
-		} else {
-			fprintf(stderr,
-				"mish: cmd FIFO full, make sure to call mish_cmd_poll()!\n");
-		}
-	} else {
-		cmd->cmd_cb(cmd->param_cb ? cmd->param_cb : c, ac, (const char**)av);
+	mish_call_queue_t 	*fifo = &_cmd_fifo[cmd->flags.safe];
+
+	// these are special commands, their parameter is the client
+	if (cmd->kind == MISH_CLIENT_CMD_KIND) {
+		cmd->cmd_cb(c, ac, (const char**)av);
 		mish_argv_free(av);
+		return 0;
 	}
-	return 0;
+	// all other commands are queued
+	if (!mish_call_queue_isfull(fifo)) {
+		mish_cmd_call_t fe = {
+				.cmd = cmd,
+				.argv = av,
+				.argc = ac,
+		};
+		mish_call_queue_write(fifo, fe);
+	} else {
+		fprintf(stderr,
+			"mish: cmd FIFO%d full, make sure to call mish_cmd_poll()!\n",
+			cmd->flags.safe);
+	}
+	return cmd->flags.safe == 0;	// we got a command to run?
+}
+
+int
+_mish_cmd_flush(
+		unsigned int queue)
+{
+	int res = 0;
+	mish_call_queue_t 	*fifo = &_cmd_fifo[!!queue];
+	while (!mish_call_queue_isempty(fifo)) {
+		mish_cmd_call_t c = mish_call_queue_read(fifo);
+		c.cmd->cmd_cb(
+				c.cmd->param_cb,
+				c.argc, (const char**)c.argv);
+		mish_argv_free(c.argv);
+		res++;
+	}
+	return res;
+
 }
 
 int
 mish_cmd_poll()
 {
-	int res = 0;
-
-	while (!mish_call_queue_isempty(&_cmd_fifo)) {
-		mish_cmd_call_t c = mish_call_queue_read(&_cmd_fifo);
-
-		c.cmd->cmd_cb(
-				c.cmd->param_cb, c.argc, (const char**)c.argv);
-		mish_argv_free(c.argv);
-		res++;
-	}
-	return res;
+	return _mish_cmd_flush(1);
 }
 
 static const char *_help[] = {
diff --git a/libmish/src/mish_priv.h b/libmish/src/mish_priv.h
index c12d236..8ba2b24 100644
--- a/libmish/src/mish_priv.h
+++ b/libmish/src/mish_priv.h
@@ -12,6 +12,7 @@
 #include <sys/select.h>
 #include <stdint.h>
 #include <pthread.h>
+#include <semaphore.h>
 #include <termios.h>
 #include "bsd_queue.h"
 #include "mish_priv_vt.h"
@@ -28,6 +29,9 @@ enum {
 	MISH_IN_SPLIT, 		// store current line, add it to backlog
 };
 
+#define MISH_CMD_KIND 			('m' << 24 | 'i' << 16 | 's' << 8 | 'h')
+#define MISH_CLIENT_CMD_KIND  	('c' << 24 | 'l' << 16 | 'i' << 8 | 'e')
+
 /*
  * This receives stuff from a file descriptor, 'processes' it, and split it
  * into lines to eventually store new lines into the 'backlog'.
@@ -49,13 +53,14 @@ typedef struct mish_input_t {
 
 /* various internal states for the client */
 enum {
-	MISH_CLIENT_INIT_SENT = (1 << 0),
+	MISH_CLIENT_INIT_SENT 		= (1 << 0),
 	MISH_CLIENT_HAS_WINDOW_SIZE = (1 << 1),
-	MISH_CLIENT_HAS_CURSOR_POS = (1 << 2),
-	MISH_CLIENT_UPDATE_PROMPT = (1 << 3),
-	MISH_CLIENT_UPDATE_WINDOW = (1 << 4),
-	MISH_CLIENT_SCROLLING = (1 << 5),
-	MISH_CLIENT_DELETE = (1 << 6),
+	MISH_CLIENT_HAS_CURSOR_POS 	= (1 << 2),
+	MISH_CLIENT_UPDATE_PROMPT 	= (1 << 3),
+	MISH_CLIENT_UPDATE_WINDOW 	= (1 << 4),
+	MISH_CLIENT_SCROLLING 		= (1 << 5),
+	MISH_CLIENT_HAS_CMD			= (1 << 6),
+	MISH_CLIENT_DELETE 			= (1 << 7),
 };
 
 typedef struct mish_client_t {
@@ -150,6 +155,8 @@ typedef struct mish_t {
 	mish_client_p	console;		// client that is also the original terminal.
 
 	pthread_t 		capture;		// libmish main thread
+	pthread_t 		cmd_runner;		// command runner thread
+	sem_t 			runner_block;	// semaphore to block the runner thread
 	pthread_t		main;			// todo: allow pause/stop/resume?
 
 	struct {
@@ -258,13 +265,20 @@ _mish_input_read(
 		mish_p m,
 		fd_set * fds,
 		mish_input_p in);
+// flush the command FIFO (queue = 0 for non-safe commands)
+int
+_mish_cmd_flush(
+		unsigned int queue);
 
 /*
- * Capture thread function
+ * Thread functions
  */
 void *
 _mish_capture_select(
 		void *param);
+void *
+_mish_cmd_runner_thread(
+		void *param);
 
 /*
  * https://en.wikipedia.org/wiki/ANSI_escape_code#Terminal_output_sequences
diff --git a/libmish/src/mish_session.c b/libmish/src/mish_session.c
index b321990..34f0902 100644
--- a/libmish/src/mish_session.c
+++ b/libmish/src/mish_session.c
@@ -147,9 +147,12 @@ mish_prepare(
 			goto error;
 		}
 	}
+	mish_set_command_parameter(MISH_CMD_KIND, m);
 	atexit(_mish_atexit);
 //	m->main = pthread_self();
 	// TODO: Make an epoll polling thread for linux
+	sem_init(&m->runner_block, 0, 0);
+	pthread_create(&m->cmd_runner, NULL, _mish_cmd_runner_thread, m);
 	pthread_create(&m->capture, NULL, _mish_capture_select, m);
 
 	_mish = m;
@@ -196,19 +199,21 @@ mish_terminate(
 		perror("mish_terminate tcsetattr");
 #endif
 	close(m->originals[0]); close(m->originals[1]);
-	if (m->capture) {
-		m->flags |= MISH_QUIT;
+	pthread_t t1 = m->cmd_runner, t2 = m->capture;
+	m->flags |= MISH_QUIT;
+	if (t1)
+		sem_post(&m->runner_block);
+	if (t2) {
 		// this will wake the select() call from sleep
 		if (write(1, "\n", 1))
 			;
-
 		time_t start = time(NULL);
 		time_t now;
 		while (((now = time(NULL)) - start < 2) && m->capture)
 			usleep(1000);
 	}
 	printf("\033[4l\033[;r\033[999;1H"); fflush(stdout);
-	printf("%s done\n", __func__);
+	//printf("%s done\n", __func__);
 	free(m);
 	_mish = NULL;
 }
@@ -223,7 +228,7 @@ _mish_cmd_quit(
 			"mish: Quitting."
 			MISH_COLOR_RESET "\n");
 
-	mish_p m = ((mish_client_p)param)->mish;
+	mish_p m = param;
 	m->flags |= MISH_QUIT;
 }
 
@@ -231,7 +236,7 @@ MISH_CMD_NAMES(quit, "q", "quit");
 MISH_CMD_HELP(quit,
 		"exit running program",
 		"Close all clients and exit(0)");
-MISH_CMD_REGISTER(quit, _mish_cmd_quit);
+MISH_CMD_REGISTER_KIND(quit, _mish_cmd_quit, 0, MISH_CMD_KIND);
 
 #define VT_COL(_c) "\033[" #_c "G"
 
@@ -245,7 +250,7 @@ _mish_cmd_mish(
 			"mish: mish command."
 			MISH_COLOR_RESET "\n");
 
-	mish_p m = ((mish_client_p)param)->mish;
+	mish_p m = param;
 	printf("Backlog: %6d lines (%5dKB)"  VT_COL(40) "Telnet Port: %5d\n",
 			m->backlog.size,
 			(int)m->backlog.alloc / 1024,
@@ -302,5 +307,5 @@ MISH_CMD_HELP(mish,
 		"   also set the maximum lines in the backlog\n"
 		"   (0 = unlimited)\n"
 		"Show status and a few bits of internals.");
-MISH_CMD_REGISTER(mish, _mish_cmd_mish);
+MISH_CMD_REGISTER_KIND(mish, _mish_cmd_mish, 0, MISH_CMD_KIND);