home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1999 mARCH / PCWK3A99.iso / Linux / DDD331 / DDD-3_1_.000 / DDD-3_1_ / ddd-3.1.1 / ddd / Agent.h < prev    next >
C/C++ Source or Header  |  1998-08-21  |  13KB  |  442 lines

  1. // $Id: Agent.h,v 1.19 1998/08/21 10:21:48 zeller Exp $
  2. // Three-Channel Agent Interface 
  3.  
  4. // Copyright (C) 1995 Technische Universitaet Braunschweig, Germany.
  5. // Written by Andreas Zeller <zeller@ips.cs.tu-bs.de>.
  6. // 
  7. // This file is part of DDD.
  8. // 
  9. // DDD is free software; you can redistribute it and/or
  10. // modify it under the terms of the GNU General Public
  11. // License as published by the Free Software Foundation; either
  12. // version 2 of the License, or (at your option) any later version.
  13. // 
  14. // DDD is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. // See the GNU General Public License for more details.
  18. // 
  19. // You should have received a copy of the GNU General Public
  20. // License along with DDD -- see the file COPYING.
  21. // If not, write to the Free Software Foundation, Inc.,
  22. // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. // 
  24. // DDD is the data display debugger.
  25. // For details, see the DDD World-Wide-Web page, 
  26. // `http://www.cs.tu-bs.de/softech/ddd/',
  27. // or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
  28.  
  29. #ifndef _DDD_Agent_h
  30. #define _DDD_Agent_h
  31.  
  32. #ifdef __GNUG__
  33. #pragma interface
  34. #endif
  35.  
  36.  
  37. /*
  38.     Agent(p) opens a three-channel communication interface to a process p
  39.     (called an "agent"). The calling process can write to the agent's stdin
  40.     and can read from the agent's stdout and stderr.
  41.  
  42.     The process is started using start(). The pipe is closed
  43.     using shutdown(), sending an EOF to the agent's stdin, on
  44.     which the agent should terminate. shutdown() does not wait
  45.     for the agent to terminate.
  46.  
  47.     Upon destruction ~Agent() or terminate(), the pipe is closed;
  48.     the calling process waits for the agent to terminate.
  49.     If it agent has not terminated after hangupTimeOut (default: 5) seconds,
  50.     the agent is sent a SIGHUP signal.
  51.     If it has not terminated after terminateTimeOut (default: 10) seconds,
  52.     the agent is sent a SIGTERM signal.
  53.     If it has not terminated after killTimeOut (default: 15) seconds,
  54.     the agent is sent a SIGKILL signal.
  55.     Signals are sent only if the appropriate timeout is >= 0.
  56.  
  57.     Each Agent can be associated with handlers for these events:
  58.     Agent::Panic    -- a serious I/O error occured
  59.     Agent::Started  -- the agent was started (is now running)
  60.     Agent::Stopped  -- the agent was stopped (is no more running)
  61.     Agent::InputEOF -- EOF on input channel detected
  62.     Agent::ErrorEOF -- EOF on error channel detected
  63.     Agent::Died     -- the agent died
  64.     Agent::_Died    -- the agent has a new status (called asynchronously)
  65.  
  66.     A handler takes the form
  67.     void handler(Agent *source, void *client_data, void *call_data)
  68.     where source is the agent causing the event and client_data is arbitrary
  69.     data passed to addHandler() when the handler was added.
  70.     In Agent::Panic, char *s = (char *)call_data is the I/O error message,
  71.     In Agent::Died, char *s = (char *)call_data is the reason for the 
  72.     agent to die (in plain text). lastStatus() gives the status itself.
  73.     In Agent::_Died, int status = (int)call_data is the new agent state.
  74.  
  75.     Note that Agent::_Died is invoked asynchronously from signal();
  76.     thus the event handlers must not call any non-reentrant routines.
  77.  
  78.     For Agent writers, an alternative interface is provided with the
  79.     constructor Agent(FILE *in = stdin, FILE *out = stdout).
  80.     No process is started and no pipe is created; instead, 
  81.     the interface listens to <in> as it would to the stdandard
  82.     output of a child process and calls appropriate handlers.
  83.     <out> defaults to stdout; output via this interface is also
  84.     logged using the standard handlers. The default parameters are chosen
  85.     so that they need not be specified by standard agents.
  86.  
  87.     For Client writers, a third interface is provided with the
  88.     constructor Agent(bool dummy). This is used for in-process 
  89.     communication. No inter-process communication ever takes place.
  90. */
  91.  
  92. #include "config.h"
  93.  
  94. #include <stdio.h>
  95. #include <signal.h>
  96. #include <sys/types.h>
  97. #include <unistd.h>
  98. #include <iostream.h>
  99. #include <strstream.h>
  100. #include <errno.h>
  101. #include <string.h>
  102.  
  103. #if !HAVE_STRERROR_DECL
  104. extern "C" char *strerror(int errno);
  105. #endif
  106.  
  107. #include "strclass.h"
  108. #include "bool.h"
  109. #include "AgentM.h"
  110. #include "HandlerL.h"
  111. #include "UniqueId.h"
  112. #include "SignalB.h"
  113. #include "TypeInfo.h"
  114.  
  115.  
  116. typedef void (*AgentHandlerProc)(class Agent *source, void *client_data,
  117.     void *call_data);
  118.  
  119.  
  120. // Event types
  121. const unsigned Panic    = 0;            // I/O error occurred
  122. const unsigned Strange  = Panic + 1;    // Strange I/O condition occurred
  123. const unsigned Started  = Strange + 1;  // Process started
  124. const unsigned Stopped  = Started + 1;  // Process stopped
  125. const unsigned InputEOF = Stopped + 1;  // EOF on input detected
  126. const unsigned ErrorEOF = InputEOF + 1; // EOF on error detected
  127. const unsigned Died     = ErrorEOF + 1; // Process died
  128. const unsigned _Died    = Died + 1;     // New Status (async call)
  129.  
  130. const unsigned Agent_NTypes = _Died + 1;    // number of events
  131.  
  132. const int READ = 0;        // constants for pipe indexing
  133. const int WRITE = 1;
  134.  
  135. class Agent {
  136.     friend class AgentManager;
  137.  
  138. public:
  139.     DECLARE_TYPE_INFO
  140.  
  141. private:
  142.     pid_t _pid;                // process id (0: not running, <0: no process)
  143.  
  144. protected:
  145.     FILE *_inputfp;            // read from child
  146.     FILE *_outputfp;            // write to child
  147.     FILE *_errorfp;            // read errors from child
  148.  
  149. private:
  150.     bool _running;              // flag: is child still running?
  151.     bool _beingTerminated;      // flag: is child just being terminated?
  152.  
  153.     int _lastStatus;            // Status after exiting
  154.  
  155.     int _terminateTimeOut;     // terminate TimeOut (in s)
  156.     int _hangupTimeOut;        // hangup TimeOut (in s)
  157.     int _killTimeOut;          // kill TimeOut (in s)
  158.  
  159. protected:
  160.     HandlerList handlers;       // error handlers
  161.  
  162. private:
  163.     int to_child[2];        // pipes
  164.     int to_parent[2];
  165.     int to_parent_error[2];
  166.  
  167.     Agent *next;            // used in agent manager
  168.  
  169.     Agent& operator = (const Agent&)  { assert(0); return *this; }
  170.  
  171. protected:
  172.     // call Handlers
  173.     void callHandlers(int type, void *call_data = 0)
  174.     {
  175.     handlers.call(type, this, call_data);
  176.     }
  177.  
  178.     // dito, but delayed
  179.     virtual void callHandlersWhenIdle(int type, void *call_data = 0)
  180.     {
  181.     callHandlers(type, call_data);
  182.     }
  183.  
  184.  
  185. private:
  186.     // check if agent is dead
  187.     void checkIfDead();
  188.  
  189.     // set new states for _running
  190.     void setRunning();
  191.     void unsetRunning();
  192.  
  193.     // start new process
  194.     void startChildProcess();
  195.  
  196.     // Handle status change
  197.     static void childStatusChange(int sig);
  198.  
  199.     // generic error report function
  200.     void _raise(string msg, 
  201.         int handler, 
  202.         bool system_error, 
  203.         bool check_if_running)
  204.     {
  205.     if (system_error)
  206.         msg += string(": ") + strerror(errno);
  207.     char *s = msg;
  208.  
  209.     callHandlers(handler, (void *)s);
  210.  
  211.     if (check_if_running)
  212.         (void)running();
  213.     }
  214.  
  215. protected:
  216.     string _path;                   // process path
  217.  
  218.     // custom error report functions
  219.     // Msg invokes error handler, Warning invokes warning handler
  220.     // IO means include system error, _ means don't check if running.
  221.  
  222.     void _raiseMsg(string msg)        { _raise(msg, Panic,   false, false); }
  223.     void _raiseWarning(string msg)    { _raise(msg, Strange, false, false); }
  224.     void _raiseIOMsg(string msg)      { _raise(msg, Panic,   true,  false); }
  225.     void _raiseIOWarning(string msg)  { _raise(msg, Strange, true,  false); }
  226.     void raiseMsg(string msg)         { _raise(msg, Panic,   false, true); }
  227.     void raiseWarning(string msg)     { _raise(msg, Strange, false, true); }
  228.     void raiseIOMsg(string msg)       { _raise(msg, Panic,   true,  true); }
  229.     void raiseIOWarning(string msg)   { _raise(msg, Strange, true,  true); }
  230.  
  231.     // Terminator
  232.     virtual void waitToTerminate();    // wait until agent dead
  233.  
  234.     // Terminator 2
  235.     virtual void abort();        // inhibit further communication
  236.  
  237.     // default handler
  238.     static void defaultHandler(Agent *source, 
  239.                    void *client_data, 
  240.                    void *call_data);
  241.     void addDefaultHandler(unsigned type);
  242.  
  243.     // restore I/O for parent
  244.     virtual void restoreParentIO();
  245.  
  246.     // setup I/O
  247.     virtual void activateIO()   {}
  248.     virtual void deactivateIO() {}
  249.  
  250.     // hooks for alternative communication schemes
  251.     virtual int setupCommunication();
  252.     virtual int setupChildCommunication();
  253.     virtual int setupParentCommunication();
  254.  
  255.     // hook for alternative child execution (never return)
  256.     virtual void executeChild();
  257.  
  258.     // hook for channel closing
  259.     virtual void closeChannel(FILE *fp);
  260.  
  261.     // EOF handlers
  262.     virtual void inputEOF();    // EOF on input detected
  263.     virtual void errorEOF();    // EOF on error detected
  264.  
  265.     // Check if we're on a terminal
  266.     bool inputIsTerminal() 
  267.     {
  268.     return inputfp() != 0 && isatty(fileno(inputfp()));
  269.     }    
  270.     bool outputIsTerminal() 
  271.     {
  272.     return outputfp() != 0 && isatty(fileno(outputfp()));
  273.     }
  274.     bool onTerminal()
  275.     {
  276.     return inputIsTerminal() && outputIsTerminal();
  277.     }
  278.  
  279. public:
  280.     // all running agents
  281.     static AgentManager runningAgents;
  282.  
  283.     // Constructor for Agent users
  284.     Agent(const string& pth, unsigned nTypes = Agent_NTypes):
  285.     _pid(0), _inputfp(0), _outputfp(0), _errorfp(0),
  286.     _running(false), _beingTerminated(false), _lastStatus(-1),
  287.     _terminateTimeOut(10), _hangupTimeOut(5), _killTimeOut(15),
  288.     handlers(nTypes), next(0), _path(pth)
  289.     {
  290.     addDefaultHandler(Panic);    // report errors
  291.     addDefaultHandler(Strange);  // report warnings
  292.     addDefaultHandler(Died);     // report death
  293.     }
  294.  
  295.     // Constructor for Agent writers
  296.     Agent(FILE *in = stdin, FILE *out = stdout, FILE *err = 0,
  297.     unsigned nTypes = Agent_NTypes):
  298.     _pid(-pid_t((unsigned long)UniqueId())), _inputfp(in), _outputfp(out),
  299.     _errorfp(err), _running(false), _beingTerminated(false),
  300.     _lastStatus(-1), _terminateTimeOut(-1), _hangupTimeOut(-1), 
  301.     _killTimeOut(-1), handlers(nTypes), next(0), _path("this")
  302.     {
  303.     addDefaultHandler(Panic);    // report errors only
  304.     }
  305.  
  306.     // "Dummy" Constructor without any communication
  307.     Agent(bool, unsigned nTypes = Agent_NTypes):
  308.     _pid(-pid_t((unsigned long)UniqueId())), _inputfp(0), _outputfp(0),
  309.     _errorfp(0), _running(false), _beingTerminated(false),
  310.     _lastStatus(-1), _terminateTimeOut(-1), _hangupTimeOut(-1),
  311.     _killTimeOut(-1), handlers(nTypes), next(0), _path("this")
  312.     {
  313.     // no need to report anything
  314.     }
  315.  
  316.     // Duplicator
  317.     Agent(const Agent& c);
  318.     virtual Agent *dup() const { return new Agent(*this); }
  319.  
  320.     // Destructor
  321.     virtual ~Agent();
  322.  
  323.     // Start agent
  324.     virtual void start();
  325.  
  326.     // File access
  327.     FILE *inputfp()  const { return _inputfp;  }
  328.     FILE *outputfp() const { return _outputfp; }
  329.     FILE *errorfp()  const { return _errorfp;  }
  330.  
  331.     // Shutdown agent (sending EOF)
  332.     virtual void shutdown();
  333.  
  334.     // Wait until terminated
  335.     virtual void wait();
  336.  
  337.     // High-Level Terminator (using signals)
  338.     virtual void terminate(bool onExit = false);
  339.  
  340.     // Low-Level Terminators
  341.     void _kill(int sig = SIGKILL);       // send signal
  342.     void _hangup()    { _kill(SIGHUP); }   // send Hangup signal
  343.     void _terminate() { _kill(SIGTERM); }  // send Terminate signal
  344.  
  345.     // Hook to be called when handler list has changed
  346.     virtual void handlerChange() {}
  347.  
  348.     // Add event handler
  349.     void addHandler(unsigned type, 
  350.                 AgentHandlerProc proc,
  351.                 void *client_data = 0)
  352.     {
  353.     SignalBlocker sb(SIGCHLD);
  354.  
  355.     handlers.add(type, (HandlerProc)proc, client_data);
  356.     handlerChange();
  357.     }
  358.     void addHandler(const Agent& a)
  359.     {
  360.     SignalBlocker sb(SIGCHLD);
  361.  
  362.     handlers.add(a.handlers);
  363.     handlerChange();
  364.     }
  365.  
  366.     // Remove event handler
  367.     void removeHandler(unsigned type, AgentHandlerProc proc,
  368.                void *client_data = 0)
  369.     {
  370.     SignalBlocker sb(SIGCHLD);
  371.  
  372.     handlers.remove(type, (HandlerProc)proc, client_data);
  373.     handlerChange();
  374.     }
  375.  
  376.     // Remove all event handlers
  377.     void removeAllHandlers(unsigned type)
  378.     {
  379.     SignalBlocker sb(SIGCHLD);
  380.  
  381.     handlers.removeAll(type);
  382.     handlerChange();
  383.     }
  384.  
  385.     // Remove all handlers for all event types
  386.     void removeAllHandlers()
  387.     {
  388.     SignalBlocker sb(SIGCHLD);
  389.  
  390.     handlers.removeAll();
  391.     handlerChange();
  392.     }
  393.  
  394.     // Check if we have a handler
  395.     bool hasHandler(unsigned type) const
  396.     {
  397.     return handlers.has(type);
  398.     }
  399.  
  400.     // inform agent about new status
  401.     void hasNewStatus(int status);
  402.  
  403.     // Resources
  404.     string path() const         { return _path;    }
  405.     pid_t pid()    const         { return _pid;     }
  406.     int& terminateTimeOut()     { return _terminateTimeOut; }
  407.     int& hangupTimeOut()     { return _hangupTimeOut; }
  408.     int& killTimeOut()         { return _killTimeOut; }
  409.     int terminateTimeOut() const { return _terminateTimeOut; }
  410.     int hangupTimeOut()    const    { return _hangupTimeOut; }
  411.     int killTimeOut()    const     { return _killTimeOut; }
  412.     bool beingTerminated() const { return _beingTerminated; }
  413.     int lastStatus() const     { return _lastStatus; }
  414.  
  415.     string name() const
  416.     {
  417.     ostrstream os;
  418.     os << path();
  419.     if (pid() > 0)
  420.         os << " [" << pid() << "]";
  421.     return string(os);
  422.     }
  423.  
  424.     // Check if still running
  425.     bool running();
  426.  
  427.     // Commit pending changes
  428.     virtual void commit()       
  429.     {
  430.     // default: do nothing
  431.     }
  432.  
  433.     // Debugging
  434.     virtual bool OK() const
  435.     {
  436.     return true;
  437.     }
  438. };
  439.  
  440. #endif // _DDD_Agent_h
  441. // DON'T ADD ANYTHING BEHIND THIS #endif
  442.