Ver código fonte

Merge branch 'us32-cli-multisession' into 'develop'

US32: CLI Multisession

Closes #36 and #59

See merge request tobias.wach/ccats!60
Serdyukov, Denys 5 anos atrás
pai
commit
6330cc413a

+ 1 - 1
cli/include/batchioman.h

@@ -76,7 +76,7 @@ public:
 	/**
 	 * Constructor and destructor
 	 */
-	BatchIoMan(char *ipcstring, bool usessl, bool beverbose, string batchpath);
+	BatchIoMan(bool usessl, bool beverbose, string batchpath);
 	~BatchIoMan();
 
 	bool init();

+ 9 - 2
cli/include/cmdman.h

@@ -29,7 +29,7 @@ public:
 	 * close	- terminate the connection
 	 * seton	- contextually change state, used for version check and login
 	 */
-	enum rettype { print = (1 << 0), send = (1 << 1), error = (1 << 2), close = (1 << 3), seton = (1 << 4) };
+	enum rettype { none = 0, print = (1 << 1), send = (1 << 2), error = (1 << 3), close = (1 << 4), connect = (1 << 5), exit = (1 << 6) };
 	/**
 	 * Response to user or command input
 	 *
@@ -61,6 +61,8 @@ public:
 	 */
 	Json::CharReader *reader;
 
+	void stateSetConnectionOk();
+
 private:
 	/**
 	 * internal json writer and error string member
@@ -91,7 +93,8 @@ private:
 	 * handlemap and to disallow the use of certain commands when logged in/not
 	 * logged in.
 	 */
-	bool doversion, loginpossible, dologin, dosignup;
+	enum state { connectionpossible, versionpossible, doversion, loginpossible, dologin, dosignup, normal, disconnecttoexit, disconnecttoexitearly };
+	state currentState;
 
 	/**
 	 * Help strings and method prototypes for commands to be used by a user
@@ -117,6 +120,10 @@ private:
 	CmdRet cmdKeyfile(vector<string> args);
 	const string descClosekey = "stop using the previously selected keyfile";
 	CmdRet cmdClosekey(vector<string> args);
+	const string descConnect = "connect to server";
+	CmdRet cmdConnect(vector<string> args);
+	const string descExit = "exit the application";
+	CmdRet cmdExit(vector<string> args);
 
 	const string descLogin = "login to the server";
 	CmdRet cmdLogin(vector<string> args);

+ 1 - 12
cli/include/ioman.h

@@ -59,16 +59,6 @@ protected:
 	virtual void printWelcomeMessage() = 0;
 	virtual std::string getCmdPrompt() = 0;
 
-	/**
-	 * Enum for internal login and version state
-	 * Variables for keeping said state
-	 * Matching mutex and condition variable
-	 */
-	enum Status { off, on, err };
-	Status loginstatus;
-	Status versionstatus;
-	std::mutex initmutex;
-	std::condition_variable initcv;
 	bool connected;
 
 	virtual void handleInCmdResponse(CmdMan::CmdRet cmdret);
@@ -136,8 +126,7 @@ public:
 	/**
 	 * Constructor and destructor
 	 */
-	IoMan(char *ipcstring);
-	IoMan(char *ipcstring, bool enablessl);
+	IoMan(bool enablessl);
 	virtual ~IoMan();
 
 	/**

+ 1 - 1
cli/include/machineioman.h

@@ -14,7 +14,7 @@ private:
 	bool verbose;
 
 public:
-	MachineIoMan(char *ipcstring, bool usessl, bool beverbose);
+	MachineIoMan(bool usessl, bool beverbose);
 
 protected:
 	/**

+ 1 - 1
cli/include/userioman.h

@@ -64,7 +64,7 @@ public:
 	/**
 	 * Constructor and destructor
 	 */
-	UserIoMan(char *ipcstring, bool usessl, bool verbose);
+	UserIoMan(bool usessl, bool verbose);
 	~UserIoMan();
 
 protected:

+ 3 - 21
cli/src/batchioman.cpp

@@ -4,7 +4,7 @@
 #include <string>
 #include <vector>
 
