ioman.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. #include "../include/ioman.h"
  2. #include "../include/global.h"
  3. #include <boost/asio.hpp>
  4. #include <iostream>
  5. #include <boost/algorithm/string.hpp>
  6. #include <string>
  7. #include <vector>
  8. #include <readline/history.h>
  9. #include <readline/readline.h>
  10. using std::string;
  11. using std::vector;
  12. using boost::asio::buffer;
  13. using boost::asio::ip::tcp;
  14. IoMan::IoMan(char *ipcstring) : cmdman(fileman) {
  15. ipstring = std::string(ipcstring);
  16. port = 1234;
  17. tcpsock = new tcp::socket(ios);
  18. connected = false;
  19. /* to be put elsewhere */
  20. /* setup json stuff */
  21. Json::CharReaderBuilder rbuilder;
  22. wbuilder.settings_["indentation"] = "";
  23. reader = rbuilder.newCharReader();
  24. runnetwork = false;
  25. runinput = false;
  26. runresponse = false;
  27. startlist = false;
  28. versionstatus = off;
  29. loginstatus = off;
  30. }
  31. IoMan::~IoMan() {
  32. networkmutex.lock();
  33. inputmutex.lock();
  34. responsemutex.lock();
  35. runnetwork = false;
  36. runinput = false;
  37. runresponse = false;
  38. networkmutex.unlock();
  39. inputmutex.unlock();
  40. responsemutex.unlock();
  41. initcv.notify_all();
  42. localcv.notify_all();
  43. netcv.notify_all();
  44. tnetwork.join();
  45. tinput.join();
  46. tresponse.join();
  47. if (connected) {
  48. disconnect();
  49. }
  50. delete tcpsock;
  51. delete reader;
  52. }
  53. void IoMan::printMessage(string nouse, OutMsgType nouse2) {}
  54. bool IoMan::connect() {
  55. tcp::endpoint *ep;
  56. ep = new tcp::endpoint(boost::asio::ip::address::from_string(ipstring), 1234);
  57. // establish connection
  58. printMessage("IoMan::connect() connecting to " + ipstring, debug);
  59. tcpsock->connect(*ep, errcode);
  60. if (errcode) {
  61. delete ep;
  62. printMessage("IoMan::connect() couldnt connect to " + ipstring + "\n" +
  63. errcode.message(),
  64. error);
  65. return false;
  66. }
  67. connected = true;
  68. delete ep;
  69. return true;
  70. }
  71. void IoMan::disconnect() {
  72. printMessage("IoMan::disconnect()", debug);
  73. tcpsock->close();
  74. connected = false;
  75. }
  76. bool IoMan::init() {
  77. CmdMan::CmdRet ret;
  78. string work;
  79. Json::Value root;
  80. std::unique_lock<std::mutex> ulock;
  81. printMessage("IoMan::Init() begin", debug);
  82. if (!connect())
  83. return false;
  84. printMessage("IoMan::Init() versioncheck", debug);
  85. localmutex.lock();
  86. printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"), debug);
  87. localinput.push_back("version " + protocolVersion);
  88. localmutex.unlock();
  89. printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"),
  90. debug);
  91. localcv.notify_all();
  92. printMessage("IoMan::Init() begin", debug);
  93. runnetwork = true;
  94. runinput = true;
  95. runresponse = true;
  96. tnetwork = std::thread(&IoMan::networkMain, this);
  97. tinput = std::thread(&IoMan::inputMain, this);
  98. tresponse = std::thread(&IoMan::responseMain, this);
  99. ulock = std::unique_lock<std::mutex>(initmutex);
  100. while (versionstatus == off) {
  101. initcv.wait(ulock);
  102. }
  103. if (versionstatus == err) {
  104. runnetwork = false;
  105. runinput = false;
  106. runresponse = false;
  107. return false;
  108. }
  109. initmutex.unlock();
  110. initcv.notify_all();
  111. printWelcomeMessage();
  112. return true;
  113. }
  114. void IoMan::networkMain() {
  115. vector<Json::Value> toput;
  116. char *recvjson;
  117. Json::Value root;
  118. unsigned int jsonsize, readsize;
  119. printMessage("IoMan::networkMain() begin", debug);
  120. networkmutex.lock();
  121. while (runnetwork) {
  122. networkmutex.unlock();
  123. /*
  124. read from network until \n
  125. try to parse json
  126. - output error if not ok
  127. store all ok json in local vector
  128. get networkmutex
  129. put all local jsons into network vector
  130. release networkmutex
  131. */
  132. // read from network
  133. readsize = boost::asio::read_until(*tcpsock, recvbuf, '\n', errcode);
  134. printMessage(string(__PRETTY_FUNCTION__) + string(" asio::read() ok ") +
  135. std::to_string(readsize),
  136. debug);
  137. // printMessage(string("have ") + std::to_string(toprocess.size()) +
  138. // string(" commands"), debug);
  139. if (errcode && errcode != boost::asio::error::eof) {
  140. printMessage("IoMan::networkMain() couldnt read json data\n" +
  141. errcode.message(),
  142. error);
  143. continue;
  144. }
  145. if (!readsize)
  146. break;
  147. recvjson = (char *)(boost::asio::buffer_cast<const char *>(recvbuf.data()));
  148. recvjson[readsize] = 0;
  149. while (strchr(recvjson, '\n')) {
  150. // parse
  151. jsonsize = strchr(recvjson, '\n') - recvjson + 1;
  152. printMessage(string(__PRETTY_FUNCTION__) + string(" found jsondata ") +
  153. string(recvjson),
  154. debug);
  155. if (!reader->parse(recvjson, recvjson + jsonsize, &root, &jsonerror)) {
  156. printMessage("IoMan::networkMain() couldnt parse json data: " +
  157. jsonerror,
  158. error);
  159. recvbuf.consume(jsonsize);
  160. continue;
  161. }
  162. recvbuf.consume(jsonsize);
  163. readsize -= jsonsize;
  164. printMessage(
  165. string(__PRETTY_FUNCTION__) + string(" remaining recvbuf ") +
  166. string(boost::asio::buffer_cast<const char *>(recvbuf.data())),
  167. debug);
  168. for (int i = 0; i < jsonsize; i++)
  169. recvjson++;
  170. // store locally
  171. toput.push_back(root);
  172. }
  173. if (toput.size()) {
  174. // put into global vector
  175. netmutex.lock();
  176. printMessage(string(__PRETTY_FUNCTION__) + string(" get netmutex"),
  177. debug);
  178. netinput.insert(netinput.end(), toput.begin(), toput.end());
  179. netmutex.unlock();
  180. printMessage(string(__PRETTY_FUNCTION__) + string(" release netmutex"),
  181. debug);
  182. }
  183. netcv.notify_all();
  184. // clean up local stuff
  185. toput = vector<Json::Value>();
  186. recvbuf.consume(readsize);
  187. networkmutex.lock();
  188. }
  189. }
  190. void IoMan::inputMain() {
  191. vector<string> toprocess;
  192. string command;
  193. vector<string> args;
  194. CmdMan::CmdRet cmdret;
  195. std::unique_lock<std::mutex> ulock;
  196. size_t prev, index, quot;
  197. printMessage("IoMan::inputMain() begin", debug);
  198. inputmutex.lock();
  199. while (runinput) {
  200. inputmutex.unlock();
  201. /*
  202. get inputmutex
  203. read all input vector into local vector
  204. release inputmutex
  205. process inputs
  206. send to server if required
  207. */
  208. // read into local vector
  209. ulock = std::unique_lock<std::mutex>(localmutex);
  210. while (!localinput.size() && runinput) {
  211. localcv.wait(ulock);
  212. }
  213. printMessage(string(__PRETTY_FUNCTION__) + string(" has localmutex"),
  214. debug);
  215. toprocess = vector<string>(localinput);
  216. localinput = vector<string>();
  217. localmutex.unlock();
  218. printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"),
  219. debug);
  220. localcv.notify_all();
  221. if (!runinput)
  222. return;
  223. // printMessage(string("have ") + std::to_string(toprocess.size()) +
  224. // string(" commands"), debug);
  225. // process
  226. for (string cmd : toprocess) {
  227. args = vector<string>();
  228. /* tokenize string into command and arguments vector*/
  229. if ((index = cmd.find(" ")) == string::npos) {
  230. // only command no args
  231. command = cmd;
  232. } else {
  233. command = cmd.substr(0, index);
  234. //~ index++;
  235. bool end_tokenizing = false;
  236. while (!end_tokenizing) {
  237. // find first char thats not a space
  238. while (cmd[index] == ' ') {
  239. index++;
  240. // bounds check
  241. if (index == cmd.size())
  242. end_tokenizing = true;
  243. }
  244. if (end_tokenizing)
  245. break;
  246. cmd = cmd.substr(index);
  247. if (cmd[0] == '\"') {
  248. // quoted string
  249. index = cmd.find("\"");
  250. args.push_back(cmd.substr(1, index - 1));
  251. index++;
  252. /*
  253. tokens.push_back(cmd.substr(0, ++index));
  254. */
  255. // char after closing quote should be space while within bounds
  256. if (index == cmd.size())
  257. end_tokenizing = true;
  258. } else {
  259. // non-quoted string
  260. index = cmd.find(" ");
  261. if (index == string::npos) { // no spaces, last arg
  262. args.push_back(cmd);
  263. end_tokenizing = true;
  264. } else {
  265. args.push_back(cmd.substr(0, index));
  266. }
  267. }
  268. }
  269. }
  270. cmdret = cmdman.execute(command, args);
  271. // determine wether to send something and do so if required
  272. switch (cmdret.type) {
  273. case CmdMan::rettype::send: {
  274. printMessage("IoMan::inputMain() sending json \"" + cmdret.msg + "\"",
  275. debug);
  276. boost::asio::write(*tcpsock, buffer(cmdret.msg + "\n"), errcode);
  277. if (errcode) {
  278. printMessage("IoMan::inputMain() couldnt send json data\n" +
  279. errcode.message() + "\n",
  280. error);
  281. continue;
  282. }
  283. break;
  284. }
  285. case CmdMan::rettype::notsend: {
  286. printMessage(cmdret.msg, normal);
  287. break;
  288. }
  289. case CmdMan::rettype::error: {
  290. printMessage(cmdret.msg, error);
  291. break;
  292. }
  293. }
  294. }
  295. // clean up local stuff
  296. toprocess = vector<string>();
  297. inputmutex.lock();
  298. }
  299. }
  300. void IoMan::responseMain() {
  301. vector<Json::Value> toprocess;
  302. vector<string> toput;
  303. CmdMan::CmdRet cmdret;
  304. std::unique_lock<std::mutex> ulock;
  305. printMessage("IoMan::responseMain() begin", debug);
  306. responsemutex.lock();
  307. while (runresponse) {
  308. responsemutex.unlock();
  309. /*
  310. get networkmutex
  311. read all network vector into local vector
  312. release networkmutex
  313. process all jsons
  314. process putdata
  315. process getdata
  316. process listdata
  317. get inputmutex
  318. place new commands into input vector
  319. release inputmutex
  320. */
  321. // read into local vector
  322. ulock = std::unique_lock<std::mutex>(netmutex);
  323. while (!netinput.size() && runresponse) {
  324. netcv.wait(ulock);
  325. }
  326. printMessage(string(__PRETTY_FUNCTION__) + string(" get netmutex"), debug);
  327. toprocess = vector<Json::Value>(netinput);
  328. netinput = vector<Json::Value>();
  329. netmutex.unlock();
  330. printMessage(string(__PRETTY_FUNCTION__) + string(" release netmutex"),
  331. debug);
  332. netcv.notify_all();
  333. if (!runresponse)
  334. return;
  335. // process jsons
  336. for (Json::Value root : toprocess) {
  337. cmdret = cmdman.handle(root);
  338. switch (cmdret.type) {
  339. case CmdMan::rettype::startlist: {
  340. startlist = true;
  341. break;
  342. }
  343. case CmdMan::rettype::stoplist: {
  344. startlist = false;
  345. break;
  346. }
  347. case CmdMan::rettype::close: {
  348. /* TODO i dunno */
  349. mainmutex.lock();
  350. runmain = false;
  351. mainmutex.unlock();
  352. break;
  353. }
  354. case CmdMan::rettype::error: {
  355. initmutex.lock();
  356. if (versionstatus == off)
  357. versionstatus = err;
  358. else if (loginstatus == off)
  359. loginstatus = err;
  360. initmutex.unlock();
  361. initcv.notify_all();
  362. printMessage(cmdret.msg, error);
  363. break;
  364. }
  365. case CmdMan::rettype::seton: {
  366. initmutex.lock();
  367. if (versionstatus == off)
  368. versionstatus = on;
  369. else
  370. loginstatus = on;
  371. initmutex.unlock();
  372. initcv.notify_all();
  373. }
  374. case CmdMan::rettype::notsend: {
  375. printMessage(cmdret.msg, normal);
  376. break;
  377. }
  378. case CmdMan::rettype::send: {
  379. toput.push_back(cmdret.msg);
  380. break;
  381. }
  382. }
  383. }
  384. if (toput.size()) {
  385. // put new commands into global vector
  386. localmutex.lock();
  387. printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"),
  388. debug);
  389. localinput.insert(localinput.end(), toput.begin(), toput.end());
  390. localmutex.unlock();
  391. printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"),
  392. debug);
  393. }
  394. localcv.notify_all();
  395. // clean up local stuff
  396. toprocess = vector<Json::Value>();
  397. toput = vector<string>();
  398. responsemutex.lock();
  399. }
  400. }
  401. void IoMan::run() {
  402. printMessage("IoMan::run() begin", debug);
  403. char *line = NULL, *user = NULL, *pass = NULL;
  404. vector<string> tokens;
  405. string work, command;
  406. CmdMan::CmdRet cmdret;
  407. Json::Value root;
  408. std::unique_lock<std::mutex> ulock;
  409. runmain = true;
  410. while (!user) {
  411. user = readline(getUserPrompt().c_str());
  412. printMessage("Using user: " + string(user), error);
  413. }
  414. while (!pass) {
  415. pass = readline(getPassPrompt().c_str());
  416. printMessage("Using pass: " + string(pass), error);
  417. }
  418. printMessage("IoMan::run() login", debug);
  419. localmutex.lock();
  420. printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"), debug);
  421. localinput.push_back("login " + string(user) + " " + string(pass));
  422. localmutex.unlock();
  423. printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"),
  424. debug);
  425. free(user);
  426. free(pass);
  427. localcv.notify_all();
  428. ulock = std::unique_lock<std::mutex>(initmutex);
  429. while (loginstatus == off) {
  430. initcv.wait(ulock);
  431. }
  432. if (loginstatus == err)
  433. return;
  434. initcv.notify_all();
  435. mainmutex.lock();
  436. while (runmain) {
  437. mainmutex.unlock();
  438. free(line);
  439. line = readline(getCmdPrompt().c_str());
  440. if (!runmain)
  441. break;
  442. // if no input, do not add to history, and go to reading next line
  443. if (strlen(line) == 0) {
  444. continue;
  445. }
  446. // split input line into tokens
  447. boost::algorithm::split(tokens, std::string(line),
  448. boost::algorithm::is_any_of(" "),
  449. boost::algorithm::token_compress_on);
  450. // if input contains only spaces, do not add to history, and go to reading
  451. // next line
  452. if (tokens.size() < 1) {
  453. continue;
  454. }
  455. // add the line to history
  456. add_history(line);
  457. localmutex.lock();
  458. printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"),
  459. debug);
  460. localinput.push_back(line);
  461. localmutex.unlock();
  462. printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"),
  463. debug);
  464. localcv.notify_all();
  465. if (!connected)
  466. break;
  467. mainmutex.lock();
  468. }
  469. free(line);
  470. }