1 // -*- c++ -*-
2 //------------------------------------------------------------------------------
3 // GenServer.h
4 //------------------------------------------------------------------------------
5 // Copyright (c) 1999-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 #ifndef GENSERVER_H
13 #define GENSERVER_H
14
15 extern "C" {
16 #include <stdio.h> /* printf, sprintf */
17 #include <unistd.h>
18 #include <stdlib.h> /* getopt() */
19 #include <string.h> /* strlen */
20 #include <errno.h> /* errno */
21 #include <signal.h> /* kill */
22 #include <sys/types.h> /* open */
23 #include <sys/stat.h> /* open */
24 #include <fcntl.h> /* open */
25 #include <limits.h> /* PATH_MAX */
26 #include <assert.h>
27
28 #if !defined(WIN32)
29 # include <sys/resource.h> /* getrlimit */
30 # include <syslog.h>
31 #endif
32 }
33
34 #include <iostream>
35 #include <sstream>
36 #include <string>
37 #include <vector>
38
39 using std::string;
40 using std::vector;
41
42 #include "assa/Assure.h"
43 #include "assa/Handlers.h"
44 #include "assa/SigHandlers.h"
45 #include "assa/Fork.h"
46 #include "assa/Reactor.h"
47 #include "assa/CmdLineOpts.h"
48 #include "assa/PidFileLock.h"
49
50 namespace ASSA {
51
52 /** @file GenServer.h
53
54 GenServer is a base class for generic servers.
55
56 This is an implementation of the Service Configurator pattern.
57 */
58
59 class GenServer :
60 public virtual EventHandler,
61 public CmdLineOpts
62 {
63 public:
64 /** @enum LogFlag
65 */
66 enum LogFlag {
67 KEEPLOG, /**< By default, append new log records to
68 the existing log file. This is operational
69 mode. */
70 RMLOG /**< Remove existing log file and start afresh.
71 Convenient during development phase. */
72 };
73
74 public:
75 /** Constructor. Corresponds to the object entering the <tt>IDLE</tt>
76 state.
77 */
78 GenServer ();
79
80 /// Destructor
81 virtual ~GenServer ();
82
83 /** Provide an entry point into the service and perfom initialization
84 of the service.
85
86 Open log file and log startup options. Process standard command-line
87 arguments.
88 Following signals are handled in uniform manner:
89 SIGHUP, SIGPIPE, SIGCHLD, SIGCLD, SIGALRM, SIGINT, SIGPOLL,
90 SIGTERM.
91
92 This function corresponds to the object moving from IDLE
93 to RUNNING state as result of service initialization,
94 or reconfiguration of the service and remaining in RUNNING state.
95
96 @param argc Pointer to number of command line arguments
97 @param argv Command line arguments char* array
98 @param help_info Title that will be displayed with -h option
99 */
100
101 virtual void init (int* argc, char* argv[], const char* help_info);
102
103 /** This is an iterface function corresponding to the object
104 moving back into IDLE state. Derived class is expected to
105 perform actions that terminate execution of the service.
106 */
fini(void)107 virtual int fini (void) { return 0; }
108
109 /** Temporarily suspend the execution of a service. Corresponds
110 to process leaving RUNNING state and entering SUSPENDED state.
111 */
suspend(void)112 virtual int suspend (void) { return 0; }
113
114 /** Resume execution of a service. Corresponds to the process
115 returning back to RUNNING state from SUSPENDED state.
116 */
resume(void)117 virtual int resume (void) { return 0; }
118
119 /** Interface function provided for derived classes as a
120 * place to initialize specifics of derived server
121 */
122 virtual void init_service () =0;
123
124 /** Interface function provided for derived classes as the main
125 * entry for data processing. This is the place to implement main
126 * event loop.
127 */
128 virtual void process_events () =0;
129
130 /** Hook for derived class to do addition clean-up when
131 terminating signal is delivered by OS. Note that
132 signal handling is provided by default and no additional
133 intervention is necessary. Use this method only to enhance it.
134 */
fatal_signal_hook()135 virtual void fatal_signal_hook () { /*--- empty ---*/ }
136
137 /** Handle fatal signals. Hook (e.g. fatalSignalHook) is provided
138 if derived class needs extra work before falling dead.
139 */
140 int handle_signal (int signum_);
141
142 /** Normally called by the main loop to find out whether
143 'graceful quit' flag had been raised, signaling that some
144 application's component requested to end data processing.
145
146 @return true when active; false if 'graceful quit' flag
147 has been raised;
148 */
service_is_active()149 bool service_is_active () { return (!m_graceful_quit); }
150
151 /** Inform server that it has to stop data processing,
152 clean up and exit. This method will also stop internal Reactor.
153 */
154 void stop_service ();
155
156 /** Set Version and Revision number.
157
158 @param release_ Release number.
159 @param revision_ Patch level.
160 */
161 void set_version (const string& release_, int revision_);
162
163 /// Obtain version information
164 string get_version ();
165
166 /// Set author's name.
167 void set_author (const string& author_);
168
169 /** New debug information is added to the old log file.
170 To erase old log file, set flag to RMLOG.
171
172 @param logf_ Defaulted to KEEPLOG that adds log records to
173 the existing log file; RMLOG - remove existing log
174 file and start afresh.
175 */
set_flags(LogFlag logf_)176 void set_flags (LogFlag logf_) { m_log_flag = logf_; }
177
178 /// List options and invocation syntax to stdout
179 virtual void display_help ();
180
181 /// Get name of process+instance_number
get_proc_name()182 string get_proc_name () { return m_proc_name; }
183
184 /** Change process name.
185 @param proc_name_ new process name
186 */
set_proc_name(string proc_name_)187 void set_proc_name (string proc_name_) { m_proc_name = proc_name_; }
188
189 /// Get command-line process name
get_cmdline_name()190 string get_cmdline_name () { return m_cmdline_name; }
191
192 /** Get default configuration file name:
193 $HOME/.{command_line_name}.cfg
194 If you want your configuration file name to be
195 different, change the value of m_std_config_name
196 in derived class
197 */
get_default_config_file()198 string get_default_config_file () { return m_default_config_file; }
199
200 /** Get alternative configuration file name. This name is
201 specified as command-line argument '-f'
202 */
get_config_file()203 string get_config_file () { return m_config_file; }
204
205 /// Return assumed name of the listening port
get_port()206 string get_port () { return m_port; }
207
208 /** Set listening port name
209 @param port_ new listening port name
210 */
set_port(string port_)211 void set_port (string port_) { m_port = port_; }
212
213 #if !defined(WIN32)
214 /** Obtain reference to the Signal Manager, class SigHandls.
215 */
get_sig_manager()216 SigHandlers& get_sig_manager () { return m_sig_dispatcher; }
217 #endif
218
219 /** Obtain reference to the Reactor.
220 */
get_reactor()221 Reactor* get_reactor () { return &m_reactor; }
222
223 /// Become a daemon process
224 static bool become_daemon ();
225
226 /// Retrieve exit value of the process
get_exit_value()227 int get_exit_value () const { return m_exit_value; }
228
229 protected:
230 /// Set exit value of the process. This value is returned to the shell.
set_exit_value(int v_)231 void set_exit_value (int v_) { m_exit_value = v_; }
232
233 protected:
234 /// process name (considering instance_number)
235 string m_proc_name;
236
237 /// process name as appeared on command line
238 string m_cmdline_name;
239
240 /// listening port name
241 string m_port;
242
243 /// standard configuration file name
244 string m_default_config_file;
245
246 /// alternative configuration file name
247 string m_config_file;
248
249 /// Max size of the log file
250 u_int m_log_size;
251
252 /// Process instance
253 int m_instance;
254
255 /// Full pathname of debug file
256 string m_log_file;
257
258 /// If 'yes', send log messages to the log server.
259 string m_with_log_server;
260
261 /** Log server, assa-logd, address (port@@host)
262 */
263 string m_log_server;
264
265 /// Debug file mask to filter debug/error messages
266 long m_mask;
267
268 /// Flag that indicates wheather server outgh to stop and exit
269 bool m_graceful_quit;
270
271 #if !defined(WIN32)
272 /// Signal handlers dispatcher
273 SigHandlers m_sig_dispatcher;
274
275 /// Function that swallows SIGPOLL calls
276 SIGPOLLHandler m_sig_poll;
277 #endif
278
279 /// GenServer object has its very own personal Reactor object.
280 Reactor m_reactor;
281
282 /// Software version
283 string m_version;
284
285 /// Software revision (patch) level
286 int m_revision;
287
288 /// Author's name
289 string m_author;
290
291 /// Help information
292 const char* m_help_msg;
293
294 /// Log file initialization flag. If RM_LOG, remove old log file.
295 LogFlag m_log_flag;
296
297 /** If 'yes', redirects all logging messages to std::cerr. */
298 string m_log_stdout;
299
300 /// Daemon option flag. If 'yes', become a UNIX daemon process.
301 string m_daemon;
302
303 /// If 'yes', skip PID file locking creation/locking step
304 string m_ommit_pidfile;
305
306 /** Logging level - an integer number that incrementally increases
307 verbosity of the looing messages. The exact meaning of each
308 level is application-specific.
309 */
310 int m_log_level;
311
312 /// PID File lock
313 PidFileLock m_pidfile_lock;
314
315 /// PID File lock path name
316 string m_pidfile;
317
318 /** Help option flag. If true, [-h, --help] option is being
319 specified on command line.
320 */
321 bool m_help_flag;
322
323 /** Version option flag. If true, [-v, --version] options is being
324 specified on command line.
325 */
326 bool m_version_flag;
327
328 /// Exit value of the process.
329 int m_exit_value;
330
331 private:
332 /// No cloning
333 GenServer (const GenServer&);
334 GenServer& operator=(const GenServer&);
335
336 /// Initialize internals
337 void init_internals ();
338 };
339
340
341 inline void
342 GenServer::
stop_service()343 stop_service ()
344 {
345 m_graceful_quit = true;
346 m_reactor.deactivate ();
347 }
348
349 inline void
350 GenServer::
set_version(const string & release_,int revision_)351 set_version (const string& release_, int revision_)
352 {
353 m_version = release_;
354 m_revision = revision_;
355 }
356
357 inline void
358 GenServer::
set_author(const string & author_)359 set_author (const string& author_)
360 {
361 m_author = author_;
362 }
363
364 inline string
365 GenServer::
get_version()366 get_version ()
367 {
368 std::ostringstream v;
369 v << "Version: " << m_version << " Revision: " << m_revision << std::ends;
370 return (v.str ());
371 }
372
373 inline void
374 GenServer::
display_help()375 display_help ()
376 {
377 std::cout << m_help_msg << '\n'
378 << "Written by " << m_author << "\n" << std::endl;
379 }
380
381 } // The end of namespase ASSA
382
383
384 #endif /* GENSERVER_H */
385