-BatchIoMan::BatchIoMan(char *ipcstring, bool usessl, bool beverbose, std::string batchpath) : IoMan(ipcstring, usessl) {
+BatchIoMan::BatchIoMan(bool usessl, bool beverbose, std::string batchpath) : IoMan(usessl) {
 	/* setup json stuff */
 	Json::CharReaderBuilder rbuilder;
 	wbuilder.settings_["indentation"] = "";
@@ -168,13 +168,6 @@ void BatchIoMan::handleOutCmdResponse(CmdMan::CmdRet cmdret, std::vector<std::st
 		return;
 	}
 	if (cmdret.type & CmdMan::rettype::error) {
-		initmutex.lock();
-		if (versionstatus == off)
-			versionstatus = err;
-		else if (loginstatus == off)
-			loginstatus = err;
-		initmutex.unlock();
-		initcv.notify_all();
 		printMessage(Json::writeString(wbuilder, cmdret.msg), error);
 		mainmutex.lock();
 		runmain = false;
@@ -187,15 +180,6 @@ void BatchIoMan::handleOutCmdResponse(CmdMan::CmdRet cmdret, std::vector<std::st
 		linecv.notify_all();
 		return;
 	}
-	if (cmdret.type & CmdMan::rettype::seton) {
-		initmutex.lock();
-		if (versionstatus == off)
-			versionstatus = on;
-		else
-			loginstatus = on;
-		initmutex.unlock();
-		initcv.notify_all();
-	}
 	if (cmdret.type & CmdMan::rettype::print) {
 		printMessage(Json::writeString(wbuilder, cmdret.msg), normal);
 	}
@@ -205,7 +189,7 @@ void BatchIoMan::handleOutCmdResponse(CmdMan::CmdRet cmdret, std::vector<std::st
 			toput.push_back(cmdret.nextcommand);
 		}
 	}
-	if (versionstatus == on && !(cmdret.type & CmdMan::rettype::send)) {
+	if (!(cmdret.type & CmdMan::rettype::send)) {
 		// only fetch next line if we did not send a new command on our own
 		// if we managed to get here, get next command from file
 		linemutex.lock();
@@ -241,7 +225,7 @@ void BatchIoMan::run() {
 		while (!line.size()) {
 			// skip empty lines until either eof or non-empty line
 			if (batchin.eof()) {
-				line = "disconnect";
+				line = "exit";
 			} else
 				std::getline(batchin, line);
 		}
@@ -258,8 +242,6 @@ void BatchIoMan::run() {
 
 		if (!connected)
 			break;
-		if (loginstatus == err)
-			break;
 
 		mainmutex.lock();
 	}

+ 113 - 33
cli/src/cmdman.cpp

@@ -14,10 +14,7 @@ CmdMan::CmdMan(FileMan &fm, void (*dpf)(string)) : fileman(fm) {
 	wbuilder.settings_["indentation"] = "";
 	reader = rbuilder.newCharReader();
 
-	doversion = false;
-	loginpossible = false;
-	dologin = false;
-	dosignup = false;
+	currentState = connectionpossible;
 
 	/* initialize execute command map */
 	execmap["help"] = &CmdMan::cmdHelp;
@@ -37,6 +34,8 @@ CmdMan::CmdMan(FileMan &fm, void (*dpf)(string)) : fileman(fm) {
 	execmap["deleteme"] = &CmdMan::cmdDeleteme;
 	execmap["keyfile"] = &CmdMan::cmdKeyfile;
 	execmap["closekey"] = &CmdMan::cmdClosekey;
+	execmap["connect"] = &CmdMan::cmdConnect;
+	execmap["exit"] = &CmdMan::cmdExit;
 
 	/* initialize description map */
 	helpmap["help"] = descHelp;
@@ -52,6 +51,8 @@ CmdMan::CmdMan(FileMan &fm, void (*dpf)(string)) : fileman(fm) {
 	helpmap["deleteme"] = descDeleteme;
 	helpmap["keyfile"] = descKeyfile;
 	helpmap["closekey"] = descClosekey;
+	helpmap["connect"] = descConnect;
+	helpmap["exit"] = descExit;
 
 	/* initialize handle command map */
 	handlemap["status"] = &CmdMan::handleStatus;
@@ -74,6 +75,8 @@ CmdMan::CmdMan(FileMan &fm, void (*dpf)(string)) : fileman(fm) {
 
 CmdMan::~CmdMan() { delete reader; }
 
+void CmdMan::stateSetConnectionOk() { currentState = versionpossible; }
+
 CmdMan::CmdRet CmdMan::cmdHelp(vector<string> args) {
 	CmdRet retval;
 	Json::Value root, arr;
@@ -105,13 +108,17 @@ CmdMan::CmdRet CmdMan::cmdDisconnect(vector<string> args) {
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	Json::Value root;
 	retval.type = send;
-	if (loginpossible) {
+	if (currentState == loginpossible || currentState == disconnecttoexitearly) {
 		// not logged in, send appropriate login message instead of normal close
 		root["login"] = false;
 		root["user"] = "";
 		root["pass"] = "";
 		root["cancel"] = true;
 		retval.type |= close;
+		if (currentState == disconnecttoexitearly) {
+			retval.nextcommand = "exit";
+		}
+		currentState = connectionpossible;
 	} else {
 		root["command"] = "close";
 	}
@@ -290,6 +297,30 @@ CmdMan::CmdRet CmdMan::cmdDeletefile(vector<string> args) {
 	return retval;
 }
 
+CmdMan::CmdRet CmdMan::cmdConnect(vector<string> args) {
+	CmdRet retval;
+	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
+	Json::Value root;
+
+	root["command"] = "connect";
+
+	if (args.size() < 1) {
+		retval.type = error;
+		root["error"] = "not enough arguments, at least 1 argument required";
+	} else if (args.size() < 2) {
+		retval.type = connect;
+		root["address"] = args[0];
+		root["port"] = 1234;
+	} else {
+		retval.type = connect;
+		root["address"] = args[0];
+		root["port"] = (unsigned int)stoul(args[1]);
+	}
+	retval.msg = root;
+
+	return retval;
+}
+
 CmdMan::CmdRet CmdMan::execute(string cmd, vector<string> args) {
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " using command \"" + cmd + "\" with arguments [ ");
@@ -306,7 +337,9 @@ CmdMan::CmdRet CmdMan::execute(string cmd, vector<string> args) {
 		root["error"] = string(__PRETTY_FUNCTION__) + " unknown command \"" + cmd + "\".\ntype help to list available commands.";
 		retval.msg = root;
 		return retval;
-	} else if (loginpossible || dologin || dosignup) {
+	} else if (!cmd.compare("help") || !cmd.compare("exit")) {
+		// allow help and exit in all cases
+	} else if (currentState == loginpossible || currentState == dologin || currentState == dosignup) {
 		DEBUGPRINT("execute does login");
 		DEBUGPRINT(string("comparison is ") +
 		           std::to_string(cmd.compare("login") && cmd.compare("signup") && cmd.compare("disconnect") && cmd.compare("help")));
@@ -318,6 +351,26 @@ CmdMan::CmdRet CmdMan::execute(string cmd, vector<string> args) {
 			retval.msg = root;
 			return retval;
 		}
+	} else if (currentState == versionpossible || currentState == doversion) {
+		DEBUGPRINT("execute does version");
+		DEBUGPRINT(string("comparison is ") + std::to_string(cmd.compare("version")));
+		if (cmd.compare("version")) {
+			retval.type = error;
+			root["command"] = "error";
+			root["error"] = string("Version not checked yet. No commands avalable.");
+			retval.msg = root;
+			return retval;
+		}
+	} else if (currentState == connectionpossible) {
+		DEBUGPRINT("execute does connect");
+		DEBUGPRINT(string("comparison is ") + std::to_string(cmd.compare("connect")));
+		if (cmd.compare("version") && cmd.compare("connect")) {
+			retval.type = error;
+			root["command"] = "error";
+			root["error"] = string("Not connected. Connect using \"connect ip [port]\".");
+			retval.msg = root;
+			return retval;
+		}
 	}
 	return (this->*(execmap[cmd]))(args);
 }
@@ -387,6 +440,26 @@ CmdMan::CmdRet CmdMan::cmdClosekey(vector<string> args) {
 	return retval;
 }
 
+CmdMan::CmdRet CmdMan::cmdExit(vector<string> args) {
+	CmdRet retval;
+	Json::Value root;
+	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
+	root["command"] = "exit";
+	if (currentState != connectionpossible) {
+		// we are connected, disconnect first
+		retval.nextcommand = "disconnect";
+		if (currentState == loginpossible)
+			currentState = disconnecttoexitearly;
+		else
+			currentState = disconnecttoexit;
+		retval.type = none;
+	} else {
+		retval.type = exit;
+	}
+	retval.msg = root;
+	return retval;
+}
+
 /* login and signup commands */
 CmdMan::CmdRet CmdMan::cmdLogin(vector<string> args) {
 	CmdRet retval;
@@ -399,9 +472,8 @@ CmdMan::CmdRet CmdMan::cmdLogin(vector<string> args) {
 		root["accept"] = false;
 		root["error"] = "not enough arguments, at least 2 argument required";
 	} else {
-		if (loginpossible) {
-			dologin = true;
-			loginpossible = false;
+		if (currentState == loginpossible) {
+			currentState = dologin;
 			root["user"] = args[0];
 			root["pass"] = args[1];
 			root["login"] = true;
@@ -431,9 +503,8 @@ CmdMan::CmdRet CmdMan::cmdSignup(vector<string> args) {
 		root["accept"] = false;
 		root["error"] = "not enough arguments, at least 2 argument required";
 	} else {
-		if (loginpossible) {
-			dosignup = true;
-			loginpossible = false;
+		if (currentState == loginpossible) {
+			currentState = dosignup;
 			root["user"] = args[0];
 			root["pass"] = args[1];
 			root["login"] = false;
@@ -457,13 +528,18 @@ CmdMan::CmdRet CmdMan::cmdVersion(vector<string> args) {
 	CmdRet retval;
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	Json::Value root;
-	root["major"] = protocolMajorVersion;
-	root["minor"] = protocolMinorVersion;
-	retval.type = send;
+	if (currentState == versionpossible) {
+		root["major"] = protocolMajorVersion;
+		root["minor"] = protocolMinorVersion;
+		retval.type = send;
+		currentState = doversion;
+	} else {
+		retval.type = error;
+		root["command"] = "error";
+		root["error"] = "Executing version command not possible. Type help to list available commands.";
+	}
 	retval.msg = root;
 
-	doversion = true;
-
 	return retval;
 }
 
@@ -471,11 +547,11 @@ CmdMan::CmdRet CmdMan::handle(Json::Value root) {
 	CmdRet retval;
 	Json::Value output;
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
-	if (doversion)
+	if (currentState == doversion)
 		root["command"] = "version";
-	else if (dosignup)
+	else if (currentState == dosignup)
 		root["command"] = "signup";
-	else if (dologin)
+	else if (currentState == dologin)
 		root["command"] = "login";
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " using json\n" + Json::writeString(wbuilder, root) + "\n");
 	string retmsg;
@@ -509,6 +585,11 @@ CmdMan::CmdRet CmdMan::handleClose(Json::Value root) {
 	retval.type = close | print;
 	retval.msg = output;
 
+	if (currentState == disconnecttoexit) {
+		retval.nextcommand = "exit";
+	}
+	currentState = connectionpossible;
+
 	return retval;
 }
 
@@ -723,17 +804,17 @@ CmdMan::CmdRet CmdMan::handleVersion(Json::Value root) {
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 
 	output["command"] = "version";
-	output["serverversion"] = root["major"].asString() + "." + root["minor"].asString();
+	output["serverversion"] = std::to_string(root["major"].asInt()) + "." + std::to_string(root["minor"].asInt());
 	output["clientversion"] = std::to_string(protocolMajorVersion) + "." + std::to_string(protocolMinorVersion);
 
 	if (!root["accept"].asBool()) {
-		retval.type = error;
+		retval.type = error | close;
 		output["accept"] = false;
+		currentState = connectionpossible;
 	} else {
-		retval.type = print | seton;
+		retval.type = print;
 		output["accept"] = true;
-		doversion = false;
-		loginpossible = true;
+		currentState = loginpossible;
 	}
 	retval.msg = output;
 
@@ -751,13 +832,12 @@ CmdMan::CmdRet CmdMan::handleLogin(Json::Value root) {
 		retval.type = error;
 		output["error"] = root["error"].asString();
 		output["accept"] = false;
-		loginpossible = true;
-		dologin = false;
+		currentState = loginpossible;
 	} else {
-		retval.type = print | seton;
+		retval.type = print;
 		output["error"] = "";
 		output["accept"] = true;
-		dologin = false;
+		currentState = normal;
 	}
 	retval.msg = output;
 
@@ -775,13 +855,12 @@ CmdMan::CmdRet CmdMan::handleSignup(Json::Value root) {
 		retval.type = error;
 		output["error"] = root["error"].asString();
 		output["accept"] = false;
-		loginpossible = true;
-		dosignup = false;
+		currentState = loginpossible;
 	} else {
-		retval.type = print | seton;
+		retval.type = print;
 		output["error"] = "";
 		output["accept"] = true;
-		dosignup = false;
+		currentState = normal;
 	}
 	retval.msg = output;
 
@@ -834,6 +913,7 @@ CmdMan::CmdRet CmdMan::handleDeleteme(Json::Value root) {
 		retval.type = error;
 	} else {
 		retval.type = close | print;
+		currentState = connectionpossible;
 	}
 	retval.msg = root;
 	return retval;

+ 60 - 68
cli/src/ioman.cpp

@@ -25,12 +25,11 @@ extern IoMan *gIOMAN;
 
 void ioman_externalDebugPrint(string msg) { gIOMAN->printMessage(msg, gIOMAN->OutMsgType::debug); }
 
-IoMan::IoMan(char *ipcstring, bool enablessl) : cmdman(fileman, &ioman_externalDebugPrint), recvbuf(16384) {
-	ipstring = std::string(ipcstring);
-	port = 1234;
+IoMan::IoMan(bool enablessl) : cmdman(fileman, &ioman_externalDebugPrint), recvbuf(16384) {
+	ipstring = "";
+	port = 0;
 	tcpsock = new tcp::socket(ios);
 	connected = false;
-	usessl = false;
 
 	/* to be put elsewhere */
 
@@ -41,8 +40,6 @@ IoMan::IoMan(char *ipcstring, bool enablessl) : cmdman(fileman, &ioman_externalD
 	runnetwork = false;
 	runinput = false;
 	runresponse = false;
-	versionstatus = off;
-	loginstatus = off;
 	usessl = enablessl;
 	if (usessl) {
 		sslctx = new boost::asio::ssl::context(boost::asio::ssl::context::sslv23);
@@ -54,8 +51,6 @@ IoMan::IoMan(char *ipcstring, bool enablessl) : cmdman(fileman, &ioman_externalD
 }
 
 IoMan::~IoMan() {
-	initcv.notify_all();
-
 	if (connected) {
 		disconnect();
 	}
@@ -161,6 +156,8 @@ bool IoMan::connect() {
 		root["error"] = errcode.message();
 		connected = false;
 	} else {
+		if(!ios.stopped()) ios.stop();
+		ios.restart();
 		// establish connection
 		printMessage(string(__PRETTY_FUNCTION__) + string(" connecting to ") + ipstring, debug);
 		ep = new tcp::endpoint(addr, port);
@@ -202,7 +199,10 @@ void IoMan::disconnect() {
 	printMessage("IoMan::disconnect()", debug);
 	tcpsock->shutdown(tcp::socket::shutdown_both, errcode);
 	if (errcode)
-		printMessage(string(__PRETTY_FUNCTION__) + string("tcp shutdown says ") + errcode.message(), error);
+		printMessage(string(__PRETTY_FUNCTION__) + string("tcp shutdown says ") + errcode.message(), debug);
+	tcpsock->close(errcode);
+	if (errcode)
+		printMessage(string(__PRETTY_FUNCTION__) + string("tcp shutdown says ") + errcode.message(), debug);
 	connected = false;
 }
 
@@ -212,43 +212,13 @@ bool IoMan::init() {
 	Json::Value root;
 	std::unique_lock<std::mutex> ulock;
 
-	printMessage("IoMan::Init() begin", debug);
-
-	if (!connect())
-		return false;
-
-	printMessage("IoMan::Init() versioncheck", debug);
-
-	localmutex.lock();
-	printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"), debug);
-	localinput.push_back("version");
-	localmutex.unlock();
-	printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"), debug);
-	localcv.notify_all();
-
-	printMessage("IoMan::Init() begin", debug);
-
-	runnetwork = true;
+	printMessage(string(__PRETTY_FUNCTION__) + string(" begin"), debug);
 	runinput = true;
 	runresponse = true;
 
-	tnetwork = std::thread(&IoMan::networkMain, this);
 	tinput = std::thread(&IoMan::inputMain, this);
 	tresponse = std::thread(&IoMan::responseMain, this);
 
-	ulock = std::unique_lock<std::mutex>(initmutex);
-	while (versionstatus == off) {
-		initcv.wait(ulock);
-	}
-	if (versionstatus == err) {
-		runnetwork = false;
-		runinput = false;
-		runresponse = false;
-		return false;
-	}
-	initmutex.unlock();
-	initcv.notify_all();
-
 	printWelcomeMessage();
 	return true;
 }
@@ -341,7 +311,7 @@ void IoMan::inputMain() {
 	CmdMan::CmdRet cmdret;
 	std::unique_lock<std::mutex> ulock;
 
-	printMessage("IoMan::inputMain() begin", debug);
+	printMessage(string(__PRETTY_FUNCTION__) + string(" begin"), debug);
 	inputmutex.lock();
 	while (runinput) {
 		inputmutex.unlock();
@@ -408,11 +378,43 @@ void IoMan::handleInCmdResponse(CmdMan::CmdRet cmdret) {
 		printMessage(Json::writeString(wbuilder, cmdret.msg), error);
 	}
 	if (cmdret.type & CmdMan::rettype::close) {
-		/* TODO i dunno */
+		// connection closed, stop network thread and shutdown any operations remaining
+		networkmutex.lock();
+		runnetwork = false;
+		networkmutex.unlock();
+		disconnect();
+		tnetwork.join();
+	}
+	if (cmdret.type & CmdMan::rettype::connect) {
+		ipstring = cmdret.msg["address"].asString();
+		port = cmdret.msg["port"].asUInt();
+		if (connect()) {
+			runnetwork = true;
+			tnetwork = std::thread(&IoMan::networkMain, this);
+
+			// put new commands into global vector
+			localmutex.lock();
+			printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"), debug);
+			localinput.push_back("version");
+			cmdman.stateSetConnectionOk();
+			localmutex.unlock();
+			printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"), debug);
+			localcv.notify_all();
+		}
+	}
+	if (cmdret.type & CmdMan::rettype::exit) {
 		mainmutex.lock();
 		runmain = false;
 		mainmutex.unlock();
 	}
+	if (cmdret.nextcommand.size()) {
+		localmutex.lock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"), debug);
+		localinput.push_back(cmdret.nextcommand);
+		localmutex.unlock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"), debug);
+		localcv.notify_all();
+	}
 }
 
 /* loop to handle responses that have been fetched by netMain and possibly add
@@ -423,7 +425,7 @@ void IoMan::responseMain() {
 	CmdMan::CmdRet cmdret;
 	std::unique_lock<std::mutex> ulock;
 
-	printMessage("IoMan::responseMain() begin", debug);
+	printMessage(string(__PRETTY_FUNCTION__) + string(" begin"), debug);
 	responsemutex.lock();
 	while (runresponse) {
 		responsemutex.unlock();
@@ -482,30 +484,19 @@ void IoMan::responseMain() {
 
 void IoMan::handleOutCmdResponse(CmdMan::CmdRet cmdret, vector<string> &toput) {
 	if (cmdret.type & CmdMan::rettype::close) {
-		/* TODO i dunno */
-		mainmutex.lock();
-		runmain = false;
-		mainmutex.unlock();
+		// connection closed, stop network thread and shutdown any operations remaining
+		networkmutex.lock();
+		runnetwork = false;
+		networkmutex.unlock();
+		disconnect();
+		tnetwork.join();
+		if (cmdret.nextcommand.size()) {
+			toput.push_back(cmdret.nextcommand);
+		}
 	}
 	if (cmdret.type & CmdMan::rettype::error) {
-		initmutex.lock();
-		if (versionstatus == off)
-			versionstatus = err;
-		else if (loginstatus == off)
-			loginstatus = err;
-		initmutex.unlock();
-		initcv.notify_all();
 		printMessage(Json::writeString(wbuilder, cmdret.msg), error);
 	}
-	if (cmdret.type & CmdMan::rettype::seton) {
-		initmutex.lock();
-		if (versionstatus == off)
-			versionstatus = on;
-		else
-			loginstatus = on;
-		initmutex.unlock();
-		initcv.notify_all();
-	}
 	if (cmdret.type & CmdMan::rettype::print) {
 		printMessage(Json::writeString(wbuilder, cmdret.msg), normal);
 	}
@@ -514,6 +505,11 @@ void IoMan::handleOutCmdResponse(CmdMan::CmdRet cmdret, vector<string> &toput) {
 			toput.push_back(cmdret.nextcommand);
 		}
 	}
+	if (cmdret.type & CmdMan::rettype::exit) {
+		mainmutex.lock();
+		runmain = false;
+		mainmutex.unlock();
+	}
 }
 
 /* this is the handler that readlines alternative interface will use to process
@@ -544,7 +540,7 @@ void ioman_readlineHandler(char *line) {
 
 /* main user input loop */
 void IoMan::run() {
-	printMessage("IoMan::run() begin", debug);
+	printMessage(string(__PRETTY_FUNCTION__) + string(" begin"), debug);
 	struct pollfd inpipestatus;
 	inpipestatus.fd = STDIN_FILENO;
 	inpipestatus.events = POLLIN;
@@ -563,10 +559,6 @@ void IoMan::run() {
 		if (inpipestatus.revents & POLLIN) {
 			rl_callback_read_char();
 		}
-		if (!connected)
-			break;
-		if (loginstatus == err)
-			break;
 		mainmutex.lock();
 	}
 	mainmutex.unlock();

+ 1 - 1
cli/src/machineioman.cpp

@@ -3,7 +3,7 @@
 #include <iostream>
 #include <vector>
 
-MachineIoMan::MachineIoMan(char *ipcstring, bool usessl, bool beverbose) : IoMan(ipcstring, usessl) { verbose = beverbose; }
+MachineIoMan::MachineIoMan(bool usessl, bool beverbose) : IoMan(usessl) { verbose = beverbose; }
 
 void MachineIoMan::printMessage(std::string msg, OutMsgType type) {
 	switch (type) {

+ 6 - 13
cli/src/main.cpp

@@ -13,8 +13,7 @@ IoMan *gIOMAN = NULL;
 
 void show_help(char *exec) {
 	std::printf("ccats command line interface\n");
-	std::printf("usage: %s IP [Options]\n", exec);
-	std::printf("IP should be in the format \"xxx.xxx.xxx.xxx\"\n");
+	std::printf("usage: %s [Options]\n", exec);
 }
 
 int main(int argc, char **argv) {
@@ -28,14 +27,8 @@ int main(int argc, char **argv) {
 	const char *file = NULL;
 	IoMan *ioman;
 
-	if (argc < 2 || !std::strncmp(argv[1], "--help", 6)) {
-		show_help(argv[0]);
-		std::cout << desc;
-		return 1;
-	}
-
 	try {
-		store(parse_command_line(argc - 1, argv + 1, desc), vm);
+		store(parse_command_line(argc, argv, desc), vm);
 		notify(vm);
 
 		if (vm.count("help")) {
@@ -61,13 +54,13 @@ int main(int argc, char **argv) {
 	} catch (const bpo::error &ex) {
 		std::fprintf(stderr, "%s\n", ex.what());
 	}
-	std::printf("ip %s machine mode is %d file is %s enablessl is %d verbose is %d\n", argv[1], machine, file ? file : "", usessl, verbose);
+	std::printf("machine mode is %d file is %s enablessl is %d verbose is %d\n", machine, file ? file : "", usessl, verbose);
 	if (batch) {
-		ioman = new BatchIoMan(argv[1], usessl, verbose, file);
+		ioman = new BatchIoMan(usessl, verbose, file);
 	} else if (machine) {
-		ioman = new MachineIoMan(argv[1], usessl, verbose);
+		ioman = new MachineIoMan(usessl, verbose);
 	} else {
-		ioman = new UserIoMan(argv[1], usessl, verbose);
+		ioman = new UserIoMan(usessl, verbose);
 	}
 	gIOMAN = ioman;
 	if (ioman->init()) {

+ 1 - 1
cli/src/userioman.cpp

@@ -4,7 +4,7 @@
 #include <readline/readline.h>
 #include <vector>
 
-UserIoMan::UserIoMan(char *ipcstring, bool usessl, bool beverbose) : IoMan(ipcstring, usessl) {
+UserIoMan::UserIoMan(bool usessl, bool beverbose) : IoMan(usessl) {
 	/* setup json stuff */
 	Json::CharReaderBuilder rbuilder;
 	wbuilder.settings_["indentation"] = "";