32 #include <condition_variable> 40 #include <boost/thread.hpp> 41 #include <boost/algorithm/string/classification.hpp> 42 #include <boost/algorithm/string/split.hpp> 55 , m_has_read_request(
false)
56 , m_read_status(state_init)
59 m_readline_buffer.start();
61 m_reader_thread = boost::thread(std::bind(&async_stdin_reader::reader_thread_func,
this));
73 return m_readline_buffer;
83 if (state_eos == m_read_status)
86 boost::unique_lock<boost::mutex> lock(m_response_mutex);
87 while (state_init == m_read_status)
89 m_response_cv.wait(lock);
93 if (state_success == m_read_status)
100 m_read_status = state_init;
105 bool eos()
const {
return m_read_status == state_eos; }
111 m_run.store(
false, std::memory_order_relaxed);
114 ::CloseHandle(::GetStdHandle(STD_INPUT_HANDLE));
117 m_request_cv.notify_one();
118 m_reader_thread.join();
120 m_readline_buffer.stop();
128 boost::unique_lock<boost::mutex> lock(m_request_mutex);
129 if (!m_run.load(std::memory_order_relaxed) || m_has_read_request)
132 m_has_read_request =
true;
133 m_request_cv.notify_one();
139 boost::unique_lock<boost::mutex> lock(m_request_mutex);
140 while (m_run.load(std::memory_order_relaxed) && !m_has_read_request)
142 m_request_cv.wait(lock);
145 if (m_has_read_request)
147 m_has_read_request =
false;
154 bool wait_stdin_data()
157 #if defined(__OpenBSD__) || defined(__ANDROID__) 158 int stdin_fileno = fileno(stdin);
160 int stdin_fileno = ::fileno(stdin);
163 while (m_run.load(std::memory_order_relaxed))
167 FD_SET(stdin_fileno, &read_set);
171 tv.tv_usec = 100 * 1000;
173 int retval = ::select(stdin_fileno + 1, &read_set, NULL, NULL, &tv);
180 while (m_run.load(std::memory_order_relaxed))
182 DWORD retval = ::WaitForSingleObject(::GetStdHandle(STD_INPUT_HANDLE), 100);
198 void reader_thread_func()
210 if (wait_stdin_data())
212 if (m_run.load(std::memory_order_relaxed))
215 switch (m_readline_buffer.get_line(line))
222 std::getline(std::cin, line);
224 read_ok = !std::cin.eof() && !std::cin.fail();
231 if (std::cin.eof()) {
235 m_read_status = state_eos;
236 m_response_cv.notify_one();
241 boost::unique_lock<boost::mutex> lock(m_response_mutex);
242 if (m_run.load(std::memory_order_relaxed))
245 m_read_status = read_ok ? state_success : state_error;
249 m_read_status = state_cancelled;
251 m_response_cv.notify_one();
266 boost::thread m_reader_thread;
267 std::atomic<bool> m_run;
273 bool m_has_read_request;
274 t_state m_read_status;
276 boost::mutex m_request_mutex;
277 boost::mutex m_response_mutex;
278 boost::condition_variable m_request_cv;
279 boost::condition_variable m_response_cv;
283 template<
class t_server>
297 template<
class t_server,
class chain_handler>
300 return run(prompt, usage, [&](
const std::string& cmd) {
return ch_handler(psrv, cmd); }, [&] { psrv->send_stop_signal(); });
303 template<
class chain_handler>
304 bool run(chain_handler ch_handler, std::function<
std::string(
void)> prompt,
const std::string& usage =
"", std::function<
void(
void)> exit_handler = NULL)
306 return run(prompt, usage, [&](
const std::string& cmd) {
return ch_handler(cmd); }, exit_handler);
312 m_stdin_reader.
stop();
321 std::string color_prompt =
"\001\033[1;33m\002" + prompt;
322 if (
' ' != prompt.back())
324 color_prompt +=
"\001\033[0m\002";
325 m_stdin_reader.get_readline_buffer().set_prompt(color_prompt);
329 if (
' ' != prompt.back())
338 template<
typename t_cmd_handler>
339 bool run(std::function<
std::string(
void)> prompt,
const std::string& usage,
const t_cmd_handler& cmd_handler, std::function<
void(
void)> exit_handler)
341 bool continue_handle =
true;
343 while(continue_handle)
354 bool get_line_ret = m_stdin_reader.
get_line(command);
357 if (m_stdin_reader.
eos())
359 MGINFO(
"EOF on stdin, exiting");
360 std::cout << std::endl;
365 MERROR(
"Failed to read line.");
374 else if(cmd_handler(command))
378 else if(0 == command.compare(
"exit") || 0 == command.compare(
"q"))
380 continue_handle =
false;
387 std::cout <<
"unknown command: " << command << std::endl;
391 catch (
const std::exception &ex)
393 LOG_ERROR(
"Exception at [console_handler], what=" << ex.what());
402 async_stdin_reader m_stdin_reader;
403 std::atomic<bool> m_running = {
true};
404 std::function<std::string(void)> m_prompt;
408 template<
class t_server,
class t_handler>
411 std::shared_ptr<async_console_handler> console_handler = std::make_shared<async_console_handler>();
412 boost::thread([=](){console_handler->run<t_server, t_handler>(ptsrv, handlr, prompt, usage);}).detach();
416 template<
class t_server,
class t_handler>
422 template<
class t_server>
428 template<
class t_server,
class t_handler>
434 template<
class t_server,
class t_handler>
438 return console_handler.
run(ptsrv, std::bind<bool>(no_srv_param_adapter<t_server, t_handler>, std::placeholders::_1, std::placeholders::_2, handlr), prompt, usage);
441 template<
class t_server,
class t_handler>
447 template<
class t_server,
class t_handler>
450 boost::thread( boost::bind(run_default_console_handler_no_srv_param<t_server, t_handler>, ptsrv, handlr, prompt, usage) );
454 template<
class t_server,
class t_handler>
484 typedef boost::function<bool (const std::vector<std::string> &)>
callback;
485 typedef std::map<std::string, std::pair<callback, std::pair<std::string, std::string>>>
lookup;
489 std::stringstream ss;
491 for(
auto& x:m_command_handlers)
493 ss << x.second.second.first <<
ENDL;
501 return std::make_pair(
"",
"");
502 auto it = m_command_handlers.find(cmd.front());
503 if(it == m_command_handlers.end())
504 return std::make_pair(
"",
"");
505 return it->second.second;
510 lookup::mapped_type & vt = m_command_handlers[cmd];
512 vt.second.first = description.empty() ? cmd : usage;
513 vt.second.second = description.empty() ? usage : description;
523 auto it = m_command_handlers.find(cmd.front());
524 if(it == m_command_handlers.end())
526 std::vector<std::string> cmd_local(cmd.begin()+1, cmd.end());
527 return it->second.first(cmd_local);
532 std::vector<std::string> cmd_v;
533 boost::split(cmd_v,cmd,boost::is_any_of(
" "), boost::token_compress_on);
537 lookup m_command_handlers;
547 std::unique_ptr<boost::thread> m_console_thread;
553 m_console_thread->detach();
558 return start_handling([prompt](){
return prompt; }, usage_string, exit_handler);
563 m_console_handler.
stop();
static void add_completion(const std::string &command)
bool run(t_server *psrv, chain_handler ch_handler, std::function< std::string(void)> prompt, const std::string &usage="")
std::map< std::string, std::pair< callback, std::pair< std::string, std::string > > > lookup
void set_handler(const std::string &cmd, const callback &hndlr, const std::string &usage="", const std::string &description="")
bool run(chain_handler ch_handler, std::function< std::string(void)> prompt, const std::string &usage="", std::function< void(void)> exit_handler=NULL)
bool get_line(std::string &line)
void reset_console_color()
bool start_default_console(t_server *ptsrv, t_handler handlr, std::function< std::string(void)> prompt, const std::string &usage="")
bool start_default_console_handler_no_srv_param(t_server *ptsrv, t_handler handlr, std::function< std::string(void)> prompt, const std::string &usage="")
std::pair< std::string, std::string > get_documentation(const std::vector< std::string > &cmd)
bool process_command_str(const std::string &cmd)
boost::function< bool(const std::vector< std::string > &)> callback
void set_console_color(int color, bool bright)
bool start_handling(std::function< std::string(void)> prompt, const std::string &usage_string="", std::function< void(void)> exit_handler=NULL)
bool empty_commands_handler(t_server *psrv, const std::string &command)
const T & move(const T &t)
bool run_default_console_handler_no_srv_param(t_server *ptsrv, t_handler handlr, std::function< std::string(void)> prompt, const std::string &usage="")
bool process_command_vec(const std::vector< std::string > &cmd)
bool no_srv_param_adapter(t_server *ptsrv, const std::string &cmd, t_handler handlr)
bool run_handling(std::function< std::string(void)> prompt, const std::string &usage_string, std::function< void(void)> exit_handler=NULL)
bool start_handling(const std::string &prompt, const std::string &usage_string="", std::function< void(void)> exit_handler=NULL)