console.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. // Copyright (c) Athena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "../common/plugin.h"
  4. #ifdef WIN32
  5. #define WIN32_LEAN_AND_MEAN
  6. #include <windows.h>
  7. #else
  8. #define __USE_XOPEN
  9. #include <features.h>
  10. #include <sys/types.h>
  11. #include <unistd.h>
  12. #include <poll.h>
  13. #include <string.h>
  14. #endif
  15. #include <stdio.h> // stdin, fgets
  16. #define INPUT_BUFSIZE 4096
  17. #define INPUT_INVALID 0
  18. #define INPUT_READY 1
  19. #define INPUT_WAITING 2
  20. #define INPUT_READING 3
  21. #define INPUT_CLOSED 4
  22. //////////////////////////////
  23. #ifdef WIN32
  24. //////////////////////////////
  25. // In windows the worker is a thread so it can access the same variables.
  26. #define WORKER_FUNC_DECLARE(name) DWORD WINAPI worker_ ## name(LPVOID lpParameter)
  27. #define WORKER_FUNC_START(name) DWORD WINAPI worker_ ## name(LPVOID lpParameter) { (void)lpParameter; {
  28. #define WORKER_FUNC_END(name) } ExitThread(0); return 0; }
  29. #define WORKER_EXECUTE(name,errvar) \
  30. do{ \
  31. buf.worker = CreateThread(NULL, 0, worker_ ## name, NULL, CREATE_SUSPENDED, NULL); \
  32. if( errvar ) \
  33. *errvar = ( buf.worker == NULL ); \
  34. }while(0)
  35. /// Buffer for asynchronous input
  36. typedef struct _buffer {
  37. char arr[INPUT_BUFSIZE];
  38. size_t len;
  39. HANDLE worker;
  40. HANDLE state_mux; // mutex for the state
  41. char state;
  42. } BUFFER;
  43. //////////////////////////////
  44. #else
  45. //////////////////////////////
  46. /// In linux the worker is a process so it needs to comunicate through pipes.
  47. #define WORKER_FUNC_DECLARE(name) void worker_ ## name(void)
  48. #define WORKER_FUNC_START(name) void worker_ ## name(void) {
  49. #define WORKER_FUNC_END(name) _exit(0); }
  50. #define WORKER_EXECUTE(name,errvar) \
  51. do{ \
  52. int pid = fork(); \
  53. if( pid == 0 ){ \
  54. worker_ ## name(); \
  55. } \
  56. if( errvar ) \
  57. *errvar = (pid == -1); \
  58. }while(0)
  59. #define PIPE_READ 0
  60. #define PIPE_WRITE 1
  61. /// Buffer for asynchronous input
  62. typedef struct _buffer {
  63. char arr[INPUT_BUFSIZE];
  64. size_t len;
  65. int data_pipe[2]; // pipe to receive data
  66. int state_pipe[2]; // pipe to send state
  67. char state;
  68. unsigned close_unused_flag : 1;
  69. } BUFFER;
  70. //////////////////////////////
  71. #endif
  72. //////////////////////////////
  73. ////// Plugin information ////////
  74. PLUGIN_INFO = {
  75. "Console", // Name
  76. PLUGIN_ALL, // Target servers
  77. "0.1", // Version
  78. "1.03", // Minimum plugin engine version to run
  79. "Console parser" // Short description
  80. };
  81. ////// Plugin event list //////////
  82. // Format: <plugin function>,<event name>
  83. // All registered functions to a event gets executed
  84. // (In descending order) when its called.
  85. // Multiple functions can be called by multiple events too,
  86. // So it's up to your creativity ^^
  87. //
  88. PLUGIN_EVENTS_TABLE = {
  89. { "console_init", EVENT_PLUGIN_INIT },
  90. { "console_final", EVENT_PLUGIN_FINAL },
  91. { "console_autostart", EVENT_ATHENA_INIT },
  92. //{ "console_start", EVENT_CONSOLE_START },//## add these events to the plugins framework
  93. //{ "console_stop", EVENT_CONSOLE_STOP },
  94. { "console_stop", EVENT_ATHENA_FINAL },
  95. { NULL, NULL }
  96. };
  97. ///// Variables /////
  98. // Imported functions
  99. typedef int TimerFunc(int tid, unsigned int tick, int id, int data);
  100. int (*add_timer_func_list)(TimerFunc func, char* name);
  101. int (*add_timer_interval)(unsigned int tick, TimerFunc* func, int id, int data, int interval);
  102. int (*delete_timer)(int tid, TimerFunc* func);
  103. unsigned int (*gettick)(void);
  104. int (*parse_console)(char* buf);
  105. // Locals
  106. int tid; // timer id
  107. BUFFER buf; // input buffer
  108. WORKER_FUNC_DECLARE(getinput); // worker for the input buffer
  109. //////// Asynchronous input functions //////////
  110. //////////////////////////////
  111. #ifdef WIN32
  112. //////////////////////////////
  113. //
  114. // --=== Asynchronous console input ===--
  115. //
  116. // On windows a thread is used (both threads have access to the same data).
  117. // The worker threads starts suspended and is resumed when data is required.
  118. // After getting the data, the worker thread updates the state variable and
  119. // suspends itself.
  120. //
  121. // A mutex is used to synchronize access to the state variable between the
  122. // threads. Access and updates to state are probably already atomic so the
  123. // mutex shouldn't be needed, but using it is more correct so it stays.
  124. //
  125. // Note: The Worker thread only starts to get input data when further data is
  126. // requested. This is a design choise and brings no real advantage or
  127. // disadvantage I can think of.
  128. //
  129. /// Returns the state of the input
  130. char input_getstate()
  131. {
  132. char state;
  133. WaitForSingleObject(buf.state_mux, INFINITE);
  134. state = buf.state;
  135. ReleaseMutex(buf.state_mux);
  136. return state;
  137. }
  138. /// Sets the state of the input
  139. void input_setstate(char state)
  140. {
  141. char oldstate;
  142. // update state
  143. WaitForSingleObject(buf.state_mux, INFINITE);
  144. oldstate = buf.state;
  145. buf.state = state;
  146. ReleaseMutex(buf.state_mux);
  147. if( state == INPUT_READY && oldstate == INPUT_READING )
  148. {// data has become available
  149. SuspendThread(buf.worker);
  150. } else if( state == INPUT_WAITING )
  151. {// input is waiting for data
  152. ResumeThread(buf.worker);
  153. //} else if( state == INPUT_READING )
  154. //{// worker is reading data
  155. } else if( state == INPUT_CLOSED )
  156. {// end the input
  157. CloseHandle(buf.state_mux);
  158. TerminateThread(buf.worker, 0);
  159. }
  160. }
  161. /// Gets the next state of the input
  162. #define input_nextstate() input_getstate()
  163. /// Returns if data is available from asynchronous input.
  164. /// Requests data if none is available.
  165. int input_hasdata(void)
  166. {
  167. if( input_getstate() == INPUT_READY )
  168. {// buffer is ready
  169. if( buf.len > 0 )
  170. return 1; // data found ;)
  171. // request data from the worker
  172. input_setstate(INPUT_WAITING);
  173. }
  174. return 0; // no data
  175. }
  176. /// Initialize asynchronous input
  177. int input_init(void)
  178. {
  179. int err = 0;
  180. memset(&buf, 0, sizeof(buf));
  181. buf.state_mux = CreateMutex(NULL, FALSE, NULL);
  182. if( buf.state_mux == NULL )
  183. {// failed to create state mutex
  184. return 1;
  185. }
  186. buf.len = 0;
  187. input_setstate(INPUT_READY);
  188. WORKER_EXECUTE(getinput, &err);
  189. if( err )
  190. {// failed to start worker
  191. input_setstate(INPUT_CLOSED);
  192. }
  193. return err;
  194. }
  195. /// Finalize asynchronous input
  196. int input_final(void)
  197. {
  198. input_setstate(INPUT_CLOSED);
  199. return 0;
  200. }
  201. //////////////////////////////
  202. #else
  203. //////////////////////////////
  204. //
  205. // --=== Asynchronous console input ===--
  206. //
  207. // On the other systems a process is used and pipes are used to comunicate.
  208. // The worker process receives status updates through one of the pipes either
  209. // requesting data or ending the worker.
  210. // The other pipe is used by the worker to send the input data and is checked
  211. // for data by the main thread in the timer function.
  212. //
  213. // Note: The Worker thread only starts to get input data when further data is
  214. // requested. This is a design choise and brings no real advantage or
  215. // disadvantage I can think of.
  216. //
  217. /// Returns the state of the input
  218. #define input_getstate() buf.state
  219. /// Sets the state of the input
  220. void input_setstate(char state)
  221. {
  222. if( state == INPUT_READY && input_getstate() == INPUT_READING )
  223. {// send data from the worker to the main process
  224. write(buf.data_pipe[PIPE_WRITE], &buf.len, sizeof(buf.len));
  225. write(buf.data_pipe[PIPE_WRITE], &buf.arr, buf.len);
  226. } else if( state == INPUT_WAITING ){
  227. if( buf.close_unused_flag == 0 )
  228. {// close unused pipe sides in the main process
  229. close(buf.data_pipe[PIPE_WRITE]);
  230. close(buf.state_pipe[PIPE_READ]);
  231. buf.close_unused_flag = 1;
  232. }
  233. // send the next state
  234. write(buf.state_pipe[PIPE_WRITE], &state, sizeof(state));
  235. } else if( state == INPUT_READING ){
  236. if( buf.close_unused_flag == 0 )
  237. {// close unused pipe sides in the worker process
  238. close(buf.data_pipe[PIPE_READ]);
  239. close(buf.state_pipe[PIPE_WRITE]);
  240. buf.close_unused_flag = 1;
  241. }
  242. } else if( state == INPUT_CLOSED )
  243. {// send next state to the worker and close the pipes
  244. write(buf.state_pipe[PIPE_WRITE], &state, sizeof(state));
  245. close(buf.data_pipe[PIPE_WRITE]);
  246. close(buf.data_pipe[PIPE_READ]);
  247. close(buf.state_pipe[PIPE_WRITE]);
  248. close(buf.state_pipe[PIPE_READ]);
  249. }
  250. buf.state = state;
  251. }
  252. /// Waits for the next state of the input
  253. char input_nextstate()
  254. {
  255. char state = INPUT_CLOSED;
  256. int bytes = 0;
  257. while( bytes == 0 )
  258. bytes = read(buf.state_pipe[PIPE_READ], &state, sizeof(state));
  259. if( bytes == -1 )
  260. {// error, terminate worker
  261. input_setstate(INPUT_CLOSED);
  262. }
  263. return state;
  264. }
  265. /// Returns if data is available from asynchronous input.
  266. /// If data is available, it's put in the local buffer.
  267. int input_hasdata()
  268. {
  269. struct pollfd fds;
  270. int hasData;
  271. if( input_getstate() == INPUT_READY )
  272. {// start getting data
  273. input_setstate(INPUT_WAITING);
  274. return 0;
  275. }
  276. // check if data is available
  277. fds.fd = buf.data_pipe[PIPE_READ];
  278. fds.events = POLLRDNORM;
  279. hasData = ( poll(&fds,1,0) > 0 );
  280. if( hasData )
  281. {// read the data from the pipe
  282. read(buf.data_pipe[PIPE_READ], &buf.len, sizeof(buf.len));
  283. read(buf.data_pipe[PIPE_READ], buf.arr, buf.len);
  284. input_setstate(INPUT_READY);
  285. }
  286. return hasData;
  287. }
  288. /// Initialize asynchronous input
  289. int input_init(void)
  290. {
  291. int err = 0;
  292. memset(&buf, 0, sizeof(buf));
  293. if( pipe(buf.data_pipe) )
  294. {// error creating data pipe
  295. return 1;
  296. }
  297. if( pipe(buf.state_pipe) )
  298. {// error creating state pipe
  299. close(buf.data_pipe[PIPE_READ]);
  300. close(buf.data_pipe[PIPE_WRITE]);
  301. return 1;
  302. }
  303. buf.len = 0;
  304. input_setstate(INPUT_READY);
  305. WORKER_EXECUTE(getinput, &err);
  306. if( err ){
  307. //printf("input_init failed to start worker (%d)\n", err);
  308. input_setstate(INPUT_CLOSED);
  309. }
  310. return err;
  311. }
  312. /// Finalize asynchronous input
  313. int input_final(void)
  314. {
  315. close(buf.data_pipe[PIPE_READ]);
  316. close(buf.data_pipe[PIPE_WRITE]);
  317. close(buf.state_pipe[PIPE_READ]);
  318. close(buf.state_pipe[PIPE_WRITE]);
  319. return 0;
  320. }
  321. //////////////////////////////
  322. #endif
  323. //////////////////////////////
  324. /// Returns the input data array
  325. #define input_getdata() buf.arr
  326. /// Returns the input data length
  327. #define input_getlen() buf.len
  328. /// Clear the input data
  329. #define input_clear() ( buf.len = 0 )
  330. /// Worker thread/process that gets input
  331. WORKER_FUNC_START(getinput)
  332. while( input_nextstate() != INPUT_CLOSED )
  333. {// get input
  334. input_setstate(INPUT_READING);
  335. buf.arr[0] = '\0';
  336. fgets(buf.arr, INPUT_BUFSIZE-1, stdin);
  337. buf.len = strlen(buf.arr);
  338. input_setstate(INPUT_READY);
  339. }
  340. WORKER_FUNC_END(getinput)
  341. //////// Plugin console functions //////////
  342. /// Timer function that checks if there's assynchronous input data and feeds parse_console()
  343. /// The input reads one line at a time and line terminators are removed.
  344. int console_getinputtimer(int tid, unsigned int tick, int id, int data)
  345. {
  346. char* cmd;
  347. size_t len;
  348. if( input_hasdata() ){
  349. // get data (removes line terminators)
  350. cmd = input_getdata();
  351. len = input_getlen();
  352. while( len > 0 && (cmd[len-1] == '\r' || cmd[len-1] == '\n') )
  353. cmd[--len] = '\0';
  354. // parse data
  355. parse_console(cmd);
  356. input_clear();
  357. }
  358. return 0;
  359. }
  360. /// Start the console
  361. void console_start(void)
  362. {
  363. if( input_init() ){
  364. return;
  365. }
  366. //##TODO add a 'startupcmd' config options
  367. //parse_console("help");
  368. add_timer_func_list(console_getinputtimer,"console_getinputtimer");
  369. tid = add_timer_interval(gettick(),console_getinputtimer,0,0,250);//##TODO add a 'timerperiod' config option
  370. }
  371. void console_autostart(void)
  372. {//##TODO add an 'autostart' config option
  373. console_start();
  374. }
  375. /// Stop the console
  376. void console_stop(void)
  377. {
  378. if( tid != -1 ){
  379. delete_timer(tid, console_getinputtimer);
  380. input_final();
  381. }
  382. return;
  383. }
  384. /// Test the console for compatibility
  385. int console_test(void)
  386. {// always compatible at the moment, maybe test if standard input is available?
  387. return 1;
  388. }
  389. /// Initialize the console
  390. void console_init(void)
  391. {
  392. // import symbols
  393. IMPORT_SYMBOL(add_timer_interval, SYMBOL_ADD_TIMER_INTERVAL);
  394. IMPORT_SYMBOL(add_timer_func_list, SYMBOL_ADD_TIMER_FUNC_LIST);
  395. IMPORT_SYMBOL(delete_timer, SYMBOL_DELETE_TIMER);
  396. IMPORT_SYMBOL(gettick, SYMBOL_GETTICK);
  397. IMPORT_SYMBOL(parse_console, SYMBOL_PARSE_CONSOLE);
  398. //printf("%d -> add_timer_func_list=0x%x\n", SYMBOL_ADD_TIMER_FUNC_LIST, (int)add_timer_func_list);
  399. //printf("%d -> add_timer_interval=0x%x\n", SYMBOL_ADD_TIMER_INTERVAL, (int)add_timer_interval);
  400. //printf("%d -> delete_timer=0x%x\n", SYMBOL_DELETE_TIMER, (int)delete_timer);
  401. //printf("%d -> gettick=0x%x\n", SYMBOL_GETTICK, (int)gettick);
  402. //printf("%d -> parse_console=0x%x\n", SYMBOL_PARSE_CONSOLE, (int)parse_console);
  403. }
  404. /// Finalize the console
  405. void console_final(void)
  406. {
  407. }