// -*- c++ -*-
//------------------------------------------------------------------------------
//                             GenServer.h
//------------------------------------------------------------------------------
//  Copyright (c) 1999 by Vladislav Grinchenko
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//------------------------------------------------------------------------------
#ifndef GENSERVER_H
#define GENSERVER_H

extern "C" {
#include <stdio.h>              /* printf, sprintf         */
#include <unistd.h>
#include <stdlib.h>				/* getopt()                */
#include <string.h>             /* strlen                  */
#include <errno.h>              /* errno                   */
#include <signal.h>             /* kill                    */
#include <sys/types.h>          /* open                    */
#include <sys/stat.h>           /* open                    */
#include <fcntl.h>              /* open                    */
#include <limits.h>             /* PATH_MAX                */
#include <sys/resource.h>		/* getrlimit               */
#include <syslog.h>
#include <assert.h>
}

//#include <iostream>
//#include <sstream>
#include <string>
#include <vector>

using std::string;
using std::vector;

#include "assa/Assert.h"
#include "assa/Handlers.h"
#include "assa/SigHandlers.h"
#include "assa/Fork.h"
#include "assa/Reactor.h"
#include "assa/CmdLineOpts.h"
#include "assa/PidFileLock.h"

namespace ASSA {

/** @file GenServer.h

    GenServer is a base class for generic servers. 

    This is an implementation of the Service Configurator pattern.
*/

class GenServer : 
	public virtual EventHandler, 
	public CmdLineOpts
{
public:
	/** @enum LogFlag
	*/
	enum LogFlag { 
		KEEPLOG,				/**< By default, append new log records to 
								   the  existing log file. This is operational 
								   mode. */
		RMLOG					/**< Remove existing log file and start afresh.
								   Convenient during development phase. */
	};

public:
	/** Constructor. Corresponds to the object entering the <tt>IDLE</tt>
	    state.
	 */
	GenServer ();

	/// Destructor
	virtual ~GenServer() {}

	/** Provide an entry point into the service and perfom initialization 
	    of the service.

	    Open log file and log startup options. Process standard command-line
		arguments.
	    Following signals are handled in uniform manner:
	    SIGHUP, SIGPIPE, SIGCHLD, SIGCLD, SIGALRM, SIGINT, SIGPOLL,
	    SIGTERM. 

	    This function corresponds to the object moving from IDLE
	    to RUNNING state as result of service initialization, 
	    or reconfiguration of the service and remaining in RUNNING state.
	  
	    @param argc Pointer to number of command line arguments
	    @param argv  Command line arguments char* array
	    @param help_info Title that will be displayed with -h option
	 */

	virtual void init (int* argc, char* argv[], const char* help_info);

	/** This is an iterface function corresponding to the object 
	    moving back into IDLE state. Derived class is expected to
	    perform actions that terminate execution of the service.
	*/
	virtual int fini (void) { return 0; }
	
	/** Temporarily suspend the execution of a service. Corresponds
	    to process leaving RUNNING state and entering SUSPENDED state.
	 */
	virtual int suspend (void) { return 0; }

	/** Resume execution of a service. Corresponds to the process
	    returning back to RUNNING state from SUSPENDED state.
	 */
	virtual int resume (void) { return 0; }

	/** Interface function provided for derived classes as a
	 *  place to initialize specifics of derived server
	 */
	virtual void initServer () =0;

	/** Interface function provided for derived classes as the main
	 *  entry for data processing. This is the place to implement main
	 *  event loop.
	 */
	virtual void processServer () =0;

	/** Hook for derived class to do addition clean-up when 
	    terminating signal is delivered by OS. Note that
	    signal handling is provided by default and no additional
	    intervention is necessary. Use this method only to enhance it.
	*/
	virtual void fatalSignalHook () { /*--- empty ---*/ }

	/** Handle fatal signals. Hook (e.g. fatalSignalHook) is provided 
		if derived class needs extra work before falling dead.
	*/
	int handle_signal (int signum_);

	/** Normally called by the main loop to find out whether 
	    'graceful quit' flag had been raised, signalling that some
	    application's component requested to end data processing.
	  
	    @return true when 'graceful quit' flag has been raised; 
	            false otherwise 
	 */
	bool stopServer() { return m_graceful_quit; }

	/** Inform server that it has to stop data processing,
	    clean up and exit. This method will also stop internal Reactor.
	 */
	void setStopServerFlag ();

	/** Set Version and Revision number. 

	    @param release_  Release number.
	    @param revision_ Patch level.
	*/
	void set_version (const string& release_, int revision_);

	/// Obtain version information
	string get_version ();

	/// Set author's name. 
	void set_author (const string& author_);

	/** New debug information is added to the old log file.
	    To erase old log file, set flag to RMLOG.

	    @param logf_ Defaulted to KEEPLOG that adds log records to 
	                 the existing log file; RMLOG - remove existing log
	                 file and start afresh.
	*/
	void set_flags (LogFlag logf_) { m_log_flag = logf_; }

	/// List options and invocation syntax to stdout
	virtual void displayHelp ();

	/// Get name of process+instance_number
	string   getProcName() { return m_proc_name; }

	/// Get command-line process name
	string   getCmdLineName() { return m_cmdline_name; }

	/** Get default configuration file name:
	    $HOME/.<command_line_name>.cfg
	    If you want your configuration file name to be
	    different, change the value of m_std_config_name
	    in derived class
	*/
	string getStdConfigName() { return m_std_config_name; }
	    
	/** Get alternative configuration file name. This name is
	    specified as command-line argument '-f'
	 */
	string getAltConfigName() { return m_alt_config_name; }

	/// Return assumed name of the listening port
	string getPortName() { return m_port; }

	/** Set listening port name
	    @param port_ new listening port name
	 */
	void setPortName (string port_) { m_port = port_; }

	/** Change process name.
	    @param proc_name_ new process name
	 */
	void setProcName (string proc_name_) { m_proc_name = proc_name_; }


	/** Obtain reference to the Signal Manager, class SigHandls.
	 */
	SigHandlers& get_sig_manager () { return m_sig_dispatcher; }
	
	/** Obtain reference to the Reactor.
	 */
    	Reactor* getReactor () { return &m_reactor; }

	/// Become a daemon process
	static bool becomeDaemon ();

	/// Retrieve exit value of the process
	int get_exit_value () const  { return m_exit_value; }

protected:
	/// Set exit value of the process. This value is returned to the shell.
    void set_exit_value (int v_) { m_exit_value = v_; }

protected:
	/// process name (considering instance_number)
	string   m_proc_name;            

	/// process name as appeared on command line
	string   m_cmdline_name;         

	/// listening port name
	string   m_port;

	/// standard configuration file name
	string   m_std_config_name;

	/// alternative configuration file name
	string   m_alt_config_name;
	
	/// Max size of the log file
	u_int    m_log_size;
	
	/// Process instance
	int      m_instance;
	
	/// Full pathname of debug file
	string   m_log_file;

	/// If true, send log messages to the log server
	bool     m_with_log_server;

	/// Log server, assa-logd, address (port@host)
	string   m_log_server;

	/// Debug file mask to filter debug/error messages
	long     m_debug_mask;

	/// Flag that indicates wheather server outgh to stop and exit
	bool     m_graceful_quit;

	/// Signal handlers dispatcher
	SigHandlers m_sig_dispatcher;

	/// Function that swallows SIGPOLL calls
	SIGPOLLHandler m_sig_poll;

	/// GenServer object has its very own personal Reactor object.
	Reactor m_reactor;

	/// Software version
	string m_version;
	
	/// Software revision (patch) level
       int m_revision;

	/// Author's name
	string m_author;

	/// Help information
	const char* m_help_msg;
	
	/// Log file initialization flag. If RM_LOG, remove old log file.
	LogFlag m_log_flag;

	/** If set, redirects all logging messages to std::cerr. */
	bool m_log_stdout_flag;
	
	/// Daemon option flag. If true, become a UNIX daemon process.
	bool m_daemon_flag;

	/** Help option flag. If true, [-h, --help] option is being 
		specified on command line.
	*/
	bool m_help_flag;

	/** Version option flag. If true, [-v, --version] options is being
		specified on command line.
	*/
	bool m_version_flag;

	/// If true, skip PID file locking creation/locking step
	bool m_ommit_pidflock_flag;

	/** Logging level - an integer number that incrementally increases
		verbosity of the looing messages. The exact meaning of each
		level is application-specific.
	*/
	unsigned int m_log_level;

	/// Exit value of the process. 
	int m_exit_value;

private:
	/// PID File lock
	PidFileLock m_pidfile_lock;

	/// PID File lock path name
	string m_pidfile;

private:
	/// No cloning 
	GenServer (const GenServer&);
	GenServer& operator=(const GenServer&);

	/// Initialize internals
	void initInternals ();
};


inline void
GenServer::
setStopServerFlag ()
{
	m_graceful_quit = true; 
	m_reactor.deactivate ();
}

inline void
GenServer::
set_version (const string& release_, int revision_)
{
	m_version = release_;
	m_revision = revision_;
}

inline void
GenServer::
set_author (const string& author_)
{
	m_author = author_;
}

} // end namespase ASSA

#endif /* GENSERVER_H */
