// -*- c++ -*-
//------------------------------------------------------------------------------
//                             Fork.h
//------------------------------------------------------------------------------
//  Copyright (C) 1997-2002  Vladislav Grinchenko <vladg@erols.com>
//
//  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 IS_FORK_H
#define IS_FORK_H

#include <unistd.h>		// fork
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>

#include "assa/Assert.h"
#include "assa/Singleton.h"

#include <list>
using std::list;

/* Sun Solaris 2.6 has wait4(3C) function definition missing
 * from its header files. The function, however, is in the 
 * standard library. Testing this scenario would require
 * writing custom m4 macro. 
 */
#if defined(__sun) 
#include <sys/time.h>
#include <sys/resource.h>
extern "C" pid_t 
wait4(pid_t pid, int *statusp, int options, struct rusage *rusage);
#endif

namespace ASSA {

/** @file Fork.h

    A simple wrapper around fork() library function call.

    Fork class is a simple wrapper around C library function fork().
    Main advantage of using Fork over fork() is that child termination
    process is handles internally by Fork class static destructor. 
*/

class Fork {
public:
	/* @enum state_t
	   Child completion states.
	*/
	enum state_t { 
		KILL_ON_EXIT,			/**< kill all childer on exit */
		WAIT_ON_EXIT,			/**< wait for all children to exit */
		LEAVE_ALONE				/**< ignore all running children on exit */
	};

	/** Constructor. Essentially fork the process in two.
	    @param s_ Specify wether wait for this child to exit
	              (WAIT_ON_EXIT by default), or kill it by
		      sending SIGTERM to it.
	*/
	Fork (state_t s_ = WAIT_ON_EXIT);
	
	/** Destructor. Doesn't really do anything. All children
	    will be terminated according to state set when 
	    process terminates.
	*/
	~Fork() { trace_with_mask("Fork::~Fork",FORK); }

	/** Test whether we are in parent section of the code.
	    @return true if it is parent code, false otherwise
	*/
	bool isParent() const { return m_pid ? true : false; }

	/** Test whether we are in child section of the code.
	    @return true if it is parent code, false otherwise
	*/
	bool isChild() const { return !m_pid ? true : false; }

	/** Retrieve child process id.
	    @return child pid
	*/
	pid_t getChildPID() const {
		trace_with_mask("Fork::getChildPID",FORK);
		return m_pid;
	}

	/** 
	 *  Execute an external command. 
	 *  Conveniently wraps fork()/execvp()/wait() sequence of calls.
	 *
	 *  @param cmd_ Command to execute.
	 *  @param args_ Command arguments as one string.
	 *  @param wait_for_completion_ 
	 *                If set to true, blocks until child exits;
	 *                false otherwise.
	 *
	 *  @return If wait_for_completion_ is false, returns child PID;
	 *          If wait_for_completion_ is true, returns command exit status:
	 *          0 returned means that command succeeded; 1 that it failed;
	 *          -1 that wait(2) failed (where it shouldn't).
	 */
	static int fork_exec (const string& cmd_, 
						  const string& args_,  
						  bool wait_for_completion_);
	
private:
	/// Child pid
	pid_t m_pid;
};

/**  forknode_t class.
*/
class fnode_t {
public:
	/// Constructor.
	fnode_t (pid_t pid_, Fork::state_t state_)
		: m_pid(pid_), m_state(state_)
		{
			trace_with_mask("fnode_t::fnode_t",FORK);
		}

	/// Retrieve child pid
	pid_t getPID() const
		{
			trace_with_mask("fnode_t::getPID",FORK);
			return m_pid;
		}

	/// Retrieve kill flag
	bool needKill() const
		{
			trace_with_mask("fnode_t::needKill",FORK);
			return  m_state == Fork::KILL_ON_EXIT ? true : false;
		}
private:
	/// Child pid
	pid_t m_pid;

	/// Child state {kill, wait}
	Fork::state_t m_state;
};

/** ForkList is a singleton class that keeps a list of all forked 
 *  children. Its task is on process exit, for each child forked,
 *  either terminate it with SIGTERM or wait for its exit. In any case,
 *  child's exit status is collected thus avoiding zombie processes.
*/

class ForkList : public Singleton < ForkList > 
{
public:
	/// Constructor
	ForkList () { trace_with_mask("ForkList::ForkList",FORK); }

	/// Destructor. Wipe out childer based on their state.
	~ForkList();

	/// List of children's data structures
	list< fnode_t* > m_list;
};

} // end namespace ASSA

#endif // IS_FORK_H





