From 5e1dff9dbae47f74232a9f775f799c75b4980d3a Mon Sep 17 00:00:00 2001 From: sc Date: Sat, 30 Dec 2000 22:45:48 +0000 Subject: Initial revision, created from bits of TextStream.cc --- comm/OLD/TextService.cc | 437 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 comm/OLD/TextService.cc diff --git a/comm/OLD/TextService.cc b/comm/OLD/TextService.cc new file mode 100644 index 0000000..7ba37ba --- /dev/null +++ b/comm/OLD/TextService.cc @@ -0,0 +1,437 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Text services + * + * $Id$ + * $CurLog$ + */ + +#include "TextService.h" +#include "TimeOut.h" +#include "PortServer.h" +#include "error.h" +#include +#include +#include +#include + + +//---------------- constants + +// we start with a few quick retries +// AbandonRetries doubles the time and decrement the retries until 1 +// 2 secs * 5 = 10 secs +// 4 secs * 4 = 16 secs +// 8 secs * 3 = 24 secs +// 16 secs * 2 = 32 secs +// 32 secs * 1 = 32 secs +// total = 15 retries in 114 secs ~ 2 minutes +// we want this to be significantly longer than the REG_TIME of the port server + +#define RETRY_TIME 2 +#define MAX_RETRIES 5 + +//---------------- ServiceStarter + +class UchServiceStarter : public UchBaseTimeOut { +friend class UchTextService; + +protected: + UchTextService* Service; + int Retries; + int Max; + +public: + UchServiceStarter (UchTextService*, int, int); + void Handle (Millisecond); +}; + +UchServiceStarter :: UchServiceStarter (UchTextService* s, int retry, int maxr) +: UchBaseTimeOut (*s->GetMultiplexer (), retry * 1000), + Service (s), + Retries (0), + Max (maxr) +{ +} + +void +UchServiceStarter :: Handle (Millisecond) +{ + /* UchServiceStarters are not timers triggered on signals. + For that reason, Handle is allowed to be complex. */ + + if (! Service) + return; + + fprintf (stderr, "ServiceStarter::TimeOut, attempting restart\n"); + if (Service->GetStatus () == UchTextService::isRunning + || Service->Restart () == UchTextService::isRunning) { + Stop (); + Service->Starter = 0; + delete this; + return; + } + + if (++Retries >= Max) { + Stop (); + if (Max <= 1) { + fprintf (stderr, "ServiceStarter::TimeOut, abandoning restart\n"); + Service->Starter = 0; + Service->AbandonRestart (); + delete this; + } else { + Max--; + Retries = 0; + ChangePeriod (GetPeriod () * 2); + Restart (); + } + } +} + + +/*? +Construct an empty \var{UchTextService}. +When using this constructor, the application must call \fun{Init} at a later +time in order to define the server to connect to. +The initial state of the service is \var{isLost}, since the actual +connection to the server is not established yet. +?*/ +UchTextService :: UchTextService () +: UchTextStream (), + StatusFlag (isLost), + Starter (0), + Closed (false), + User (), + Service (), + Host () +{ +} + +/*? +Construct a \var{UchTextService} connected to server \var{s} on host \var{h}. +This is equivalent to the empty constructor followed by a call to \fun{Init}. +?*/ +UchTextService :: UchTextService (UchBaseMultiplexer& mpx, const char* s, const char* h) +: UchTextStream (), + StatusFlag (isLost), + Starter (0), + Closed (false), + User (), + Service (), + Host () +{ + Init (mpx, s, h); +} + +/*? +The (protected) destructor. +Call \fun{Close} or \fun{CloseNow} to close down the connection and +delete the service. +?*/ +UchTextService :: ~UchTextService () +{ + if (Starter) + delete Starter; +} + +/*? +This protected virtual function is called by \fun{UchTextStream::HandleRead} +when an end-of-file is read or when a read failed. +It calls \var{LostServer} so that an attempt to reconnect to the server +can be made. +?*/ +void +UchTextService :: Closing (bool) +{ + fprintf (stderr, "UchTextService::Closing, lost server\n"); + StatusFlag = isLost; + LostServer (); +} + +/*? +Attempt to connect to the server. +If the address of the server cannot be retrieved from the port server, +this function sets the state of the service to \var{isUnavailable} +and returns. +If an address is obtained from the port server, this function tries to connect +to that address. +If this fails, it sets the state to \var{isError} and returns. +Otherwise, it sets the state to \var{isRunning} and calls the virtual +function \fun{GotServer}. +The service is automatically registered with the multiplexer, with mode \var{IONone} +the first time the connection is established. +?*/ +UchTextService::status +UchTextService :: Restart () +{ + UchInetAddress* addr = PortServerInquire ("portserv", Service, Host); + if (! addr) { +#if 0 + // *** horrible patch - see portcli.C. This info should be returned by PortServerInquire + extern bool PortServerInquireSysError; + if (PortServerInquireSysError) { + Log ("TSERVICE::Restart", "no agent manager"); + return StatusFlag = isError; + } +#endif + fprintf (stderr, "UchTextService::Restart, no address in port server\n"); + return StatusFlag = isUnavailable; + } + if (addr->Host () == 0 && addr->Port () == 0) { +// Log ("TSERVICE::Restart", "agent being launched by agent manager"); + return StatusFlag = isLost; + } + + ConnectTo (addr); + + bool wasopened = Fd.Opened (); + bool setup = true; + if (wasopened) { + // we are in trouble because a stream socket can be connected + // only once and a channel is immutable... + // so we have to fake a reopen + int ofd = FilDes (); + if (ofd >= 0) { + close (ofd); + int nfd = socket (ConnectedTo () ->Family (), SockType (), 0); +// fprintf (stderr, "UchTextService::Restart, reopening socket: %d -> %d\n", (void*) ofd, (void*) nfd); + if (nfd < 0) { + setup = false; + } else if (nfd != ofd) { + dup2 (nfd, ofd); + close (nfd); + } + } + } + if (setup) + setup = Setup (); + if (! wasopened) { + SetMode (IONone); + if (Mpx) + Mpx->Add (this); ///////// Mpx was initialized by Init + } + if (! setup) { + fprintf (stderr, "UchTextService::Restart, could not setup connection\n"); + return StatusFlag = isError; + } + + StatusFlag = isRunning; + if (Starter) // do not log if got server at first attempt + fprintf (stderr, "UchTextService::Restart, got server\n"); + GotServer (); + return StatusFlag; +} + +/*? +This protected function arranges for a connection to be attempted in +\var{retry_time} seconds. +If the connection cannot be established after \var{maxr} retries, +\var{retry_time} is doubled and \var{maxr} is decremented and new attempts +are made with these new values. +When the number of retries is null, then the virtual function +\fun{AbandonRestart} is called and no further attempts are made. +If \var{retry_time} is 0, any pending attempts are cancelled. +If \var{retry_time} or var{maxr} is negative, suitable default values are used. +?*/ +void +UchTextService :: AutoStart (int retry_time, int maxr) +{ + if (retry_time < 0) + retry_time = RETRY_TIME; + if (maxr < 0) + maxr = MAX_RETRIES; + + if (retry_time == 0) { // cancel auto-start + if (Starter) { + delete Starter; + Starter = 0; + } + return; + } + if (Starter) { + Starter->ChangePeriod (retry_time); + Starter->Retries = 0; + Starter->Max = maxr; + } else if (Mpx) { + Starter = new UchServiceStarter (this, retry_time, maxr); + } else + fprintf (stderr, "UchTextService::AutoStart, no multiplexer for time-out\n"); +} + +/*? +This protected virtual function is called when the connection to the server +has been broken. +By default, it resets the mode of the channel to \var{IONone} so that the +multiplexer does not attempt to read from the server. +Derived classes can redefine this function, but the redefinition should +call the default implementation. +?*/ +void +UchTextService :: LostServer () +{ + Mpx->SetMode (*this, IONone); + AutoStart (); +} + +/*? +This protected virtual function is called when the connection to the server +has been established. +By default, it sets the mode of the channel to \var{IORead} so that the +multiplexer can monitor the channel. +If the output buffer is not empty, it sends it to the server by calling +\fun{DoSend}. +Derived classes can redefine this function, but the redefinition should +call the default implementation. +?*/ +void +UchTextService :: GotServer () +{ + Mpx->SetMode (*this, IORead); + if (OutBuffer.BufLength () != 0) + Flush (); +} + +/*? +This protected virtual function is called when the connection cannot +be restarted (see \fun{AutoStart}). +By default it calls \fun{CloseNow} which should ultimately destroy the object. +A derived class can redefine this function to take whatever clean-up action +is required. +?*/ +void +UchTextService :: AbandonRestart () +{ + CloseNow (); +} + +/*? +This implementation of the protected virtual function \fun{UchTextStream::Execute} +always return \var{isCmdOk}. +This is suitable for the global function \var{TellServer}. +Derived classes generally do redefine this function. +?*/ +UchTextStream::cmd_res +UchTextService :: Execute (const UchTextLine&) +{ + // just discard received request by default + // (suitable for TellServer below) + return isCmdOk; +} + +/*? +This implementation of the protected virtual function \fun{UchBufStream::Flush} +writes the output buffer only if the state of the connection is +\var{isRunning}. +Otherwise, the outgoing requests accumulate in the ouput buffer until the +connection is established. +This function also closes down the connection by calling \var{MpxRemoveChannel} +if \fun{Close} has been called before the connection ws actually established. +?*/ +void +UchTextService :: Flush () +{ + if (StatusFlag == isRunning) { + Write (OutBuffer); + if (Closed) + Mpx->Remove (*this); + } +} + +/*? +Define the server and host to connect to. This function must be used +if the object was constructed with the default constructor. +If the connection to the server cannot be established, \fun{Init} calls +\var{AutoStart} to attempt the connection later. +?*/ +void +UchTextService :: Init (UchBaseMultiplexer& mpx, const char* s, const char* h) +{ + if (StatusFlag == isRunning) + return; + + Service = s; + Host = h; + Mpx = &mpx; + + StatusFlag = Restart (); + if (StatusFlag == isError) { + // try to run the agent manager + if (fork () != 0) { + // we are the parent process, and we become the agent manager + // *** how to have the agent manager survive the client ??? + ::Error (ErrLog, "TSERVICE::Init", "trying to run agentman"); + if (execlp ("agentman", "agentman", 0) != 0) + ::Error (ErrWarn, "TSERVICE::Init", "could not run agentman"); + } + } + // delay retry + if (StatusFlag != isRunning) + AutoStart (); +} + +/*? +Return the current retry time for the auto-restart of the connection. +If the connection is not in auto-restart mode, return -1; +?*/ +int +UchTextService :: GetRetryTime () +{ + return Starter ? Starter->GetPeriod () / 1000 : -1; +} + +/*? +Return the current maximum number of retries for the auto-restart of the connection. +If the connection is not in auto-restart mode, return -1; +?*/ +int +UchTextService :: GetMaxRetries () +{ + return Starter ? Starter->Max : -1; +} + +/*? +Remove this service from the multiplexer if the connection is established +or if the output buffer is empty. +Otherwise, mark the connection as being closed; +in this case, the output buffer will be sent when the connection is etablished, +and then the connection will be closed. +?*/ +void +UchTextService :: Close () +{ + Closed = true; + if (StatusFlag == isRunning || OutBuffer.BufLength () == 0) + Mpx->Remove (*this); +} + +/*? +Remove this service from the multiplexer, whatever +the state of the connection is. +?*/ +void +UchTextService :: CloseNow () +{ + Mpx->Remove (*this); +} + +#if 0 +/*? +This global function provides the simplest interface to send a request \var{req} +to a server \var{sname} on host \var{host}. +?*/ +bool +TellServer (const char* sname, const char* host, const char* req) +{ + UchTextService* serv = new UchTextService; + serv->Init (sname, host); + serv->Send (req); + serv->Close (); + return true; +} +#endif -- cgit v1.1