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