main.cxx
Go to the documentation of this file.
1 // =============================================================================
2 //! \file
3 //! \brief Global functions and startup entry point
4 //!
5 //! Copyright (c) 2012-2022 by the developers. See the LICENSE file for details.
6 
7 
8 // =============================================================================
9 // Include headers
10 
11 // Include this first for conditional compilation
12 extern "C"
13 {
14 #include "config.h"
15 }
16 
17 // C++98
18 #include <cstdlib>
19 #include <cstring>
20 #include <iostream>
21 #include <sstream>
22 
23 // Local
24 extern "C"
25 {
26 #include "bdate.h"
27 #include "conf.h"
28 #include "core.h"
29 #include "digest.h"
30 #include "database.h"
31 #include "filter.h"
32 #include "group.h"
33 #include "hmac.h"
34 #include "inet.h"
35 #include "log.h"
36 #include "nls.h"
37 #include "sighandler.h"
38 #include "unicode.h"
39 }
40 #include "compression.hxx"
41 #include "main.hxx"
42 #include "tls.hxx"
43 #include "ui.hxx"
44 
45 
46 // =============================================================================
47 //! \defgroup MAIN MAIN: Command line option parser
48 //! @{
49 
50 
51 // =============================================================================
52 // Constants
53 
54 //! \brief Message prefix for MAIN module
55 #define MAIN_ERR_PREFIX "MAIN: "
56 
57 
58 // =============================================================================
59 // Variables
60 
61 extern "C"
62 {
63 //! Enable additional debug output if nonzero
64 int main_debug = 0;
65 
66 //! Configuration directory path from command line option or \c NULL otherwise
67 const char* main_confprefix = NULL;
68 }
69 
70 //! Flag indicating that configuration was loaded
71 static int main_config_loaded = 0;
72 
73 
74 // =============================================================================
75 //! Print program version information
76 
77 static void print_version(void)
78 {
79  std::cout << CFG_NAME << " " << CFG_VERSION << " for " << CFG_OS << "\n"
80 #if defined(CFG_MODIFIED) && CFG_MODIFIED != 0
81  << "(This is a modified version!)" << "\n"
82 #endif // CFG_MODIFIED
83  << "Unicode version: " << UC_VERSION << "\n"
84  << "Compiled with options:"
85 #if CFG_USE_CLB
86  << " CLB"
87 #endif // CFG_USE_CLB
88 #if CFG_USE_FSC
89  << " FSC"
90 #endif // CFG_USE_FSC
91 #if CFG_USE_IP6
92  << " IP6"
93 #endif // CFG_USE_IP6
94 #if CFG_USE_XSI && !CFG_NLS_DISABLE
95  << " NLS"
96 #endif // CFG_USE_XSI && !CFG_NLS_DISABLE
97 #if CFG_USE_POSIX_API >= 200112
98  << " P2K"
99 #endif // CFG_USE_POSIX_API >= 200112
100 #if CFG_USE_TLS
101  << " TLS"
102 #endif // CFG_USE_TLS
103 #if !CFG_DB_DISABLE
104  << " XDBE"
105 #endif // CFG_DB_DISABLE
106 #if CFG_USE_XSI
107  << " XSI"
108 #endif // CFG_USE_XSI
109 #if CFG_USE_ZLIB
110  << " ZLIB"
111 #endif // CFG_USE_ZLIB
112  << "\n"
113  << "Build: " << BDATE << std::endl;
114 
115 }
116 
117 
118 // =============================================================================
119 // Print help
120 
121 static void print_help(void)
122 {
123  std::cout << "Usage: " << CFG_NAME << " [options]\n"
124  << "\n"
125  << "Options:\n\n"
126  << " -4 Force usage of IPv4 network "
127  << "protocol\n"
128  << " -confprefix path Specify configuration directory "
129  << "path\n"
130  << " -debug Enable debug mode\n"
131  << " -display [host]:display X window system display\n"
132  << " -geometry WxH[+X+Y] Window size and position\n"
133  << " -h Print help message "
134  << "to standard output and exit\n"
135  << " -iconic Starts with minimized window\n"
136  << " -notooltips Disable tooltip messages\n"
137  << " -v Print version information "
138  << "to standard output and exit\n"
139  << "\n"
140  << "The GUI style can be customized with the environment "
141  << "variable FLTK_SCHEME.\n"
142  << "(See manual page for details)"
143  << std::endl;
144 }
145 
146 
147 // =============================================================================
148 // Save configuration and exit
149 
150 static void save_exit(int ret)
151 {
152  if(main_config_loaded)
153  {
154  if(EXIT_SUCCESS == ret)
155  {
156  // Save configuration
158  }
159  // Release memory allocated for configuration data
161  }
162 
163  // Release memory allocated for configuration path
164  if(NULL != main_confprefix)
165  {
166  delete[] main_confprefix;
167  }
168 
169  // Regular exit
170  ts_environ_exit();
171  std::exit(ret);
172 }
173 
174 
175 // =============================================================================
176 // Loop for normal execution
177 
178 static void loop(void)
179 {
180  static const char sbuf[] = "Terminate due to signal";
181 
182  while(1)
183  {
184  // Drive UI
185  if(ui_exec()) { break; }
186 
187  // Check whether signal to exit is pending
189  {
190  // Yes
191  std::clog << std::endl;
192  std::cout << CFG_NAME << ": " << MAIN_ERR_PREFIX
193  << sbuf << std::endl << std::flush;
194  break;
195  }
196  }
197 }
198 
199 
200 // =============================================================================
201 //! \brief Lock UI thread
202 //!
203 //! Exported as C style function
204 //!
205 //! \note
206 //! It is save to call this function for all threads, including the UI thread
207 //! itself.
208 //!
209 //! \attention
210 //! It is not allowed for a thread to call this function multiple times without
211 //! unlock between them!
212 //!
213 //! \return
214 //! - 0 on success
215 //! - Negative value on error
216 
217 int ts_lock_ui(void)
218 {
219  int res = 0;
220 
221  // Execute as NOP for UI thread itself
222  if(!core_check_thread_ui())
223  {
224  res = ui_lock();
225  }
226 
227  return(res);
228 }
229 
230 
231 // =============================================================================
232 //! \brief Unlock UI thread
233 //!
234 //! Exported as C style function
235 //!
236 //! \note
237 //! It is save to call this function for all threads, including the UI thread
238 //! itself.
239 //!
240 //! \return
241 //! - 0 on success
242 //! - Negative value on error
243 
244 int ts_unlock_ui(void)
245 {
246  int res = 0;
247 
248  // Execute as NOP for UI thread itself
249  if(!core_check_thread_ui())
250  {
251  res = ui_unlock();
252  }
253 
254  return(res);
255 }
256 
257 
258 // =============================================================================
259 //! \brief Print error message
260 //!
261 //! Exported as C style function
262 //!
263 //! \param[in] msg String (Unicode error message)
264 //!
265 //! This function executes as NOP if \e msg is \e NULL .
266 //! Otherwise \e msg must be a NUL-terminated string.
267 //! <br>
268 //! The Unicode encoding of \e msg is checked and is allowed to be invalid.
269 //!
270 //! \attention
271 //! Currently only the US-ASCII subset of Unicode will be readable in output.
272 //!
273 //! \note
274 //! This function is guaranteed to work before other modules are initialized.
275 
276 void print_error(const char* msg)
277 {
278  char* s;
279  std::size_t len;
280  std::size_t i;
281  int c;
282 
283  if(NULL != msg)
284  {
285  len = std::strlen(msg);
286  s = new char[len + (std::size_t) 1];
287 
288  for(i = 0; i < len; ++i)
289  {
290  c = (int) msg[i];
291  // Accept only printable, HT (9), LF (10, 0x0A) and SPACE (32, 0x20)
292  if(!(9 == c || 10 == c || (32 <= c && 126 >= c))) { s[i] = '?'; }
293  else { s[i] = msg[i]; }
294  }
295  s[len] = 0;
296  std::clog << CFG_NAME << ": " << s << std::endl << std::flush;
297 
298  delete[] s;
299  }
300 }
301 
302 
303 // =============================================================================
304 //! \brief Program entry point
305 //!
306 //! \param[in] argc Number of command line arguments
307 //! \param[in] argv Array containing command line argument strings
308 //!
309 //! Parse command line and control startup and shutdown
310 //!
311 //! \return
312 //! - \c EXIT_SUCCESS on success
313 //! - \c EXIT_FAILURE on error
314 
315 int main(int argc, char** argv)
316 {
317  // List of options that must not be passed to FLTK
318  static const char* options_to_remove[] =
319  {
320  "-debug", "-confprefix", "-4", NULL
321  };
322  int rv;
323  enum { OK, ERROR, ABORT } rv2 = OK;
324  char option = 0; // Flag indicating option value will follow
325  unsigned int i, j, k;
326  int removed;
327  char* tmp;
328 
329  ts_environ_init();
330 
331  // Install signal handlers
332  // Note:
333  // This is done in a separate C module because POSIX requires a C compiler
334  rv = sighandler_install();
335  if(0 > rv) rv2 = ERROR;
336 
337  // Delete old logfile
338  if(OK == rv2) { log_delete_logfile(); }
339 
340  // Process parameters
341  if (OK == rv2)
342  {
343  for(i = 1; i < (unsigned int) argc; i++)
344  {
345  if(!std::strcmp(argv[i], "-v") || !std::strcmp(argv[i], "--version"))
346  {
347  // Print version information and report success
348  print_version();
349  rv2 = ABORT;
350  break;
351  }
352  else if(!std::strcmp(argv[i], "-h") || !std::strcmp(argv[i], "--help"))
353  {
354  // Print help and report success
355  print_help();
356  rv2 = ABORT;
357  break;
358  }
359  else if(!std::strcmp(argv[i], "-debug")) // Not for FLTK
360  {
361  main_debug = 1;
362  }
363  else if(!std::strcmp(argv[i], "-confprefix")) // Not for FLTK
364  {
365  option = 2;
366  }
367  else if(!std::strcmp(argv[i], "-4")) // Not for FLTK
368  {
369  // Set flag to force IPv4 network protocol
370  inet_force_ipv4 = 1;
371  }
372  else if(!std::strcmp(argv[i], "-display")
373  || !std::strcmp(argv[i], "-geometry")
374  || !std::strcmp(argv[i], "-iconic")
375  || !std::strcmp(argv[i], "-notooltips"))
376  {
377  // Handled by FLTK
378  option = 1;
379  }
380  else if(option)
381  {
382  if(2 == option)
383  {
384  // Special handling for option "-confprefix"
385  tmp = new char[std::strlen(argv[i]) + (std::size_t) 1];
386  std::strcpy(tmp, argv[i]);
387  main_confprefix = tmp;
388  }
389  option = 0;
390  }
391  else if('-' == argv[i][0])
392  {
393  // Unknown option
394  PRINT_ERROR("Unknown option");
395  rv2 = ERROR;
396  break;
397  }
398  }
399  if(ERROR == rv2)
400  {
401  PRINT_ERROR("Use '-h' or read the man page for help");
402  }
403  }
404  if(OK == rv2 && main_debug)
405  {
406  std::cout << CFG_NAME << ": " << MAIN_ERR_PREFIX
407  << "Debug mode enabled" << std::endl << std::flush;
408  if(inet_force_ipv4)
409  {
410  std::cout << CFG_NAME << ": " << MAIN_ERR_PREFIX
411  << "Force IPv4 network protocol" << std::endl << std::flush;
412  }
413  }
414 
415  // Remove options that are not supported by FLTK
416 repeat:
417  removed = 0;
418  for(i = 1; i < (unsigned int) argc; i++)
419  {
420  j = 0;
421  while(NULL != options_to_remove[j])
422  {
423  if(!std::strcmp(argv[i], options_to_remove[j]))
424  {
425  // Found option to remove
426  for (k = i; k < (unsigned int) argc; k++)
427  {
428  argv[k] = argv[k + 1U];
429  }
430  --argc;
431  // Remove potential value of option too
432  if(i < (unsigned int) argc && '-' != argv[i][0])
433  {
434  for (k = i; k < (unsigned int) argc; k++)
435  {
436  argv[k] = argv[k + 1U];
437  }
438  --argc;
439  }
440  removed = 1;
441  break;
442  }
443  ++j;
444  }
445  // Repeat loop because argc has changed
446  if(removed) { break; }
447  }
448  if(removed) { goto repeat; }
449  if(OK == rv2 && main_debug)
450  {
451  std::cout << CFG_NAME << ": " << MAIN_ERR_PREFIX
452  << "Options passed to FLTK: ";
453  for(i = 1; i < (unsigned int) argc; i++)
454  {
455  std::cout << argv[i] << " ";
456  }
457  std::cout << std::endl << std::flush;
458  }
459 
460  // Load configuration (after options are parsed because of "-confprefix")
461  if(OK == rv2)
462  {
463  if(main_debug && NULL != main_confprefix)
464  {
465  std::cout << CFG_NAME << ": " << MAIN_ERR_PREFIX
466  << "Configuration directory path: "
467  << main_confprefix << std::endl << std::flush;
468  }
469  rv = conf_load(config);
470  if(0 != rv)
471  {
472  rv2 = ERROR;
473  }
474  else
475  {
476  // Valid default configuration is sufficient
477  main_config_loaded = 1;
478  }
479  }
480 
481  // Check what to do
482  switch(rv2)
483  {
484  case OK:
485  {
486  // Normal startup into user interface
487  // Attention:
488  // All modules that spawn additional threads must be initialized by the
489  // UI, not here!
490  nls_init(); // Don't abort on error, the NLS module will always work
491  if(0 > filter_init(ui_get_locale_utf8())) // Do this after NLS init
492  {
493  rv = EXIT_FAILURE;
494  break;
495  }
496 #if !CFG_CMPR_DISABLE
497  if(0 > cmpr_init())
498  {
499  rv = EXIT_FAILURE;
500  break;
501  }
502 #endif // !CFG_CMPR_DISABLE
503 #if CFG_USE_TLS
504  if(0 > tls_init())
505  {
506  rv = EXIT_FAILURE;
507  break;
508  }
509 #endif // CFG_USE_TLS
510  digest_init();
511  hmac_init();
512  group_init();
513  if(0 > db_init())
514  {
515  rv = EXIT_FAILURE;
516  break;
517  }
518  // Core init is handled by UI
519  ui_init(argc, argv);
520  loop();
521  std::cout << CFG_NAME << ": " << MAIN_ERR_PREFIX
522  << "Shutdown" << std::endl << std::flush;
523  // Core shutdown is handled by UI
524  ui_exit();
525  db_exit();
526  group_exit();
527  hmac_exit();
528  digest_exit();
529 #if CFG_USE_TLS
530  tls_exit();
531 #endif // CFG_USE_TLS
532 #if !CFG_CMPR_DISABLE
533  cmpr_exit();
534 #endif // !CFG_CMPR_DISABLE
535  filter_exit();
536  nls_exit();
537  rv = EXIT_SUCCESS;
538  break;
539  }
540  case ABORT:
541  {
542  // Nothing more to do, but no error
543  rv = EXIT_SUCCESS;
544  break;
545  }
546  default:
547  {
548  // Error
549  rv = EXIT_FAILURE;
550  break;
551  }
552  }
553 
554  save_exit(rv);
555  // We should never be here
556  PRINT_ERROR("Fatal error: Hit end of main()");
557 }
558 
559 
560 //! @}
561 
562 // EOF
ts_environ_exit
void ts_environ_exit(void)
Destroy copy of environment variables.
Definition: ts_functions.c:107
tls_exit
void tls_exit(void)
Shutdown TLS subsystem.
Definition: tls.c:1813
digest_exit
void digest_exit(void)
Shutdown message digest module.
Definition: digest.c:200
db_exit
int db_exit(void)
Shutdown database.
Definition: database.c:317
MAIN_ERR_PREFIX
#define MAIN_ERR_PREFIX
Message prefix for MAIN module.
Definition: main.cxx:55
filter_exit
void filter_exit(void)
Shutdown filter module.
Definition: filter.c:1335
config
struct conf config[CONF_NUM]
Global configuration.
Definition: conf.c:63
conf_store
int conf_store(struct conf *cfg)
Store configuration to config file.
Definition: conf.c:818
ts_environ_init
void ts_environ_init(void)
Copy environment variables.
Definition: ts_functions.c:58
nls_exit
void nls_exit(void)
Shutdown NLS subsystem.
Definition: nls.c:447
hmac_exit
void hmac_exit(void)
Shutdown HMAC module.
Definition: hmac.c:269
core_check_thread_ui
int core_check_thread_ui(void)
Check whether code is running in UI thread (exported for UI)
Definition: core.c:6596
sighandler_install
int sighandler_install(void)
Install signal handlers.
Definition: sighandler.c:66
ui_unlock
int ui_unlock(void)
Unlock for multithread support.
Definition: gui.cxx:12728
main_debug
int main_debug
Enable additional debug output if nonzero.
Definition: main.cxx:64
hmac_init
void hmac_init(void)
Initialize HMAC module.
Definition: hmac.c:260
tls_init
int tls_init(void)
Init TLS subsystem.
Definition: tls.c:1762
ui_exec
int ui_exec(void)
Drive GUI.
Definition: gui.cxx:12407
conf_delete
void conf_delete(struct conf *cfg)
Delete configuration.
Definition: conf.c:726
cmpr_exit
void cmpr_exit(void)
Shutdown compress module.
Definition: compression.c:870
cmpr_init
int cmpr_init(void)
Initialize compression module.
Definition: compression.c:821
ui_get_locale_utf8
int ui_get_locale_utf8(void)
Check whether locale use UTF-8 encoding.
Definition: gui.cxx:12584
sighandler_check_abort
int sighandler_check_abort(void)
Check abort flag.
Definition: sighandler.c:155
PRINT_ERROR
#define PRINT_ERROR(s)
Prepend module prefix and print error message.
Definition: main.h:19
ts_unlock_ui
int ts_unlock_ui(void)
Unlock UI thread.
Definition: main.cxx:244
ui_lock
int ui_lock(void)
Lock for multithread support.
Definition: gui.cxx:12706
db_init
int db_init(void)
Init database.
Definition: database.c:293
main_confprefix
const char * main_confprefix
Configuration directory path from command line option or NULL otherwise.
Definition: main.cxx:67
group_exit
void group_exit(void)
Shutdown group handling.
Definition: group.c:643
conf_load
int conf_load(struct conf *cfg)
Load configuration from config file.
Definition: conf.c:762
group_init
void group_init(void)
Initialize group handling.
Definition: group.c:550
nls_init
int nls_init(void)
Init NLS subsystem.
Definition: nls.c:173
log_delete_logfile
void log_delete_logfile(void)
Delete logfile if present.
Definition: log.c:85
filter_init
int filter_init(int utf8)
Initialize filter module.
Definition: filter.c:1047
main
int main(int argc, char **argv)
Program entry point.
Definition: main.cxx:315
digest_init
void digest_init(void)
Initialize message digest module.
Definition: digest.c:191
ts_lock_ui
int ts_lock_ui(void)
Lock UI thread.
Definition: main.cxx:217
ui_exit
void ui_exit(void)
Shutdown GUI.
Definition: gui.cxx:12527
print_error
void print_error(const char *msg)
Print error message.
Definition: main.cxx:276
ui_init
void ui_init(int argc, char **argv)
Init GUI.
Definition: gui.cxx:12139

Generated at 2024-04-27 using  doxygen