libassa  3.5.1
GenServer.cpp
Go to the documentation of this file.
1 // -*- c++ -*-
2 //---------------------------------------------------------------------------
3 // GenServer.cpp
4 //---------------------------------------------------------------------------
5 // Copyright (c) 1997-2004,2005 by Vladislav Grinchenko
6 //
7 // This library is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU Library General Public
9 // License as published by the Free Software Foundation; either
10 // version 2 of the License, or (at your option) any later version.
11 //---------------------------------------------------------------------------
12 
13 /*
14  [a e g ijk o qr tu wxy ]
15  [ABC EFGHIJK MNOPQR TUVWXYZ]
16 
17 " Standard command-line arguments: \n"
18 " \n"
19 " -b, --daemon BOOL - Run process as true UNIX daemon \n"
20 " -l, --pidfile PATH - The process ID is written to the lockfile PATH \n"
21 " instead of default ~/.{procname}.pid \n"
22 " -L, --ommit-pidfile BOOL - Do not create PID lockfile \n"
23 " -d, --log-stdout BOOL - Write debug to standard output \n"
24 " -D, --log-file NAME - Write debug to NAME file \n"
25 " -z, --log-size NUM - Maximum size debug file can reach \n"
26 " (default is 10Mb) \n"
27 " -c, --log-level NUM - Log verbosity \n"
28 " -s, --with-log-server BOOL - Redirect log messages to the log server \n"
29 " -S, --log-server NAME - Define assa-logd server address \n"
30 " (default: assalogd@localhost) \n"
31 " -m, --mask MASK - Mask (default: ALL = 0x7fffffff) \n"
32 " -p, --port NAME - The TCP/IP port NAME (default - procname) \n"
33 " -n, --instance NUM - Process instance NUM (default - none) \n"
34 " -f, --config-file NAME - Alternative config file NAME \n"
35 " -h, --help - Print this message \n"
36 " -v, --version - Print version number \n"
37 " \n"
38 " NOTE: BOOL value is either 'yes' or 'no' \n"
39 */
40 //------------------------------------------------------------------------------
41 
42 #include <sys/types.h> // stat(2)
43 #include <sys/stat.h> // stat(2)
44 #include <unistd.h> // stat(2)
45 
46 #ifdef __CYGWIN32__ // to resolve h_errno dependency
47 # include <errno.h>
48 # include <netdb.h>
49 #endif
50 
51 #include "assa/GenServer.h"
52 #include "assa/CommonUtils.h"
53 
54 using namespace ASSA;
55 
57  :
58  m_log_size (10485760), // 10 Mb
59  m_instance (-1),
60  m_with_log_server ("no"),
61  m_log_server ("assalogd@"),
62  m_mask (ALL),
63  m_graceful_quit (false),
64  m_version ("unknown"),
65  m_revision (0),
66  m_author ("John Doe"),
67  m_help_msg ("No help available"),
68  m_log_flag (KEEPLOG),
69  m_log_stdout ("no"),
70  m_daemon ("no"),
71  m_ommit_pidfile ("no"),
72  m_log_level (-1),
73  m_help_flag (false),
74  m_version_flag (false),
75  m_exit_value (0)
76 {
77  add_flag_opt ('h', "help", &m_help_flag);
78  add_flag_opt ('v', "version", &m_version_flag);
79 
80  add_opt ('d', "log-stdout", &m_log_stdout);
81  add_opt ('b', "daemon", &m_daemon);
82  add_opt ('L', "ommit-pidfile", &m_ommit_pidfile);
83  add_opt ('s', "with-log-server", &m_with_log_server);
84  add_opt ('m', "mask", &m_mask);
85  add_opt ('D', "log-file", &m_log_file);
86  add_opt ('f', "config-file", &m_config_file);
87  add_opt ('n', "instance", &m_instance);
88  add_opt ('p', "port", &m_port);
89  add_opt ('z', "log-size", &m_log_size);
90  add_opt ('l', "pidfile", &m_pidfile);
91  add_opt ('S', "log-server", &m_log_server);
92  add_opt ('c', "log-level", &m_log_level);
93 
96  char hn[64];
97  ::gethostname (hn, sizeof (hn)-1);
98  m_log_server += hn;
99 }
100 
112 ~GenServer ()
113 {
114  Log::log_close ();
115 }
116 
117 //------------------------------------------------------------------------------
118 // Get command line process name parse command line arguments
119 // request internals initialization.
120 //------------------------------------------------------------------------------
121 
122 void
124 init (int* argc, char* argv [], const char* ht_)
125 {
126  char* cp = argv [0];
127  m_help_msg = ht_;
128 
133  if (strchr(cp, ASSA_DIR_SEPARATOR)) {
134  cp += strlen(argv[0]); // position at the end
135  while (*cp-- != ASSA_DIR_SEPARATOR) {
136  ;
137  }
138  cp += 2;
139  }
140 
141 #if defined (WIN32) // get rid of '.exe'
142  char* extidx = cp;
143  while (*extidx) {
144  if (*extidx == '.') {
145  *extidx = '\0';
146  break;
147  }
148  extidx++;
149  }
150 #endif
151  m_cmdline_name = cp;
152 
153  if (!parse_args ((const char **)argv)) {
154  std::cerr << "Error in arguments: " << get_opt_error () << std::endl;
155  std::cerr << "Try '" << argv[0] << " --help' for details.\n";
156  exit (1);
157  }
158 
159  if (m_help_flag) {
160  display_help ();
161  exit (0);
162  }
163 
164  if (m_version_flag) {
165  std::cerr << '\n' << argv[0] << " " << get_version () << '\n' << '\n'
166  << "Written by " << m_author << "\n\n";
167  exit (0);
168  }
169 
173  std::string s;
174 
175  if (m_default_config_file.size ()) {
178  }
179 
180  if (m_config_file.size ()) {
181  s = ASSA::Utils::strenv (m_config_file.c_str ());
182  m_config_file = s;
183  }
184 
185  if (m_log_file.size ()) {
186  s = ASSA::Utils::strenv (m_log_file.c_str ());
187  m_log_file = s;
188  }
189 
190  if (m_pidfile.size ()) {
191  s = ASSA::Utils::strenv (m_pidfile.c_str ());
192  m_pidfile = s;
193  }
194 
197  if (m_daemon == "yes") {
198  assert(become_daemon ());
199  }
200 
203  char instbuf[16]; // INT_MAX [-]2147483647
204  sprintf(instbuf, "%d", m_instance);
205 
206  if (m_proc_name.length() == 0) {
208 
209  if (m_instance != -1) {
210  m_proc_name += instbuf;
211  }
212  }
213  if (m_port.length() == 0) {
215  }
216 
217 #if !defined(WIN32)
221  SigAction ignore_act( SIG_IGN );
222 
230  ignore_act.register_action( SIGHUP );
231 
232  ignore_act.register_action( SIGPIPE );
233  ignore_act.register_action( SIGCHLD );
234 #if !(defined (__FreeBSD__) || defined(__FreeBSD_kernel__) \
235  || defined (__NetBSD__))
236  ignore_act.register_action( SIGCLD );
237 #endif
238  ignore_act.register_action( SIGALRM );
239 
245 
252  m_sig_dispatcher.install ( SIGINT, (EventHandler*) this );
253 
260  m_sig_dispatcher.install ( SIGTERM, (EventHandler*) this );
261 
262 #endif // !defined(WIN32)
263 
266  init_internals ();
267 }
268 
269 void
272 {
273  static const char self[] = "GenServer::init_internals";
274 
279 #if defined (WIN32)
280  m_default_config_file = this->get_cmdline_name () + ".ini";
281 #else
282  m_default_config_file = "$HOME/." + this->get_cmdline_name ();
284 #endif
285 
291  if (m_log_flag == RMLOG && m_log_stdout == "no")
292  {
293  struct stat fst;
294  if (::stat (m_log_file.c_str(), &fst) == 0)
295  {
296  if (S_ISREG (fst.st_mode)) {
297  ::unlink (m_log_file.c_str());
298  }
299  }
300  }
301 
310 
311  if (m_log_stdout == "yes") {
313  }
314  else {
315  if (m_with_log_server == "yes") {
317  m_log_file.c_str(),
318  get_reactor (),
319  m_mask,
320  m_log_size) ;
321  }
322  else {
324  }
325  }
326 
327  trace(self);
328 
329  if (m_ommit_pidfile == "no")
330  {
331  if (m_pidfile.size () == 0) {
332  string s ("~/." + m_proc_name + ".pid");
333  m_pidfile = ASSA::Utils::strenv (s.c_str ());
334  }
335  if (! m_pidfile_lock.lock (m_pidfile)) {
336  DL((ASSAERR,"Failed to lock PID file: %s\n",
338  exit (1);
339  }
340  }
341 
342  DL((APP,"\n" ));
343  DL((APP,"========================================================\n"));
344  DL((APP,"|| Server configuration settings ||\n"));
345  DL((APP,"========================================================\n"));
346  DL((APP," cmd_line_name = '%s'\n", m_cmdline_name.c_str() ));
347  DL((APP," name = '%s'\n", m_proc_name.c_str() ));
348  DL((APP," default config file = '%s'\n", m_default_config_file.c_str()));
349  DL((APP," config file = '%s'\n", m_config_file.c_str() ));
350  DL((APP," mask = 0x%X\n", m_mask ));
351  dump ();
352  DL((APP,"========================================================\n"));
353  DL((APP,"\n"));
354 }
355 
356 bool
358 become_daemon ()
359 {
360 #if defined(WIN32)
361  return true;
362 #else
364 
365  if (!f.isChild ()) { // parent exits
366  exit (0);
367  }
368 
369  int size = 1024;
370  int i = 0;
371  pid_t nullfd;
372 
373  for (i = 0; i < size; i++) {
374  (void) close (i);
375  }
376 
377  nullfd = open ("/dev/null", O_WRONLY | O_CREAT, 0666);
378  if (nullfd == -1) {
379  syslog (LOG_ERR,"failed to open \"/dev/null\"");
380  return false;
381  }
382 
383  (void) dup2 (nullfd, 1);
384  (void) dup2 (nullfd, 2);
385  (void) close (nullfd);
386 
387  if ( setsid() == -1 ) {
388  syslog (LOG_ERR,"setsid() failed");
389  return false;
390  }
391 
392  /*---
393  Changing to root directory would be the right thing to do for a
394  server (so that it wouldn't possibly depend on any mounted file
395  systems. But, in practice, it might cause a lot of problems.
396  ---*/
397 #if 0
398  if ( chdir("/") == -1 ) {
399  return false;
400  }
401 #endif
402  return (true);
403 
404 #endif // defined(WIN32)
405 }
406 
407 int
409 handle_signal (int signum_)
410 {
411  trace("GenServer::handle_signal");
412  std::ostringstream m;
413 
414  switch (signum_)
415  {
416  case SIGTERM: m << "SIGTERM signal caugth. "; break;
417  case SIGINT: m << "SIGINT signal caugth. "; break;
418  default: m << "Unexpected signal caugth.";
419  }
420  m << "Signal # " << signum_ << std::ends;
421  DL((APP,"%s\n", m.str ().c_str () ));
422  DL((APP,"Initiating shutdown sequence...\n"));
423 
425 
426  DL((APP, "Shutdown sequence completed - Exiting !\n"));
427 
428  /* Calling stop_service () triggers a call to Reactor::stopReactor()
429  with subsequent call to Reactor::removeIOHandler() and then
430  EventHandler::handle_close(). If EventHandler is in the middle
431  of the *slow* system call such as read(2), handle_close() will
432  destry EventHandler, and after cotrol is returned from
433  GenServer::handle_signal(), *slow* system call is restarted
434  and proceeds to operate on the memory that has been deleted already.
435 
436  Calling Reactor::deactivate() instead delays memory release.
437  */
438  get_reactor()->deactivate ();
439  m_graceful_quit = true;
440 
441  return 0;
442 }
443 
#define ASSA_DIR_SEPARATOR
CommonUtils.h.
Definition: CommonUtils.h:47
GenServer is a base class for generic servers.
#define ASSAIOSIG
Definition: Handlers.h:38
#define trace(s)
trace() is used to trace function call chain in C++ program.
Definition: Logger.h:429
#define DL(X)
A macro for writing debug message to the Logger.
Definition: Logger.h:273
bool parse_args(const char *argv[])
Parse command line arguments based on installed options set.
bool add_opt(const char c, const string &s, string *str)
Add an option with STL string argument.
bool add_flag_opt(const char c, const string &s, bool *f)
Add binary flag option.
void dump() const
Write options set to the log file.
const char * get_opt_error() const
If previous call to one of member functions returned false, retrieve detailed error message.
Definition: CmdLineOpts.h:309
EventHandler class.
Definition: EventHandler.h:103
Fork class is a simple wrapper around C library function fork().
Definition: Fork.h:86
@ LEAVE_ALONE
Ignore all running children on exit.
Definition: Fork.h:94
bool isChild() const
Test whether we are in child section of the code.
Definition: Fork.h:136
@ IGNORE_STATUS
Don't wait for child to complete.
Definition: Fork.h:100
string m_log_server
Log server, assa-logd, address (port@host)
Definition: GenServer.h:263
string m_author
Author's name.
Definition: GenServer.h:289
static bool become_daemon()
Become a daemon process.
Definition: GenServer.cpp:358
string m_cmdline_name
process name as appeared on command line
Definition: GenServer.h:238
string get_cmdline_name()
Get command-line process name.
Definition: GenServer.h:190
int m_instance
Process instance.
Definition: GenServer.h:253
GenServer()
Constructor.
Definition: GenServer.cpp:56
string m_log_file
Full pathname of debug file.
Definition: GenServer.h:256
virtual ~GenServer()
Destructor.
Definition: GenServer.cpp:112
PidFileLock m_pidfile_lock
PID File lock.
Definition: GenServer.h:313
bool m_graceful_quit
Flag that indicates wheather server outgh to stop and exit.
Definition: GenServer.h:269
string m_config_file
alternative configuration file name
Definition: GenServer.h:247
void init_internals()
Initialize internals.
Definition: GenServer.cpp:271
string m_ommit_pidfile
If 'yes', skip PID file locking creation/locking step.
Definition: GenServer.h:304
string m_pidfile
PID File lock path name.
Definition: GenServer.h:316
virtual void display_help()
List options and invocation syntax to stdout.
Definition: GenServer.h:375
string m_with_log_server
If 'yes', send log messages to the log server.
Definition: GenServer.h:259
bool m_version_flag
Version option flag.
Definition: GenServer.h:326
u_int m_log_size
Max size of the log file.
Definition: GenServer.h:250
long m_mask
Debug file mask to filter debug/error messages.
Definition: GenServer.h:266
string m_log_stdout
If 'yes', redirects all logging messages to std::cerr.
Definition: GenServer.h:298
string m_default_config_file
standard configuration file name
Definition: GenServer.h:244
SIGPOLLHandler m_sig_poll
Function that swallows SIGPOLL calls.
Definition: GenServer.h:276
@ RMLOG
Remove existing log file and start afresh.
Definition: GenServer.h:70
string m_port
listening port name
Definition: GenServer.h:241
LogFlag m_log_flag
Log file initialization flag. If RM_LOG, remove old log file.
Definition: GenServer.h:295
virtual void fatal_signal_hook()
Hook for derived class to do addition clean-up when terminating signal is delivered by OS.
Definition: GenServer.h:135
const char * m_help_msg
Help information.
Definition: GenServer.h:292
string get_proc_name()
Get name of process+instance_number.
Definition: GenServer.h:182
virtual void init(int *argc, char *argv[], const char *help_info)
Provide an entry point into the service and perfom initialization of the service.
Definition: GenServer.cpp:124
string m_proc_name
process name (considering instance_number)
Definition: GenServer.h:235
int m_log_level
Logging level - an integer number that incrementally increases verbosity of the looing messages.
Definition: GenServer.h:310
Reactor * get_reactor()
Obtain reference to the Reactor.
Definition: GenServer.h:221
int handle_signal(int signum_)
Handle fatal signals.
Definition: GenServer.cpp:409
SigHandlers m_sig_dispatcher
Signal handlers dispatcher.
Definition: GenServer.h:273
string get_version()
Obtain version information.
Definition: GenServer.h:366
bool m_help_flag
Help option flag.
Definition: GenServer.h:321
string m_daemon
Daemon option flag. If 'yes', become a UNIX daemon process.
Definition: GenServer.h:301
const char * get_error_msg() const
In case of error, return a verbal description of the last error.
Definition: PidFileLock.h:141
bool lock(const string &filename_)
Lock the file.
Definition: PidFileLock.cpp:62
void deactivate(void)
Deactivate Reactor.
Definition: Reactor.h:234
int register_action(int signum_, SigAction *oaction_=0)
Register this object as current disposition for signal signum_, and store old disposition in oaction_...
Definition: SigAction.h:360
virtual int install(int signum_, EventHandler *new_hand_, SigAction *new_disp_=0, EventHandler **old_hand_=0, SigAction *old_disp_=0)
Register EventHandler with dispatching system.
Definition: SigHandlers.cpp:36
int open_log_file(const char *logfname_, u_long groups_=ALL, u_long maxsize_=10485760)
Open log file.
Definition: Logger.h:319
int log_close(void)
Close logging stream.
Definition: Logger.h:359
void set_app_name(const std::string &appname_)
Set application name.
Definition: Logger.h:305
int open_log_server(const std::string &logsvraddr_, const char *logfname_, Reactor *reactor_, u_long groups_=ASSA::ALL, u_long maxsize_=10485760)
Open connection with and write log message to the log server.
Definition: Logger.h:344
int open_log_stdout(u_long groups_=ALL)
Write log message to standard output.
Definition: Logger.h:327
std::string strenv(const char *in_)
Expand the passed string in_ by substituting environment variable names for their values.
Definition: Acceptor.h:40
@ APP
Application-level messages
Definition: LogMask.h:27
@ ASSAERR
ASSA and system errors
Definition: LogMask.h:34
@ ALL
All messages: library + application
Definition: LogMask.h:62
Socket & endl(Socket &os_)
endl manipulator.
Definition: Socket.h:602
Socket & ends(Socket &os_)
ends manipulator.
Definition: Socket.h:622