ioman.cpp 15 KB

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