Initial commit
[forms.git] / src / F_App.C
blobcb5aa536069833951f51aad500a61796bb6a138d
2 #include <iostream>
3 #include <F_App.H>
5 using namespace F;
6 bool F::sigsegv_ = false;
8 int F_App::argc_;
9 char **F_App::argv_;
11 void (*F_App::idle_)(void);
12 F_App *F_App::this_ = 0;
13 std::string F_App::name_;
15 ost::CommandOption *f_app_cmd_options = 0;
16 ost::CommandOptionNoArg help_option("help", "h", "\t\t\tprints short usage info",
17   false, &f_app_cmd_options);
18 ost::CommandOptionNoArg version_option("version", "v",
19   "\t\tprints program name & version", false, &f_app_cmd_options);
21 #include <cxxabi.h>
23 const char *F::demangled_(const char *mngl)
25  static std::string mangled;
26  char *ptr = NULL;
27  size_t len;
28  int status;
29  mangled = mngl;
30  // do we need to free ptr ?
31  const char *dm = abi::__cxa_demangle(mangled.c_str(), ptr, &len, &status);
32  if (dm)
33    mangled = dm;
34  return mangled.c_str();
37 // for this pleasure you need libiberty from binutils or cxxabi.h
38 #define F_DEMANGLED_BACKTRACE
40 #ifdef  F_DEMANGLED_BACKTRACE
42 static std::string demangle_(const char *mngl)
44  // ÂÙ×ÁÀÔ ×ÁÒÉÁÎÔÙ :
45  //
46  // [X] [0xffffe440]                                              - ÏÔÂÒÁÓÙ×ÁÅÍ
47  // [X] /lib/libpthread.so.0 [0xb7d23167]                         - ÏÔÂÒÁÓÙ×ÁÅÍ
48  // [X] ./test/demo(_ZN1F12F_Console_UID2Ev+0x35) [0x804fd25]     - ÄÅÍÁÎÇÌÉÍ
49  // ×ÙÒÅÚÁÅÍ ÏÔÓÅÄÁ|......................| ÄÏ ÓÅÄÁ ×ËÌÀÞÉÔÅÌØÎÏ
50  std::string mangled = mngl;
51  size_t fname_start = mangled.find('(');
52  if (fname_start == std::string::npos) // no '('
53    return mangled;
54  size_t fname_end = mangled.rfind('+');
55  if (fname_end == std::string::npos) // no '+'
56    return mangled;
57  std::string mfunc(mangled, fname_start + 1, fname_end - fname_start - 1);
58  std::string demangled(mangled, 0, fname_start + 1);
59  const char *dm = demangled_(mfunc.c_str());
60  if (!dm)
61    dm = mfunc.c_str();
62  demangled.append(dm);
63  if (demangled.find("()") == std::string::npos)
64    demangled.append("()");
65  demangled.append(mngl + fname_end);
66  return demangled;
69 #else
70 #define demangle_(x) std::string(x)
71 #endif
73 #include <sys/utsname.h>
74 #include <gnu/libc-version.h>
76 // ËÒÁÔËÉÅ Ó×ÅÄÅÎÉÑ Ï ÓÉÓÔÅÍÅ
77 static inline void print_sysinfo()
79  struct utsname buf;
80  int err = uname(&buf);
81  time_t t = time(0);
83  std::clog << "Occured  : " << ctime(&t);
84  std::clog << "Compiled : " << __DATE__ << " " << __TIME__ << std::endl;
85  std::clog << "Node     : " << (err ? "Unknown" : buf.nodename) << std::endl;
86  std::clog << "Machine  : " << (err ? "Unknown" : buf.machine) << std::endl;
87  std::clog << "OS Name  : " << (err ? "Unknown" : buf.sysname) << std::endl;
88  std::clog << "Release  : " << (err ? "Unknown" : buf.release) << std::endl;
89  std::clog << "Version  : " << (err ? "Unknown" : buf.version) << std::endl;
90  std::clog << "GCC      : " << __VERSION__ << std::endl;
91  std::clog << "GLIBC    : " << gnu_get_libc_version() << " " << gnu_get_libc_release() << std::endl;
92  std::clog << std::endl;
95 #include <execinfo.h>
97 static inline void print_backtrace(siginfo_t* siginfo, signal_regs_t *regs)
99  // may be implement smth. like show_regs ...?
100  void *ba[128];
101  size_t bas = backtrace(ba, 128);
102  char **bts = backtrace_symbols(ba, bas);
103  if (bts) {
104    std::clog << "*** Siginfo: pid "<< getpid() << ", tid " << pthread_self()
105     << ", fault eip 0x" << std::hex << regs->eip << ", fault addr " <<
106     siginfo->si_addr <<  std::endl << std::dec;
107    std::clog << "*** Current thread backtrace (" << (bas - 3) << " funcs) :" <<
108     std::endl << std::endl;
109    for (size_t i = 3; i < bas; i++) // ÐÅÒ×ÙÅ ÔÒÉ ÎÅ ÎÕÖÎÙ
110     std::clog << "[" << (i - 3) << "] " << demangle_(bts[i]) << std::endl;
111    std::clog << std::endl;
112    free(bts);
116 // ÔÁËÖÅ ÄÌÑ ÚÁ×ÉÓÛÉÈ ×ÁÒÉÁÎÔÏ× ÍÏÖÎÏ ÚÁÄÁÔØ ÓÉÇÎÁÌ (F_TRACE_SIGNAL) É ÐÏ ÎÅÍÕ
117 // ÐÅÞÁÔÁÔØ backtrace(), ÔÏËÍÁ ÓÌÅÄÕÅÔØ ÕÞÅÓÔØ ÛÏ ÜÎÔÏÔ ÓÉÇÎÁÌ ÐÏÌÕÞÁÀÔØ ÕÓÅ
118 // ÔÒÅÄÙ, ÔÏ ÂÉÛØ ÐÏÌÕÞÉÍ ËÉÐÕ backtrac'Ï× ÄÌÑ multithread app ...
121 void F_App::signal_handler(int signo, siginfo_t* siginfo, void* ptr)
123   struct sigaction dfl_handler;
124   sigemptyset(&dfl_handler.sa_mask);
125   dfl_handler.sa_flags = SA_SIGINFO | SA_RESTART;
126   dfl_handler.sa_handler = 0;
127   dfl_handler.sa_restorer = NULL;
128   switch (signo) {
129     case SIGCONT:
130     case SIGCHLD:
131       dfl_handler.sa_sigaction = F_App::signal_handler;
132       sigaction(SIGTSTP, &dfl_handler, NULL);
133       sigaction(SIGSTOP, &dfl_handler, NULL);
134       this_->sigcont();
135       if (this_->ui_)
136         this_->ui_->sigcont();
137       return;
138     case SIGINT:
139     case SIGQUIT:
140       // temporary disable signals
141       dfl_handler.sa_handler = SIG_IGN;
142       sigaction(SIGINT, &dfl_handler, NULL);
143       sigaction(SIGQUIT, &dfl_handler, NULL);
144       // ask user if he/she want to leave app
145       if (this_->exit_confirm(1))
146         break;
147       else { // reenable sigs
148         dfl_handler.sa_sigaction = F_App::signal_handler;
149         sigaction(SIGINT, &dfl_handler, NULL);
150         sigaction(SIGQUIT, &dfl_handler, NULL);
151       }             
152       return;
153     case SIGTSTP:
154     case SIGSTOP:
155       if (!this_->sigstop()) // app doesn't accept sigstops
156         return;
157       if (this_->ui_)
158         this_->ui_->sigstop();
159       dfl_handler.sa_handler = SIG_DFL;
160       sigaction(SIGTSTP, &dfl_handler, NULL);
161       raise(SIGTSTP);
162       return;
163     case SIGWINCH:
164       if (this_->ui_ && (getpid() == F_UI::pid)) // deliver only to ui thread
165         this_->ui_->sigwinch();
166       return;
167 #ifdef DEBUG_VERSION
168     case F_TRACE_SIGNAL:
169       std::clog << "\n*** Got backtrace signal \'" << strsignal(signo) <<
170         "\'.\n";
171       print_backtrace(siginfo, (signal_regs_t *)ptr);
172       return;
173     case SIGSEGV:
174     case SIGILL:
175     case SIGFPE:
176     case SIGBUS:
177      if (this_->ui_)
178        this_->ui_->sigsegv();
179      std::clog << "\n*** Got " << strsignal(signo) << " signal, exiting ...\n";
180      print_backtrace(siginfo, (signal_regs_t *)ptr);
181      print_sysinfo();
182      std::clog << "*** You may send this backtrace to \'" << this_->author_ <<
183       "\'\n\n";
184      break;      
185 #endif
186     default:
187       break;
188    }
189  if (this_->ui_)
190    this_->ui_->shutdown();
191  log("signal_handler", ALERT_LEVEL, "ðÒÅËÒÁÝÅÎÉÅ ÒÁÂÏÔÙ (%s signal)",
192    strsignal(signo));
193  ::exit(0);
196 static void out_of_memory()
198  std::cerr << "Out of memory, bye ..." << std::endl;
199  // if we can free some memory for backtrace it will be wonderfull ...
200  // kill(getpid(), F_TRACE_SIGNAL);
201  ::exit(-1);
204 void F_App::signals_setup(void)
206  struct sigaction new_action;
207  sigemptyset(&new_action.sa_mask);
208  new_action.sa_handler = 0;
209  new_action.sa_restorer = NULL;
210  new_action.sa_sigaction = F_App::signal_handler;
211  new_action.sa_flags = SA_SIGINFO | SA_ONESHOT;
212  // system signals - default action is to terminate app
213  sigaction (SIGHUP, &new_action, NULL);
214  sigaction (SIGSEGV, &new_action, NULL);
215  sigaction (SIGILL, &new_action, NULL);
216  sigaction (SIGFPE, &new_action, NULL);
217  sigaction (SIGBUS, &new_action, NULL);
218  // user trapped sigs
219  new_action.sa_flags = SA_SIGINFO | SA_RESTART;
220  sigaction (SIGCHLD, &new_action, NULL);
221  sigaction (SIGSTOP, &new_action, NULL); // sigstop is no reacheable for app
222  sigaction (SIGCONT, &new_action, NULL);
223  sigaction (SIGTSTP, &new_action, NULL);
224  sigaction (SIGQUIT, &new_action, NULL);
225  sigaction (SIGINT, &new_action, NULL);
226  sigaction (SIGWINCH, &new_action, NULL);
227  sigaction (F_TRACE_SIGNAL, &new_action, NULL);
230 void F_App::default_idle(void)
232  ost::Thread::sleep(10);
235 #include <exception>
237 F_App::F_App(int argc, char **argv, const char *name, const char *version,
238   const char *author, const char *license, ost::CommandOption *user_cmd_opts,
239   const char *comment, const char *l_prefix)
241  if (this_) {
242    std::cerr << "Only one F_App instance is allowed, bye ..." << std::endl;
243    ::abort();
245  this_ = this;
246  ui_ = 0;
247  old_new_handler = std::set_new_handler(out_of_memory);
248  // GNU extension
249  std::set_terminate (__gnu_cxx::__verbose_terminate_handler);
250  signals_setup();
251  idle(default_idle);
252  name_ = name ? name : "Unknown App,";
253  version_ = version ? version : "version unknown,";
254  author_ = author ? author : "Mr. Unknown <unknown@nowhere.world>";
255  license_ = license ? license : "unknown";
256  // parse internal command options
257  if (user_cmd_opts) {
258   ost::CommandOption *tt = user_cmd_opts;
259    while (tt->next)
260     tt = tt->next;
261    // this is strange - options is parsed from end ?!
262    tt->next = f_app_cmd_options;
264  if (!comment)
265    comment = "program usage :";
266  this->argc_ = argc;
267  this->argv_ = argv; 
268  cmd_opts_ = ost::makeCommandOptionParse(argc, argv, (char *)comment,
269    user_cmd_opts ? user_cmd_opts : f_app_cmd_options);
270  if (help_option.numSet) {
271    std::clog << cmd_opts_->printUsage();
272    ::exit(0);
274  if (version_option.numSet) {
275    std::clog << name_ << " " << version_ << " written by " << author_ <<
276      " under " << license_ << " license" << std::endl;
277    ::exit(0);
279  if (cmd_opts_->argsHaveError()) {
280    std::cerr << cmd_opts_->printErrors();
281    std::cerr << "Did you try " << argv[0] << " --help ?\n";
282    ::exit(-1);
284  // create main log channel
285  log_init();
286  if (l_prefix)
287    log_prefix(l_prefix);
288  log("F_App", DEBUG_LEVEL, "F_App() v%0d.%0d.%0d created.", F_MAJOR_VERSION,
289    F_MINOR_VERSION, F_PATCH_VERSION);
292 void F::shutdown(int retval)
294  F_App::shutdown();
295  ::exit(retval);
298 // do this as atexit handler ?
299 F_App::~F_App()
301  shutdown();
302  if (this_)
303    delete this_;
304  this_ = 0;
307 void F_App::init(void)
309  // ÅÓÌÉ ÐÒÏÇÒÁÍÍÁ ×ÙÈÏÄÉÔ ÔÕÔÁ ÐÏ Ctrl-C, ÔÏ ÔÁË ËÁË ui ËÌÁÓÓ ÍÏÖÅÔ ÂÙÔØ
310  // ÅÝÅ ÎÅ ÓÏÚÄÁΠ- ÓÌÅÄÕÅÔ ÜÔÏ ÕÞÅÓÔØ
311  parse_command_options();
312  // create ui
313  if (!F_UI::ui_count()) {
314   log("ui_count", FATAL_LEVEL, "Oops, your app doesn't define any UI !");
315   ::exit(-1);
317  ui_ = F_UI::make_ui();