Electroneum
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 
48 namespace 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
208 reread:
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
233 eof:
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:
294  {
295  }
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 
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 
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 }
const char * res
Definition: hmac_keccak.cpp:41
#define MERROR(x)
Definition: misc_log_ex.h:73
#define LOG_PRINT_L2(x)
Definition: misc_log_ex.h:101
static void add_completion(const std::string &command)
::std::string string
Definition: gtest-port.h:1097
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)
std::string & trim(std::string &str)
Definition: string_tools.h:288
#define MGINFO(x)
Definition: misc_log_ex.h:80
void reset_console_color()
Definition: mlog.cpp:460
return true
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)
#define false
Definition: stdbool.h:38
boost::function< bool(const std::vector< std::string > &)> callback
void set_console_color(int color, bool bright)
Definition: mlog.cpp:341
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)
Definition: gtest-port.h:1317
#define LOG_ERROR(x)
Definition: misc_log_ex.h:98
#define ENDL
Definition: misc_log_ex.h:149
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)