// -*- c++ -*- //------------------------------------------------------------------------------ // Fork.h //------------------------------------------------------------------------------ // Copyright (C) 1997-2002,2005 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 IS_FORK_H #define IS_FORK_H #include // fork #include #include #include #include #ifndef WIN32 # include #endif #include 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 #include extern "C" pid_t wait4(pid_t pid, int *statusp, int options, struct rusage *rusage); #endif #include "assa/Assure.h" #include "assa/Singleton.h" #include "assa/EventHandler.h" #include "assa/SigHandler.h" #include "assa/SigAction.h" namespace ASSA { /** @file Fork.h @brief A simple wrapper around fork() library function call. */ /** @class ChildStatusHandler @brief A helper class of Fork */ class ChildStatusHandler : public EventHandler { public: ChildStatusHandler () : m_exit_status (-1), m_caught (false) { /* no-op */ } int handle_signal (int signum_); /** @return -1 if failed on wait(); otherwise an exit status of the child process as returned by either RETURN or EXIT */ int exit_status () const { return m_exit_status; } /** @return true if the signal has been caught; false otherwise */ bool caught () const { return m_caught; } private: int m_exit_status; bool m_caught; }; /** @class Fork @brief 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 */ }; /** @enum wait4status_t */ enum wait4status_t { IGNORE_STATUS, /**< Don't wait for child to complete */ COLLECT_STATUS /**< Wait for child to complete and collect its exit status */ }; #if !defined(WIN32) /** * Fork the current process in two immediately. * * @param exit_action_ Specify (default=WAIT_ON_EXIT) whether to wait * for the child to finish or kill it with SIGTERM * on process exit. * * @param catch_status_ If true (default=COLLECT_STATUS), pause for the * child to exit and collect its exit status. */ Fork (state_t exit_action_ = WAIT_ON_EXIT, wait4status_t catch_status_ = COLLECT_STATUS); /** * 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; } /** * Retrieve exit status of a child process if the constructor's * parameter catch_status_ was set to TRUE. */ int get_exit_status () const { return m_chstath.exit_status (); } #endif // !defined(WIN32) /** * 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. * @param ignore_output_ Discard child's output to stdout/stderr. * * @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_, wait4status_t wait_for_completion_, bool ignore_output_ = false); #if !defined(WIN32) private: /// Child pid pid_t m_pid; /// Local signal handler SigHandler m_local_sh; /// Handler to catch Child's status ChildStatusHandler m_chstath; /// Old signal disposition SigAction m_old_disp; #endif // !defined(WIN32) }; /** 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