Electroneum
Loading...
Searching...
No Matches
console_handler.h
Go to the documentation of this file.
1// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above copyright
9// notice, this list of conditions and the following disclaimer in the
10// documentation and/or other materials provided with the distribution.
11// * Neither the name of the Andrey N. Sabelnikov nor the
12// names of its contributors may be used to endorse or promote products
13// derived from this software without specific prior written permission.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
19// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25//
26
27#pragma once
28
29#include "misc_log_ex.h"
30#include "string_tools.h"
31#include <atomic>
32#include <condition_variable>
33#include <functional>
34#include <mutex>
35#include <thread>
36#include <iostream>
37#ifdef __OpenBSD__
38#include <stdio.h>
39#endif
40#include <boost/thread.hpp>
41#include <boost/algorithm/string/classification.hpp>
42#include <boost/algorithm/string/split.hpp>
43
44#ifdef HAVE_READLINE
45 #include "readline_buffer.h"
46#endif
47
48namespace epee
49{
51 {
52 public:
54 : m_run(true)
55 , m_has_read_request(false)
56 , m_read_status(state_init)
57 {
58#ifdef HAVE_READLINE
59 m_readline_buffer.start();
60#endif
61 m_reader_thread = boost::thread(std::bind(&async_stdin_reader::reader_thread_func, this));
62 }
63
65 {
66 try { stop(); }
67 catch (...) { /* ignore */ }
68 }
69
70#ifdef HAVE_READLINE
71 rdln::readline_buffer& get_readline_buffer()
72 {
73 return m_readline_buffer;
74 }
75#endif
76
77 // Not thread safe. Only one thread can call this method at once.
78 bool get_line(std::string& line)
79 {
80 if (!start_read())
81 return false;
82
83 if (state_eos == m_read_status)
84 return false;
85
86 boost::unique_lock<boost::mutex> lock(m_response_mutex);
87 while (state_init == m_read_status)
88 {
89 m_response_cv.wait(lock);
90 }
91
92 bool res = false;
93 if (state_success == m_read_status)
94 {
95 line = m_line;
96 res = true;
97 }
98
99 if (!eos())
100 m_read_status = state_init;
101
102 return res;
103 }
104
105 bool eos() const { return m_read_status == state_eos; }
106
107 void stop()
108 {
109 if (m_run)
110 {
111 m_run.store(false, std::memory_order_relaxed);
112
113#if defined(WIN32)
114 ::CloseHandle(::GetStdHandle(STD_INPUT_HANDLE));
115#endif
116
117 m_request_cv.notify_one();
118 m_reader_thread.join();
119#ifdef HAVE_READLINE
120 m_readline_buffer.stop();
121#endif
122 }
123 }
124
125 private:
126 bool start_read()
127 {
128 boost::unique_lock<boost::mutex> lock(m_request_mutex);
129 if (!m_run.load(std::memory_order_relaxed) || m_has_read_request)
130 return false;
131
132 m_has_read_request = true;
133 m_request_cv.notify_one();
134 return true;
135 }
136
137 bool wait_read()
138 {
139 boost::unique_lock<boost::mutex> lock(m_request_mutex);
140 while (m_run.load(std::memory_order_relaxed) && !m_has_read_request)
141 {
142 m_request_cv.wait(lock);
143 }
144
145 if (m_has_read_request)
146 {
147 m_has_read_request = false;
148 return true;
149 }
150
151 return false;
152 }
153
154 bool wait_stdin_data()
155 {
156#if !defined(WIN32)
157 #if defined(__OpenBSD__) || defined(__ANDROID__)
158 int stdin_fileno = fileno(stdin);
159 #else
160 int stdin_fileno = ::fileno(stdin);
161 #endif
162
163 while (m_run.load(std::memory_order_relaxed))
164 {
165 fd_set read_set;
166 FD_ZERO(&read_set);
167 FD_SET(stdin_fileno, &read_set);
168
169 struct timeval tv;
170 tv.tv_sec = 0;
171 tv.tv_usec = 100 * 1000;
172
173 int retval = ::select(stdin_fileno + 1, &read_set, NULL, NULL, &tv);
174 if (retval < 0)
175 return false;
176 else if (0 < retval)
177 return true;
178 }
179#else
180 while (m_run.load(std::memory_order_relaxed))
181 {
182 DWORD retval = ::WaitForSingleObject(::GetStdHandle(STD_INPUT_HANDLE), 100);
183 switch (retval)
184 {
185 case WAIT_FAILED:
186 return false;
187 case WAIT_OBJECT_0:
188 return true;
189 default:
190 break;
191 }
192 }
193#endif
194
195 return true;
196 }
197
198 void reader_thread_func()
199 {
200 while (true)
201 {
202 if (!wait_read())
203 break;
204
205 std::string line;
206 bool read_ok = true;
207#ifdef HAVE_READLINE
208reread:
209#endif
210 if (wait_stdin_data())
211 {
212 if (m_run.load(std::memory_order_relaxed))
213 {
214#ifdef HAVE_READLINE
215 switch (m_readline_buffer.get_line(line))
216 {
217 case rdln::empty: goto eof;
218 case rdln::partial: goto reread;
219 case rdln::full: break;
220 }
221#else
222 std::getline(std::cin, line);
223#endif
224 read_ok = !std::cin.eof() && !std::cin.fail();
225 }
226 }
227 else
228 {
229 read_ok = false;
230 }
231 if (std::cin.eof()) {
232#ifdef HAVE_READLINE
233eof:
234#endif
235 m_read_status = state_eos;
236 m_response_cv.notify_one();
237 break;
238 }
239 else
240 {
241 boost::unique_lock<boost::mutex> lock(m_response_mutex);
242 if (m_run.load(std::memory_order_relaxed))
243 {
244 m_line = std::move(line);
245 m_read_status = read_ok ? state_success : state_error;
246 }
247 else
248 {
249 m_read_status = state_cancelled;
250 }
251 m_response_cv.notify_one();
252 }
253 }
254 }
255
256 enum t_state
257 {
258 state_init,
259 state_success,
260 state_error,
261 state_cancelled,
262 state_eos
263 };
264
265 private:
266 boost::thread m_reader_thread;
267 std::atomic<bool> m_run;
268#ifdef HAVE_READLINE
269 rdln::readline_buffer m_readline_buffer;
270#endif
271
272 std::string m_line;
273 bool m_has_read_request;
274 t_state m_read_status;
275
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;
280 };
281
282
283 template<class t_server>
284 bool empty_commands_handler(t_server* psrv, const std::string& command)
285 {
286 return true;
287 }
288
289
291 {
292 public:
296
297 template<class t_server, class chain_handler>
298 bool run(t_server* psrv, chain_handler ch_handler, std::function<std::string(void)> prompt, const std::string& usage = "")
299 {
300 return run(prompt, usage, [&](const std::string& cmd) { return ch_handler(psrv, cmd); }, [&] { psrv->send_stop_signal(); });
301 }
302
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)
305 {
306 return run(prompt, usage, [&](const std::string& cmd) { return ch_handler(cmd); }, exit_handler);
307 }
308
309 void stop()
310 {
311 m_running = false;
312 m_stdin_reader.stop();
313 }
314
316 {
317 std::string prompt = m_prompt();
318 if (!prompt.empty())
319 {
320#ifdef HAVE_READLINE
321 std::string color_prompt = "\001\033[1;33m\002" + prompt;
322 if (' ' != prompt.back())
323 color_prompt += " ";
324 color_prompt += "\001\033[0m\002";
325 m_stdin_reader.get_readline_buffer().set_prompt(color_prompt);
326#else
328 std::cout << prompt;
329 if (' ' != prompt.back())
330 std::cout << ' ';
332 std::cout.flush();
333#endif
334 }
335 }
336
337 private:
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)
340 {
341 bool continue_handle = true;
342 m_prompt = prompt;
343 while(continue_handle)
344 {
345 try
346 {
347 if (!m_running)
348 {
349 break;
350 }
351 print_prompt();
352
353 std::string command;
354 bool get_line_ret = m_stdin_reader.get_line(command);
355 if (!m_running)
356 break;
357 if (m_stdin_reader.eos())
358 {
359 MGINFO("EOF on stdin, exiting");
360 std::cout << std::endl;
361 break;
362 }
363 if (!get_line_ret)
364 {
365 MERROR("Failed to read line.");
366 }
367 string_tools::trim(command);
368
369 LOG_PRINT_L2("Read command: " << command);
370 if (command.empty())
371 {
372 continue;
373 }
374 else if(cmd_handler(command))
375 {
376 continue;
377 }
378 else if(0 == command.compare("exit") || 0 == command.compare("q"))
379 {
380 continue_handle = false;
381 }
382 else
383 {
384#ifdef HAVE_READLINE
385 rdln::suspend_readline pause_readline;
386#endif
387 std::cout << "unknown command: " << command << std::endl;
388 std::cout << usage;
389 }
390 }
391 catch (const std::exception &ex)
392 {
393 LOG_ERROR("Exception at [console_handler], what=" << ex.what());
394 }
395 }
396 if (exit_handler)
397 exit_handler();
398 return true;
399 }
400
401 private:
402 async_stdin_reader m_stdin_reader;
403 std::atomic<bool> m_running = {true};
404 std::function<std::string(void)> m_prompt;
405 };
406
407
408 template<class t_server, class t_handler>
409 bool start_default_console(t_server* ptsrv, t_handler handlr, std::function<std::string(void)> prompt, const std::string& usage = "")
410 {
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();
413 return true;
414 }
415
416 template<class t_server, class t_handler>
417 bool start_default_console(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "")
418 {
419 return start_default_console(ptsrv, handlr, [prompt](){ return prompt; }, usage);
420 }
421
422 template<class t_server>
423 bool start_default_console(t_server* ptsrv, const std::string& prompt, const std::string& usage = "")
424 {
425 return start_default_console(ptsrv, empty_commands_handler<t_server>, prompt, usage);
426 }
427
428 template<class t_server, class t_handler>
429 bool no_srv_param_adapter(t_server* ptsrv, const std::string& cmd, t_handler handlr)
430 {
431 return handlr(cmd);
432 }
433
434 template<class t_server, class t_handler>
435 bool run_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, std::function<std::string(void)> prompt, const std::string& usage = "")
436 {
437 async_console_handler console_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);
439 }
440
441 template<class t_server, class t_handler>
442 bool run_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "")
443 {
444 return run_default_console_handler_no_srv_param(ptsrv, handlr, [prompt](){return prompt;},usage);
445 }
446
447 template<class t_server, class t_handler>
448 bool start_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, std::function<std::string(void)> prompt, const std::string& usage = "")
449 {
450 boost::thread( boost::bind(run_default_console_handler_no_srv_param<t_server, t_handler>, ptsrv, handlr, prompt, usage) );
451 return true;
452 }
453
454 template<class t_server, class t_handler>
455 bool start_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "")
456 {
457 return start_default_console_handler_no_srv_param(ptsrv, handlr, [prompt](){return prompt;}, usage);
458 }
459
460 /*template<class a>
461 bool f(int i, a l)
462 {
463 return true;
464 }*/
465 /*
466 template<class chain_handler>
467 bool default_console_handler2(chain_handler ch_handler, const std::string usage)
468 */
469
470
471 /*template<class t_handler>
472 bool start_default_console2(t_handler handlr, const std::string& usage = "")
473 {
474 //std::string usage_local = usage;
475 boost::thread( boost::bind(default_console_handler2<t_handler>, handlr, usage) );
476 //boost::function<bool ()> p__ = boost::bind(f<t_handler>, 1, handlr);
477 //boost::function<bool ()> p__ = boost::bind(default_console_handler2<t_handler>, handlr, usage);
478 //boost::thread tr(p__);
479 return true;
480 }*/
481
483 public:
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;
486
487 std::string get_usage()
488 {
489 std::stringstream ss;
490
491 for(auto& x:m_command_handlers)
492 {
493 ss << x.second.second.first << ENDL;
494 }
495 return ss.str();
496 }
497
498 std::pair<std::string, std::string> get_documentation(const std::vector<std::string>& cmd)
499 {
500 if(cmd.empty())
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;
506 }
507
508 void set_handler(const std::string& cmd, const callback& hndlr, const std::string& usage = "", const std::string& description = "")
509 {
510 lookup::mapped_type & vt = m_command_handlers[cmd];
511 vt.first = hndlr;
512 vt.second.first = description.empty() ? cmd : usage;
513 vt.second.second = description.empty() ? usage : description;
514#ifdef HAVE_READLINE
516#endif
517 }
518
519 bool process_command_vec(const std::vector<std::string>& cmd)
520 {
521 if(!cmd.size())
522 return false;
523 auto it = m_command_handlers.find(cmd.front());
524 if(it == m_command_handlers.end())
525 return false;
526 std::vector<std::string> cmd_local(cmd.begin()+1, cmd.end());
527 return it->second.first(cmd_local);
528 }
529
530 bool process_command_str(const std::string& cmd)
531 {
532 std::vector<std::string> cmd_v;
533 boost::split(cmd_v,cmd,boost::is_any_of(" "), boost::token_compress_on);
534 return process_command_vec(cmd_v);
535 }
536 private:
537 lookup m_command_handlers;
538 };
539
540 /************************************************************************/
541 /* */
542 /************************************************************************/
544 {
545 typedef command_handler::callback console_command_handler;
546 typedef command_handler::lookup command_handlers_map;
547 std::unique_ptr<boost::thread> m_console_thread;
548 async_console_handler m_console_handler;
549 public:
550 bool start_handling(std::function<std::string(void)> prompt, const std::string& usage_string = "", std::function<void(void)> exit_handler = NULL)
551 {
552 m_console_thread.reset(new boost::thread(boost::bind(&console_handlers_binder::run_handling, this, prompt, usage_string, exit_handler)));
553 m_console_thread->detach();
554 return true;
555 }
556 bool start_handling(const std::string &prompt, const std::string& usage_string = "", std::function<void(void)> exit_handler = NULL)
557 {
558 return start_handling([prompt](){ return prompt; }, usage_string, exit_handler);
559 }
560
562 {
563 m_console_handler.stop();
564 }
565
566 bool run_handling(std::function<std::string(void)> prompt, const std::string& usage_string, std::function<void(void)> exit_handler = NULL)
567 {
568 return m_console_handler.run(std::bind(&console_handlers_binder::process_command_str, this, std::placeholders::_1), prompt, usage_string, exit_handler);
569 }
570
572 {
573 m_console_handler.print_prompt();
574 }
575 };
576
578 //template<class t_server>
579 //class srv_console_handlers_binder: public command_handler
580 //{
581 // async_console_handler m_console_handler;
582 //public:
583 // bool start_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string = "")
584 // {
585 // boost::thread(boost::bind(&srv_console_handlers_binder<t_server>::run_handling, this, psrv, prompt, usage_string)).detach();
586 // return true;
587 // }
588
589 // bool run_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string)
590 // {
591 // return m_console_handler.run(psrv, boost::bind(&srv_console_handlers_binder<t_server>::process_command_str, this, _1, _2), prompt, usage_string);
592 // }
593
594 // void stop_handling()
595 // {
596 // m_console_handler.stop();
597 // }
598 //private:
599 // bool process_command_str(t_server* /*psrv*/, const std::string& cmd)
600 // {
601 // return console_handlers_binder::process_command_str(cmd);
602 // }
603 //};
604}
bool run(chain_handler ch_handler, std::function< std::string(void)> prompt, const std::string &usage="", std::function< void(void)> exit_handler=NULL)
bool run(t_server *psrv, chain_handler ch_handler, std::function< std::string(void)> prompt, const std::string &usage="")
bool get_line(std::string &line)
std::map< std::string, std::pair< callback, std::pair< std::string, std::string > > > lookup
std::pair< std::string, std::string > get_documentation(const std::vector< std::string > &cmd)
boost::function< bool(const std::vector< std::string > &)> callback
bool process_command_str(const std::string &cmd)
bool process_command_vec(const std::vector< std::string > &cmd)
void set_handler(const std::string &cmd, const callback &hndlr, const std::string &usage="", const std::string &description="")
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)
bool start_handling(std::function< std::string(void)> prompt, const std::string &usage_string="", std::function< void(void)> exit_handler=NULL)
static void add_completion(const std::string &command)
const char * res
#define MERROR(x)
Definition misc_log_ex.h:73
#define ENDL
#define LOG_ERROR(x)
Definition misc_log_ex.h:98
#define MGINFO(x)
Definition misc_log_ex.h:80
#define LOG_PRINT_L2(x)
std::string & trim(std::string &str)
void set_console_color(int color, bool bright)
Definition mlog.cpp:341
bool empty_commands_handler(t_server *psrv, const std::string &command)
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 no_srv_param_adapter(t_server *ptsrv, const std::string &cmd, t_handler handlr)
bool start_default_console(t_server *ptsrv, t_handler handlr, std::function< std::string(void)> prompt, const std::string &usage="")
void reset_console_color()
Definition mlog.cpp:460
@ console_color_yellow
bool start_default_console_handler_no_srv_param(t_server *ptsrv, t_handler handlr, std::function< std::string(void)> prompt, const std::string &usage="")
#define true
#define false