Browse Source

Begin rework into allowing multiple server sessions from one CLI session
Can connect and disconnect multiple times from the same CLI session
Can not exit from CLI without Ctrl-C

Sander, Paul 4 years ago
parent
commit
b5558de178

+ 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"] = "";