From ba066c34dde204aa192d03a23a81356374d93731 Mon Sep 17 00:00:00 2001 From: chatty Date: Wed, 7 Apr 1993 11:50:31 +0000 Subject: Initial revision --- comm/OLD/Agent.cc | 386 +++++++++++++++++ comm/OLD/Agent.h | 82 ++++ comm/OLD/Event.cc | 121 ++++++ comm/OLD/Event.h | 92 ++++ comm/OLD/PortServer.cc | 410 ++++++++++++++++++ comm/OLD/PortServer.h | 48 +++ comm/OLD/PortServerReq.cc | 60 +++ comm/OLD/PortServerReq.h | 45 ++ comm/OLD/ReqMgr.cc | 184 ++++++++ comm/OLD/ReqMgr.h | 81 ++++ comm/OLD/ReqMgr.l | 95 +++++ comm/OLD/ReqMgr.y | 285 +++++++++++++ comm/OLD/Server.cc | 395 +++++++++++++++++ comm/OLD/Server.h | 77 ++++ comm/OLD/Service.cc | 328 +++++++++++++++ comm/OLD/Service.h | 100 +++++ comm/OLD/SimpleMessage.cc | 47 +++ comm/OLD/SimpleMessage.h | 23 + comm/OLD/TextStream.cc | 1030 +++++++++++++++++++++++++++++++++++++++++++++ comm/OLD/TextStream.h | 157 +++++++ comm/OLD/dgram.cc | 717 +++++++++++++++++++++++++++++++ comm/OLD/dgram.h | 102 +++++ comm/OLD/portserv.cc | 653 ++++++++++++++++++++++++++++ comm/OLD/porttest.cc | 72 ++++ comm/OLD/reqgen.cc | 34 ++ 25 files changed, 5624 insertions(+) create mode 100644 comm/OLD/Agent.cc create mode 100644 comm/OLD/Agent.h create mode 100644 comm/OLD/Event.cc create mode 100644 comm/OLD/Event.h create mode 100644 comm/OLD/PortServer.cc create mode 100644 comm/OLD/PortServer.h create mode 100644 comm/OLD/PortServerReq.cc create mode 100644 comm/OLD/PortServerReq.h create mode 100644 comm/OLD/ReqMgr.cc create mode 100644 comm/OLD/ReqMgr.h create mode 100644 comm/OLD/ReqMgr.l create mode 100644 comm/OLD/ReqMgr.y create mode 100644 comm/OLD/Server.cc create mode 100644 comm/OLD/Server.h create mode 100644 comm/OLD/Service.cc create mode 100644 comm/OLD/Service.h create mode 100644 comm/OLD/SimpleMessage.cc create mode 100644 comm/OLD/SimpleMessage.h create mode 100644 comm/OLD/TextStream.cc create mode 100644 comm/OLD/TextStream.h create mode 100644 comm/OLD/dgram.cc create mode 100644 comm/OLD/dgram.h create mode 100644 comm/OLD/portserv.cc create mode 100644 comm/OLD/porttest.cc create mode 100644 comm/OLD/reqgen.cc (limited to 'comm/OLD') diff --git a/comm/OLD/Agent.cc b/comm/OLD/Agent.cc new file mode 100644 index 0000000..3e4bf8a --- /dev/null +++ b/comm/OLD/Agent.cc @@ -0,0 +1,386 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Agents, by Stephane Chatty + * + * $Id$ + * $CurLog$ + */ + +#include "Agent.h" + +/*?class UchAgent +The class \typ{UchAgent} derives from \typ{UchStream}. +It is associated to a channel set to monitor the connected agents in parallel. +Whenever a new agent connects, the virtual function \fun{HandleNew} is called. +This function must return a pointer to an object of class \typ{UchRemoteAgent}. + +Because the agents may be on different machines with different architectures, +the byte swapping is always done by the ``server'' (ie. the agent that did not initiate +the communication), by convention. (NOTE: byte swapping is not currently implemented) +?*/ + +/*?class UchRemoteAgent +Class \typ{UchRemoteAgent} derives from \typ{UchMsgStream}. +It is still an abstract class because the virtual functions \fun{NewMessage} and \fun{ConvertAnswer} +of \typ{UchMsgStream} are not defined in \typ{UchRemoteAgent}. +Instances of \fun{UchRemoteAgent} only know that they may belong to an instance of \typ{UchAgent}. +Thus the virtual function \fun{Delete} is redefined to achieve a clean removal from the server's list. +?*/ + +/*? +Construct an empty server port. +?*/ +UchAgent :: UchAgent () +: UchStream (), + RemoteAgents (), + ChanSet (0) +{ +} + +/*?nodoc?*/ +UchAgent :: UchAgent (const UchAgent& sp) +: UchStream (sp), + RemoteAgents (), + ChanSet (0) +{ +} + +/*? +Construct a server port bound to address \var{a}. +When using Internet domain addresses, \var{a} should refer to +the wildcard address so that clients can connect from any machine. +This can be done with the instruction \com{new UchInetAddress(ANYADDR)}. +?*/ +UchAgent :: UchAgent (UchAddress* a) +: UchStream (a, 0), + RemoteAgents (), + ChanSet (0) +{ +} + +/*?nodoc?*/ +UchAgent :: ~UchAgent () +{ + UchRemoteAgent* cl = 0; + + while (cl = RemoteAgents.First ()) + cl->Delete (); + ChanSet = 0; // will delete it if necessary +} + +/*?nodoc?*/ +UchChannel* +UchAgent :: Copy () const +{ + return new UchAgent (*this); +} + +/*? +Remove a client from the set of clients connected to the server. +This is normally done automatically whenever the client disconnects itself, +as a side effect of destroying the client. +?*/ +void +UchAgent :: RemoveRemoteAgent (UchRemoteAgent* cl) +{ + if (RemoteAgents.Remove (cl)) { + cl->MyLocalAgent = 0; + HandleRemove (cl); + } else + Error (ErrWarn, "UchAgent::RemoveRemoteAgent", "not owned by this server"); +} + +/*? +Bind the socket and listen to it. +Initialize the channel set of the server to \var{cs}, or create a new one if \var{cs} is 0. +The channel set is used by the server to read and process messages from its clients: +whenever a client connects to the server, an instance of \typ{UchRemoteAgent} is added to the channel set. +Whenever data is readable from a client, it is read in an input buffer and processed as soon +as a full message is received. +Whenever a client closes the connection, it is removed from the channel set of the server. +This function returns TRUE if all went well, else it calls \fun{SysError} and returns FALSE. +You may want to pass your own channel set if you are already multiplexing +i/o on that channel set. For instance, you may have several \typ{UchAgent} objects +in your application. +A smart pointer to the channel set is actually kept in the server, so that it can be shared +securely. +?*/ +bool +UchAgent :: Setup (UchMultiplexer* cs) +{ + if (Listen () < 0) { + SysError (ErrWarn, "UchAgent::Setup"); + return FALSE; + } + SetMode (IORead); + if (!cs) + cs = new UchMultiplexer; + ChanSet = cs; + ChanSet->Add (this); + + return TRUE; +} + +/*? +This function removes the agent's registration channel from its channel set. +This has the effect of ignoring any incoming connections. +This function will also make \fun{Run} exit if there is no other +channel in the channel set. +Unless you added your own channels to this channel set, +this will be the case if there is no remote agent currently connected. +This is the only way to exit properly from \fun{Run}. +?*/ +void +UchAgent :: Unlisten () +{ + if (ChanSet) + ChanSet->Remove (*this); +} + + +/*?nodoc?*/ +void +UchAgent :: HandleRead () +{ +// cannot redefine Accept to return UchRemoteAgent*, so this does not work : +// UchRemoteAgent* cl = Accept (); +// need to delete the channel created by Accept, so this does not work either : +// UchRemoteAgent* cl = new UchRemoteAgent (Accept ()); +// so we do this (less elegant ...) : + UchChannel* ch = Accept (); + if (! ch) { + SysError (ErrWarn, "UchAgent::HandleRead: Accept"); + return; + } + UchRemoteAgent* ra = new UchRemoteAgent (this, ch); + delete ch; + + ra->SetMode (IOReadSelect); + RemoteAgents.Append (ra); + if (ChanSet) + ChanSet->Add (ra); +} + +/*? +This virtual function is called whenever a new agent connects to this one. +?*/ +void +UchAgent :: HandleNew (UchRemoteAgent*) +{ +} + +/*? +This virtual function is called whenever a remote agent is removed (\fun{RemoveRemoteAgent}). +It is a hook for the application to take whatever action; the default action is to do nothing. +When this function is called, the client is already removed from the client list of the server. +?*/ +void +UchAgent :: HandleRemove (UchRemoteAgent*) +{ +} + +// default error functions +// +/*?nextdoc?*/ +bool +UchAgent :: SysError (errtype how, const char* who, int excl1, int excl2) +{ + return ::SysError (how, who, excl1, excl2); +} + +/*? +Each server port class can have its own error handling routines. +Their default action is to call the global functions \fun{Error} and \fun{SysError}. +They can be called from the following functions: +\fun{Error} can be called from +\fun{RemoveRemoteAgent} when the client does not belong to this server; +\fun{SysError} can be called from +\fun{Setup} when the socket could not be setup correctly, +and from \fun{HandleRead} +when the connection could not be accepted. +?*/ +void +UchAgent :: Error (errtype how, const char* who, const char* what) +{ + ::Error (how, who, what); +} + +/*? +Setup the server if necessary, then scan and process its channel set +by calling \fun{LoopScan} for it. +To have a server active, you need at least to initialize it (and thus know its address), +and then run it. +If you have added your own channels to the channel set of the server, +their \fun{HandleRead} and \fun{HandleWrite} functions will be called normally by \fun{Run}. +?*/ +void +UchAgent :: Run () +{ + if (! ChanSet) + if (! Setup ()) + Error (ErrFatal, "UchAgent::Run", "could not setup"); + if (ChanSet) + ChanSet->LoopScan (); +} + +/*? +Send a message to all the agents currently connected to this one. +If \var{flush} is TRUE, the output buffer of each remote agent is flushed. +?*/ +void +UchAgent :: Broadcast (UchMessage& msg, bool flush) +{ + CcuListIterOf li (RemoteAgents); + while (++li) + (*li)->Send (msg, flush); +} + +/*? +Send a message to all the agents currently connected to this one, except \var{exclude}. +If \var{flush} is TRUE, the output buffer of each client is flushed. +?*/ +void +UchAgent :: Broadcast (UchMessage& msg, UchRemoteAgent* excl, bool flush) +{ + CcuListIterOf li (RemoteAgents); + while (++li) + if (*li != excl) + (*li)->Send (msg, flush); +} + +/*?nextdoc?*/ +void +UchAgent :: Broadcast (UchMsgBuffer& buf, bool flush) +{ + CcuListIterOf li (RemoteAgents); + while (++li) + (*li)->Send (buf, flush); +} + +/*? +These functions are similar to the previous ones, except that they take +a buffer instead of a message. The buffer {\em must} contain a converted message. +It is more efficient to broadcast a buffer than a message because there is less +message conversion overhead. +?*/ +void +UchAgent :: Broadcast (UchMsgBuffer& buf, UchRemoteAgent* excl, bool flush) +{ + CcuListIterOf li (RemoteAgents); + while (++li) + if (*li != excl) + (*li)->Send (buf, flush); +} + +#ifdef DOC +//fake entries for inline functions + +/*? +Return the channel set used by the server. +This is useful if you want to add your own channels to the channel set. +?*/ +UchMultiplexer* +UchAgent :: GetChanSet () +{ +} +#endif + +UchRemoteAgent* +UchAgent :: Contact (UchAddress* a) +{ + if (!a) + return 0; + UchRemoteAgent* ra = new UchRemoteAgent (this, a); + if (ra) + ChanSet->Add (ra); + return ra; +} + +/*?nodoc?*/ +UchRemoteAgent :: UchRemoteAgent (UchAgent* a) +: UchMsgStream (), + MyLocalAgent (a) +{ +} + +/*?nodoc?*/ +UchRemoteAgent :: UchRemoteAgent (const UchRemoteAgent& cl) +: UchMsgStream (cl), + MyLocalAgent (cl.MyLocalAgent) +{ +} + +/*? +Construct a new agent connected to this one on channel \var{ch}. +?*/ +UchRemoteAgent :: UchRemoteAgent (UchAgent* a, UchChannel* ch) +: UchMsgStream (), + MyLocalAgent (a) +{ + UchChannel::Open (ch->FilDes ()); +} + +UchRemoteAgent :: UchRemoteAgent (UchAgent* a, UchAddress* addr) +: UchMsgStream (addr, 0), + MyLocalAgent (a) +{ +} + + +/*?nodoc?*/ +UchRemoteAgent :: ~UchRemoteAgent () +{ + if (MyLocalAgent) { + UchAgent* s = MyLocalAgent; + s->Error (ErrWarn, "~UchRemoteAgent", "remote agent still connected; deleting anyway ...\n"); + s->RemoveRemoteAgent (this); + if (s->ChanSet) + s->ChanSet->Remove (*this); // calls the destructor if no more refs + } +} + +/*?nodoc?*/ +UchChannel* +UchRemoteAgent :: Copy () const +{ + return new UchRemoteAgent (*this); +} + +/*? +This function must be used to delete a remote agent explicitly. +It is not safe to use the operator delete. +This function is called when an end of file is read from the client; +this means that you usually do not need to call it. +?*/ +void +UchRemoteAgent :: Delete () +{ + if (MyLocalAgent) { + UchAgent* s = MyLocalAgent; + s->RemoveRemoteAgent (this); + if (s->ChanSet) + s->ChanSet->Remove (*this); // calls the destructor if no more refs + } +} + + + +#ifdef DOC +//fake entries for inline functions + +/*? +Return the server corresponding to a given client. +?*/ +UchAgent* +UchRemoteAgent :: GetAgent () +{ +} + +#endif /* DOC */ + diff --git a/comm/OLD/Agent.h b/comm/OLD/Agent.h new file mode 100644 index 0000000..e7db419 --- /dev/null +++ b/comm/OLD/Agent.h @@ -0,0 +1,82 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Agents, by Stephane Chatty + * + * $Id$ + * $CurLog$ + */ + +#ifndef Agent_H_ +#define Agent_H_ + +#include "ccu/List.h" +#include "MsgStream.h" +#include "Multiplexer.h" +#include "error.h" + +class UchRemoteAgent; + +class UchAgent : public UchStream { +friend class UchRemoteAgent; + +protected: + CcuListOf RemoteAgents; + pUchMultiplexer ChanSet; + + void HandleRead (); + +public: + UchAgent (); + UchAgent (const UchAgent&); + UchAgent (UchAddress*); + ~UchAgent (); + + UchChannel* Copy () const; + + bool Setup (UchMultiplexer* cs = 0); +inline UchMultiplexer* GetMultiplexer () { return ChanSet; } + + UchRemoteAgent* Contact (UchAddress*); + void RemoveRemoteAgent (UchRemoteAgent*); + void Run (); + void Unlisten (); + + void Broadcast (UchMessage&, bool = FALSE); + void Broadcast (UchMessage&, UchRemoteAgent*, bool = FALSE); + void Broadcast (UchMsgBuffer&, bool = FALSE); + void Broadcast (UchMsgBuffer&, UchRemoteAgent*, bool = FALSE); + +virtual void HandleNew (UchRemoteAgent*); +virtual void HandleRemove (UchRemoteAgent*); +virtual bool SysError (errtype, const char*, int = 0, int = 0); +virtual void Error (errtype, const char*, const char*); +}; + + +class UchRemoteAgent : public UchMsgStream { +friend class UchAgent; + +protected: + UchAgent *MyLocalAgent; + +public: + UchRemoteAgent (UchAgent*); + UchRemoteAgent (const UchRemoteAgent& cl); + UchRemoteAgent (UchAgent*, UchAddress*); + UchRemoteAgent (UchAgent*, UchChannel* ch); + ~UchRemoteAgent (); + + UchChannel* Copy () const; + void Delete (); + +inline UchAgent* GetLocalContact () { return MyLocalAgent; } +}; + + +#endif /* Agent_H_ */ diff --git a/comm/OLD/Event.cc b/comm/OLD/Event.cc new file mode 100644 index 0000000..5fdef7c --- /dev/null +++ b/comm/OLD/Event.cc @@ -0,0 +1,121 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1993 + * Centre d'Etudes de la Navigation Aerienne (CENA) + * + * Events, by Stephane Chatty + * + * $Id$ + * $CurLog$ + */ + +#include "Event.h" +#include + +UchEventFeatureList :: UchEventFeatureList (const UchEventFeatureList& base, int nbf, UchEventFeature* f) +: CcuListOf (base) +{ + Load (nbf, f); +} + +UchEventFeatureList :: UchEventFeatureList (int nbf, UchEventFeature* f) +: CcuListOf () +{ + Load (nbf, f); +} + +void +UchEventFeatureList :: Load (int nbf, UchEventFeature* f) +{ + while (nbf-- > 0) + Append (f++); +} + +UchEventFeatureList :: ~UchEventFeatureList () +{ +} + + +CcuListOf * UchEventType::AllTypes = 0; + +void +UchEventType :: ClassInit () +{ + AllTypes = new CcuListOf ; +} + +UchEventType :: UchEventType (const char* name, int nbf, UchEventFeature f []) +: Name (name), +#if 0 + Features (), +#endif + NbFeatures (nbf), + FeaturesOffsets (new int [nbf]) +{ + if (!AllTypes) + ClassInit (); + AllTypes->Append (this); + int offset = 0; + for (int i = 0; i < nbf; ++i) { + offset += f[i].Size; + FeaturesOffsets [i] = offset; + } +} + +UchEventType :: UchEventType (const char* name, const UchEventFeatureList& fl) +: Name (name), +#if 0 + Features (), +#endif + NbFeatures (fl.Length ()), + FeaturesOffsets (new int [NbFeatures]) +{ + if (!AllTypes) + ClassInit (); + AllTypes->Append (this); + int offset = 0; + int i = 0; + CcuListIterOf f = fl; + while (++f) { + offset += (*f)->Size; + FeaturesOffsets [i] = offset; + ++i; + } +} + +UchEventType :: ~UchEventType () +{ + AllTypes->Remove (this); + delete [] FeaturesOffsets; +} + +UchEvent :: UchEvent (const UchEventType* t) +: Type (*t) +{ + Data = new char [t->GetTotalSize ()]; +} + +UchEvent :: ~UchEvent () +{ + delete [] Data; +} + +void +UchEvent :: SetFeature (int i, const void* data) +{ + int offset = Type.GetOffset (i); + int size = Type.GetSize (i); + memcpy (Data+offset, data, size); +} + +void +UchEvent :: GetFeature (int i, void* data) +{ + int offset = Type.GetOffset (i); + int size = Type.GetSize (i); + memcpy (data, Data+offset, size); +} + diff --git a/comm/OLD/Event.h b/comm/OLD/Event.h new file mode 100644 index 0000000..27f3907 --- /dev/null +++ b/comm/OLD/Event.h @@ -0,0 +1,92 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1993 + * Centre d'Etudes de la Navigation Aerienne (CENA) + * + * Events, by Stephane Chatty + * + * $Id$ + * $CurLog$ + */ + +#ifndef UchEvent_H_ +#define UchEvent_H_ + +#include "cplus_bugs.h" +#include "global.h" +#include "ccu/String.h" +#include "ccu/List.h" + +#if 0 +class UchEventFeature { +protected: + CcuString Name; + UchEventFeatureType& Type; + +public: +inline UchEventFeature (const char* n, UchEventFeatureType& t) : Name (n), Type (t) {} +inline ~UchEventFeature () {} +}; +#endif + +struct UchEventFeature { + const char* Name; + int Size; +}; + + +class UchEventFeatureList : public CcuListOf { +private: + void Load (int, UchEventFeature*); + +public: + UchEventFeatureList (int, UchEventFeature*); + UchEventFeatureList (const UchEventFeatureList&, int, UchEventFeature*); + ~UchEventFeatureList (); +}; + + +class UchEventType { +private: +static void ClassInit (); + +protected: +static CcuListOf * AllTypes; + CcuString Name; +#if 0 + CcuListOf Features; +#endif + int NbFeatures; + int* FeaturesOffsets; + +public: + UchEventType (const char*, int, UchEventFeature []); + UchEventType (const char*, const UchEventFeatureList&); + ~UchEventType (); +inline int operator == (const UchEventType& evt) const { return (this == &evt); } +inline int GetTotalSize () const { return FeaturesOffsets [NbFeatures - 1]; } +inline int GetOffset (int i) const { return i ? FeaturesOffsets [i-1] : 0; } +inline int GetSize (int i) const { return FeaturesOffsets [i] - (i ? FeaturesOffsets [i-1] : 0); } +#if 0 +inline void AddFeature (const char* fn, UchEventFeatureType& t) { Features->Append (new UchEventFeature (fn, t); } +inline void RemoveFeature () { } +#endif +}; + +class UchEvent { +protected: + const UchEventType& Type; + char* Data; + +public: + UchEvent (const UchEventType*); + ~UchEvent (); + void SetFeature (int, const void*); + void GetFeature (int, void*); +inline const UchEventType& GetType () const { return Type; } +}; + +#endif /* UchEvent_H_ */ diff --git a/comm/OLD/PortServer.cc b/comm/OLD/PortServer.cc new file mode 100644 index 0000000..db4b020 --- /dev/null +++ b/comm/OLD/PortServer.cc @@ -0,0 +1,410 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Port server: client side + * + * $Id$ + * $CurLog$ + */ + +#include "PortServer.h" +#include "PortServerReq.h" +#include "error.h" +#include "Datagram.h" +#include "ccu/Timer.h" + +#include +#include +// sometimes, cuserid is declared in stdio.h ... +#include +#include + +/*?class UchPortServer +The class \typ{UchPortServer} described here implements the communication with the port server. +Both servers and clients need to use this class. +The server registers its address with a well-known key in the port server, +while a client uses that key to retrieve the server's address. +?*/ + +/*? +Construct a link to the port server, given a service name and a host name. +The service name must be present in the service database +(usually the file \com{/etc/services} or a yellow pages map). +The host name identifies the machine on which the port server lives. +If it is the null pointer, the loopback host is assumed. +?*/ +UchPortServer :: UchPortServer (const char* serv, const char* host) +: Name () +{ + /* get service name */ + struct servent* service = getservbyname (serv, 0); + sword portno; + if (service) { + portno = ntohs (service->s_port); + } else { + const char* port = getenv (serv); + if (port) + portno = atoi (port); + else + Error (ErrFatal, "getservbyname", "service not available"); + } + + /* open datagram socket */ + if (host) + Serv = new UchDatagram (0, new UchInetAddress (host, portno)); + else + Serv = new UchDatagram (0, new UchInetAddress (LOOPBACK, portno)); + if (!Serv->Setup ()) + SysError (ErrFatal, "Setup"); + Name = serv; +} + +/*?nodoc?*/ +UchPortServer :: ~UchPortServer () +{ + delete Serv; + Serv = 0; +} + +static void +SendRequest (UchDatagram* to, UchPortServerReq req) +{ + UchMsgBuffer buf (256); + buf.Append (req); + to->Write (buf); +} + +/*? +Register a server address in the port server. +The actual key that is stored in the port server is computed by \fun{MakeKey(key)}. +This function is to be used by servers that want to register themselves. +If the host field of \var{addr} is \var{LOOPBACK} or \var{ANYADDR}, the actual host id +is registered instead. This makes it possible to retrieve a valid address from another host. +\var{id} is used as an identication key when the server asks to be removed. +If it is 0, the process number of the server (\fun{getpid()}) is used. +?*/ +void +UchPortServer :: Register (const char* key, UchInetAddress& addr, int id) +{ + long host = addr.Host (); + if (host == LOOPBACK || host == ANYADDR) + host = UchInetAddress::LoopBack (); + if (! id) + id = getpid (); + UchPortServerReq req (PortServRegister, host, addr.Port (), id, MakeKey (key)); + SendRequest (Serv, req); +} + +/*? +Remove a registered service from the port server. +This function should be called by a server when it exits. +\var{id} authenticates the server so that any other process is unlikely +to be able to remove the service. +If it is 0, the process number of the server (\fun{getpid()}) is used. +If a server fails to remove itself, the association will stay forever in the port server, +unless it is overwritten by the registration of the same key. +?*/ +void +UchPortServer :: Remove (const char* key, UchInetAddress& addr, int id) +{ + long host = addr.Host (); + if (host == LOOPBACK || host == ANYADDR) + host = UchInetAddress :: LoopBack (); + if (! id) + id = getpid (); + UchPortServerReq req (PortServRemove, host, addr.Port (), id, MakeKey (key)); + SendRequest (Serv, req); +} + +#if 0 +static jmp_buf JmpTimeOut; + +static void +Timeout (int) +{ + longjmp (JmpTimeOut, 1); +} +#else +static bool expired = FALSE; +static void +Expire (Millisecond) +{ + expired = TRUE; +} +#endif + +/*? +Get a server address from the port server. +Wait for \var{timeout} seconds before giving up: +the port server may not respond because it is not present (the communication uses datagrams). +0 is returned if the function failed (because of the timeout or because +the given key is not registered); +else a newly allocated address is returned. +If the host field of the retrieved address is the same as the host id, +it is replaced by the loopback host. +This function is to be used by clients to retrieve the address of the server they want to connect to. +?*/ +UchInetAddress* +UchPortServer :: Inquire (const char* key, int timeout) +{ + UchPortServerReq req (PortServInquire, 0, 0, 0, MakeKey (key)); + SendRequest (Serv, req); + UchMsgBuffer buf (256); +#if 0 + if (setjmp (JmpTimeOut)) { + alarm (0); + Error (ErrWarn, "UchPortServer::Inquire", "timeout"); + return 0; + } + signal (SIGALRM, Timeout); + alarm (timeout); + int n = Serv->Receive (buf); + alarm (0); +#else + expired = FALSE; + CcuTimer timer (timeout * 1000, Expire); + int n = Serv->Receive (buf); + if (expired) { + Error (ErrWarn, "UchPortServer::Inquire", "timeout"); + return 0; + } +#endif + if (n <= 0) { + SysError (ErrWarn, "Inquire"); + return 0; + } + if (! buf.Get (&req)) { + Error (ErrWarn, "UchPortServer::Inquire", "could not read answer"); + return 0; + } + switch (req.Type) { + case PortServAnswer : + return new UchInetAddress (req.Host == UchInetAddress::LoopBack () ? LOOPBACK : req.Host, req.Port); + case PortServFail : + return 0; + } + Error (ErrWarn, "UchPortServer::Inquire", "unexpected answer type"); + return 0; +} + +/*? +Retrieve all entries in the port server that match a given key. +The key is a regular expression in the form of \com{ed(1)}. +For each match, the function \fun{f} is called with four arguments: +the matched string, its associated host and port numbers, and the original key. +If it returns 0, \fun{Match} returns immediately. +The type \typ{^{PortServMatchFun}} is declared like this:\\ +\com{typedef int (* PortServMatchFun) (const char*, lword, sword, const char*)}\\ +\fun{Match} waits for \var{timeout} seconds before giving up while waiting an answer: +the port server may not respond because it is not present (the communication uses datagrams). +The function returns the total number of expected answers (it can be 0), or -1 if an error occurs. +?*/ +int +UchPortServer :: Match (const char* key, PortServMatchFun f, int timeout) +{ + int nb = 0; + int call = 1; + + UchPortServerReq req (PortServMatch, 0, 0, 0, key); + SendRequest (Serv, req); + UchMsgBuffer buf (1024); + for (;;) { + buf.Flush (); +#if 0 + if (setjmp (JmpTimeOut)) { + alarm (0); + Error (ErrWarn, "UchPortServer::Match", "timeout"); + return 0; + } + signal (SIGALRM, Timeout); + alarm (timeout); + int n = Serv->Receive (buf); + alarm (0); +#else + expired = FALSE; + CcuTimer timer (timeout * 1000, Expire); + int n = Serv->Receive (buf); + if (expired) { + Error (ErrWarn, "UchPortServer::Match", "timeout"); + return 0; + } +#endif + if (n <= 0) { + SysError (ErrWarn, "Match"); + break; + } + if (! buf.Get (&req)) { + Error (ErrWarn, "Match", "could not read message"); + continue; + } + switch (req.Type) { + case PortServMatch: + if (call) + call = f (req.Key, req.Host, req.Port, key); + nb++; + break; + case PortServEndMatch: + if (nb != req.Ident) + Error (ErrWarn, "Match", "lost answers"); + return req.Ident; + default: + Error (ErrWarn, "Match", "unexpected answer type"); + return -1; + } + } + return nb; +} + +/*? +Ask the port server to dump its state. +This is not normally used unless for debugging. +Note that this is different from the save feature of the port server. +\fun{Dump} uses a different formats, and always uses the standard output of the port server. +?*/ +void +UchPortServer :: Dump () +{ + UchPortServerReq req (PortServDump); + SendRequest (Serv, req); +} + +/*? +Ask the port server to quit. +If the port server was configured to save its state, it does so before exiting. +NOTE: because the state cannot be reloaded from a file, this function +is dangerous: it makes the port server loose its associations. +?*/ +void +UchPortServer :: Quit () +{ + UchPortServerReq req (PortServQuit); + SendRequest (Serv, req); +} + +/*? +Build the actual key that will be stored in the port server. +The key is made of a set of components normally separated by colons. +Each component can be a literal string or a meta-character: +\com{\%h} for hostname, \com{\%s} for service name, \com{\%u} for user name. +A sample key is ``\com{\%s:\%u:foo}''. +\com{\%u} and \com{\%h} are useful when a given service needs a different server, +and thus a different association in the port server, +for different users and/or hosts. +\com{\%s} is useful if the same port server is used for different services. +?*/ +char* +UchPortServer :: MakeKey (const char* key) +{ + static char buffer [256]; + char* p = buffer; + + for (const char* q = key; *q; q++) { + if (*q == '%') { + switch (*++q) { + case 'h' : + if (gethostname (p, sizeof (buffer) - (p - buffer)) < 0) { + SysError (ErrWarn, "UchPortServer::MakeKey"); + strcpy (p, "???"); + } + break; + case 'u' : { + char* l = getlogin (); + if (! l) + l = cuserid (0); + if (! l) { + Error (ErrWarn, "UchPortServer::MakeKey", "getlogin failed"); + strcpy (p, "???"); + } + strcpy (p, l); + } + break; + case 's' : + strcpy (p, Name); + break; + default : + *p++ = '%'; + *p++ = *q; + *p = 0; + break; + } + p += strlen (p); + } else + *p++ = *q; + } + *p = 0; + + return buffer; +} + +#ifdef DOC +/*?nextdoc?*/ +void +PortServerRegister (const char* service, const char* name, UchAddress* addr) +{ } +#endif /* DOC */ + +/*? +This global function can be used for simple registration to the portserver. +The key used is ``\com{\%s:\%u:name}'', and the default identifier is the process number. +It connects to the local port server. +This function is overloaded for an easy use with \fun{UchAddress::GetAddr} +because it returns an \typ{UchAddress*}. +Be sure however that the address is an internet address (and not a Unix address for instance). +?*/ +void +PortServerRegister (const char* service, const char* name, UchInetAddress& addr) +{ + UchPortServer ps (service); + char key [256]; + strcpy (key, "%s:%u:"); + strcat (key, name); + ps.Register (key, addr, getpid ()); +} + +#ifdef DOC +/*?nextdoc?*/ +void +PortServerRemove (const char* service, const char* name, UchAddress* addr) +{ +} +#endif /* DOC */ + +/*? +This global function can be used for a simple removal from the portserver. +The key used is ``\com{\%s:\%u:name}'', and the default identifier is the process number. +It connects to the local port server. +This function is overloaded for an easy use with \fun{UchAddress::GetAddr} +because it returns an \typ{UchAddress*}. +Be sure however that the address is an internet address (and not a Unix address for instance). +?*/ +void +PortServerRemove (const char* service, const char* name, UchInetAddress& addr) +{ + UchPortServer ps (service); + char key [256]; + strcpy (key, "%s:%u:"); + strcat (key, name); + ps.Remove (key, addr, getpid ()); +} + +/*? +This global function can be used for a simple inquiry to the portserver. +The key used is ``\com{\%s:\%u:name}''. +It connects to the port server on the given host (default is local). +?*/ +UchInetAddress* +PortServerInquire (const char* service, const char* name, const char* host) +{ + UchPortServer ps (service, (host && *host) ? host : 0); + char key [256]; + strcpy (key, "%s:%u:"); + strcat (key, name); + return ps.Inquire (key); +} + + diff --git a/comm/OLD/PortServer.h b/comm/OLD/PortServer.h new file mode 100644 index 0000000..1cd8ff5 --- /dev/null +++ b/comm/OLD/PortServer.h @@ -0,0 +1,48 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Port server : client side + * + * $Id$ + * $CurLog$ + */ + +#ifndef PortServer_H_ +#define PortServer_H_ + +#include "Socket.h" +#include "ccu/String.h" +class UchDatagram; + +typedef int (* PortServMatchFun) (const char*, lword, sword, const char*); + +class UchPortServer { +protected: + UchDatagram* Serv; + CcuString Name; + +public: + UchPortServer (const char*, const char* = 0); + ~UchPortServer (); + + void Register (const char*, UchInetAddress&, int = 0); + void Remove (const char*, UchInetAddress&, int = 0); + UchInetAddress* Inquire (const char*, int = 10); + int Match (const char*, PortServMatchFun, int = 10); + void Dump (); + void Quit (); + char* MakeKey (const char*); +}; + +extern void PortServerRegister (const char*, const char*, UchInetAddress&); +extern void PortServerRemove (const char*, const char*, UchInetAddress&); +inline void PortServerRegister (const char* s, const char* h, UchAddress* a) { PortServerRegister (s, h, * ((UchInetAddress*) a)); } +inline void PortServerRemove (const char* s, const char* h, UchAddress* a) { PortServerRemove (s, h, * ((UchInetAddress*) a)); } +extern UchInetAddress* PortServerInquire (const char*, const char*, const char* = 0); + +#endif /* PortServer_H_ */ diff --git a/comm/OLD/PortServerReq.cc b/comm/OLD/PortServerReq.cc new file mode 100644 index 0000000..5628408 --- /dev/null +++ b/comm/OLD/PortServerReq.cc @@ -0,0 +1,60 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Port server requests + * + * $Id$ + * $CurLog$ + */ + +#include "PortServerReq.h" + +UchPortServerReq :: UchPortServerReq () +: UchMessage (), + Type (0), + Host (0), + Port (0), + Ident (0), + Key (0) +{ +} + +UchPortServerReq :: UchPortServerReq (sword t) +: Type (t), + Host (0), + Port (0), + Ident (0), + Key (0) +{ +} + +UchPortServerReq :: UchPortServerReq (sword t, lword h, sword p, lword i, const char* k) +: Type (t), + Host (h), + Port (p), + Ident (i), + Key (k) +{ +} + +UchPortServerReq :: ~UchPortServerReq () +{ +} + +void +UchPortServerReq :: ReadFrom (UchMsgBuffer& b, lword) +{ + b >> Type >> Host >> Port >> Ident; + Key = b.BufLength () ? (const char*) b.Buffer () : 0; +} + +void +UchPortServerReq :: WriteTo (UchMsgBuffer& b) +{ + b << Type << Host << Port << Ident << (char*) Key; +} diff --git a/comm/OLD/PortServerReq.h b/comm/OLD/PortServerReq.h new file mode 100644 index 0000000..febde80 --- /dev/null +++ b/comm/OLD/PortServerReq.h @@ -0,0 +1,45 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Port server requests + * + * $Id$ + * $CurLog$ + */ + +#ifndef PortServerReq_H_ +#define PortServerReq_H_ + +#include "MsgBuffer.h" +#include "Message.h" + +enum PortServMessages { + PortServRegister, PortServRemove, PortServInquire, + PortServMatch, PortServEndMatch, PortServAnswer, + PortServFail, PortServDump, PortServQuit +}; + +class UchPortServerReq : public UchMessage { +public: + sword Type; + lword Host; + sword Port; + const char* Key; + lword Ident; + + UchPortServerReq (); + UchPortServerReq (sword); + UchPortServerReq (sword, lword, sword, lword, const char* = 0); + ~UchPortServerReq (); + +inline void SetKey (const char* k) { Key = k; } + void ReadFrom (UchMsgBuffer&, lword); + void WriteTo (UchMsgBuffer&); +}; + +#endif /* PortServerReq_H_ */ diff --git a/comm/OLD/ReqMgr.cc b/comm/OLD/ReqMgr.cc new file mode 100644 index 0000000..3850723 --- /dev/null +++ b/comm/OLD/ReqMgr.cc @@ -0,0 +1,184 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1993 + * Centre d'Etudes de la Navigation Aerienne (CENA) + * + * Request management, by Stephane Chatty + * + * $Id$ + * $CurLog$ + */ + +#include "ReqMgr.h" +#include +#include +#include + +UchReqMgr :: UchReqMgr () +: Name () +{ +} + +UchReqMgr :: ~UchReqMgr () +{ +} + +void +UchReqMgr :: SetName (const char* n) +{ + if (Name) + fprintf (stderr, "client type already set to %s\n", (const char*) Name); + else + Name = n; +} + +void +UchReqMgr :: DumpHeader (const char* file) +{ + ofstream f (file, ios::out); + if (!f) { + extern int errno; + fprintf (stderr, "can't write to %s: %s\n", file, sys_errlist [errno]); + return; + } + f << "/*\n *\tRequests for clients " << Name << "\n"; + f << " *\n *\tThis file was generated by reqgen - do not edit\n*/\n\n"; + f << "#ifndef " << Name << "Req_H_\n"; + f << "#define " << Name << "Req_H_\n\n"; + f << "#include \n\n"; + + CcuListIterOf req (Requests); + while (++req) { + (*req)->DumpHeader (f); + } + f << "#endif\t/* " << Name << "Req_H_ */\n"; +} + + +void +UchReqMgr :: DumpSource (const char* file) +{ + ofstream f (file, ios::out); + if (!f) { + extern int errno; + fprintf (stderr, "can't write to %s: %s\n", file, sys_errlist [errno]); + return; + } + + f << "/*\n *\tRequests for clients " << Name << "\n"; + f << " *\n *\tThis file was generated by reqgen - do not edit\n*/\n\n"; + f << "#include \"" << file << ".h\"\n\n"; + + CcuListIterOf req (Requests); + while (++req) { + (*req)->DumpSource (f); + } +} + + +void +RequestType :: DumpHeader (ofstream& f) +{ + f << "class " << Name << " : public UchMessage {\nprotected:\n"; + + CcuListIterOf fields (Fields); + while (++fields) { + RequestField* field = *fields; + f << "\t" << field->GetImpl () << "\t" << field->GetName () << ";\n"; + } + + f << "public:\n\t\t" << Name << " ();\n"; + + CcuListIterOf constr (Constructors); + while (++constr) { + f << "\t\t" << Name << " ("; + CcuListIterOf fields ((*constr)->GetParameters ()); + int first = 1; + while (++fields) { + if (first) + first = 0; + else + f << ", "; + f << (*fields)->GetType (); + } + f << ");\n"; + } + f << "\t\t~" << Name << " ();\n"; + f << "\tvoid\tWriteTo (UchMsgBuffer&);\n"; + f << "\tvoid\tReadFrom (UchMsgBuffer&, lword);\n"; + f << "\tvoid\tActivate ();\n"; + + CcuListIterOf getter (Getters); + while (++getter) { + RequestField* field = *getter; + f << "inline\t" << field->GetType () << "\tGet" << field->GetName () + << " () const { return (" << field->GetType () << ") " << field->GetName () << "; }\n"; + } + + CcuListIterOf setter (Setters); + while (++setter) { + RequestField* field = *setter; + f << "inline\tvoid\tSet" << field->GetName () << " (" << field->GetType () + << " f) { " << field->GetName () << " = (" << field->GetImpl () << ") f; }\n"; + } + + f << "};\n\n"; +} + +void +RequestType :: DumpSource (ofstream& f) +{ + f << Name << " :: " << Name << " ()\n: UchMessage ()\n{\n}\n\n"; + + CcuListIterOf constr (Constructors); + while (++constr) { + f << Name << " :: " << Name << " ("; + CcuListIterOf fields ((*constr)->GetParameters ()); + int i = 0; + while (++fields) { + if (i > 0) + f << ", "; + f << (*fields)->GetType () << " i" << i; + ++i; + } + f << ")\n: UchMessage ()"; + fields.Reset (); + i = 0; + while (++fields) { + f << ",\n " << (*fields)->GetName () << " (i" << i << ")"; + ++i; + } + f << "\n{\n}\n\n"; + } + + f << Name << " :: ~" << Name << " ()\n{\n}\n\n"; + + f << "#ifdef SERVER\n\n"; + + f << "void\n" << Name << " :: ReadFrom (UchMsgBuffer& b, lword l)\n{\n"; + f << "\tb"; + CcuListIterOf field (Fields); + while (++field) { + f << " >> " << (*field)->GetName (); + } + f << ";\n"; + f << "}\n\n"; + + f << "#endif\t/* SERVER */\n\n"; + f << "#ifdef CLIENT\n\n"; + + f << "void\n" << Name << " :: " << "WriteTo (UchMsgBuffer& b)\n{\n"; + f << "\tb"; + field.Reset (); + while (++field) { + f << " << " << (*field)->GetName (); + } + f << ";\n"; + f << "}\n\n"; + + f << "#endif\t/* CLIENT */\n"; +} + diff --git a/comm/OLD/ReqMgr.h b/comm/OLD/ReqMgr.h new file mode 100644 index 0000000..1b02196 --- /dev/null +++ b/comm/OLD/ReqMgr.h @@ -0,0 +1,81 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1993 + * Centre d'Etudes de la Navigation Aerienne (CENA) + * + * Request management, by Stephane Chatty + * + * $Id$ + * $CurLog$ + */ + +#ifndef ReqMgr_H_ +#define ReqMgr_H_ + +#include "ccu/String.h" +#include "ccu/List.h" +class ofstream; + +class RequestField { +protected: + CcuString Name; + CcuString Type; + CcuString Impl; +public: +inline RequestField (const char* n, const char* t, const char* i) : Name (n), Type (t), Impl (i) {} +inline const char* GetName () const { return Name; } +inline const char* GetType () const { return Type; } +inline const char* GetImpl () const { return Impl; } +}; + +class RequestConstructor { +friend int yyparse (); +protected: + CcuListOf Params; +public: +inline void AddParameter (RequestField& f) { Params.Append (&f); } +inline const CcuListOf & GetParameters () const { return Params; } +}; + +class RequestType { +friend int yyparse (); +protected: + CcuString Name; + CcuListOf Fields; + CcuListOf Getters; + CcuListOf Setters; + CcuListOf Constructors; + +public: +inline RequestType (const char* n) : Name (n) { } +inline void AddField (RequestField* f) { Fields.Append (f); } +inline void AddConstructor (RequestConstructor* f) { Constructors.Append (f); } +inline const char* GetName () const { return Name; } + void Dump (ofstream&); + void DumpHeader (ofstream&); + void DumpSource (ofstream&); +}; + + +class UchReqMgr { +friend int yyparse (); + +protected: + CcuString Name; + CcuListOf Requests; + void SetName (const char*); +inline void Add (RequestType* t) { Requests.Append (t); } + +public: + UchReqMgr (); + ~UchReqMgr (); + void Read (const char*); + void Dump (const char*); + void DumpHeader (const char*); + void DumpSource (const char*); +}; + +#endif /* ReqMgr_H_ */ diff --git a/comm/OLD/ReqMgr.l b/comm/OLD/ReqMgr.l new file mode 100644 index 0000000..bad288c --- /dev/null +++ b/comm/OLD/ReqMgr.l @@ -0,0 +1,95 @@ +%{ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1993 + * Centre d'Etudes de la Navigation Aerienne (CENA) + * + * Request management, by Stephane Chatty + * + * $Id$ + * $CurLog$ + */ + +#include +#include +#include "ccu/HashTable.h" +#include "ccu/String.h" +#include "ReqMgr.yacc.h" // produced from the parser file with '-d' option of yacc + +extern int tee (int); +#define RETURN(x) return (tee(x)) + +extern "C" { + int atoi (const char *); + void exit (int); + int read (int, char*, int); +} + + +CcuDictionnary* ResWords; +int LineNo = 1; +static char IdBuf [1024]; + +void +LexInit () +{ + if (!ResWords) { + ResWords = new CcuDictionnary (10); + (*ResWords)["client"] = (void*) (Y_CLIENT); + (*ResWords)["request"] = (void*) (Y_REQUEST); + (*ResWords)["getters"] = (void*) (Y_GETTERS); + (*ResWords)["setters"] = (void*) (Y_SETTERS); + (*ResWords)["const"] = (void*) (Y_CONST); +// (*ResWords)[""] = (void*) (Y_); + } + LineNo = 1; + +#ifdef FLEX_SCANNER +static void yyrestart (FILE*); + yyrestart (0); +#endif +} + +%} + +Int [-]?[0-9]+ +Ident [a-zA-Z_][a-zA-Z_0-9]* +nl [\n] +sp0 [ \t]* +sp1 [ \t]+ +LocHeader \"{Ident}.h\" +GlobHeader \ + +%% + +[,();{}*] RETURN (yytext[0]); + +\%.*\n { /* comments */ + ++LineNo; + } + +{sp1} {} + +{nl} { + ++LineNo; + } + +-> { RETURN (Y_YIELDS); } + +#include +{Ident} { + CcuHashCell* c; + if (c = ResWords->Get (yytext)) { + RETURN(int (c->GetInfo ())); + } else { + yylval.string = NewString (yytext); + RETURN (Y_ID); + } + } + +. { + RETURN (-1); + } diff --git a/comm/OLD/ReqMgr.y b/comm/OLD/ReqMgr.y new file mode 100644 index 0000000..4c29f00 --- /dev/null +++ b/comm/OLD/ReqMgr.y @@ -0,0 +1,285 @@ +%{ + +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1993 + * Centre d'Etudes de la Navigation Aerienne (CENA) + * + * Request management, by Stephane Chatty + * + * $Id$ + * $CurLog$ + */ + +#include "ccu/HashTable.h" +extern "C" { + int yyerror (const char*); + int yyparse(); + int yywrap (); + int yylex(); +} + +/* This comes from the lexical analyzer */ +extern CcuDictionnary* ResWords; + +static void error (const char*, ...); + +#include +#include +#include +#include + +#include "ReqMgr.h" + +static UchReqMgr* CurMgr; +static RequestType* CurRequest; +static CcuListOf * CurFieldList; +static CcuDictionnaryOf Fields (16); + +static void AddFieldToReq (const char*, const char*, const char*, const char*, const char*); +static void AddFieldToList (const char*); + +%} + +%union { + int integer; + const char *string; +} + +%token Y_CLIENT Y_REQUEST Y_YIELDS Y_GETTERS Y_SETTERS Y_CONST +%token Y_ID +%type opt_star + +%% + +file: + /* empty */ + | file decl + ; + +decl: + client + | request + ; + +client: + Y_CLIENT Y_ID ';' + { + CurMgr->SetName ($2); + } + ; + +request: + Y_REQUEST Y_ID + { + CurRequest = new RequestType ($2); + CurMgr->Add (CurRequest); + Fields.Clear (); + } + '{' request_entries '}' ';' + ; + +request_entries: + /* empty */ + | request_entries request_entry + ; + +request_entry: + field + | getters + | setters + | constructor + ; + +field: + Y_CONST Y_ID opt_star Y_ID Y_YIELDS Y_ID ';' + { + AddFieldToReq ("const", $2, $3, $4, $6); + } + | Y_ID opt_star Y_ID Y_YIELDS Y_ID ';' + { + AddFieldToReq (0, $1, $2, $3, $5); + } + ; + +opt_star: + /* empty */ { $$ = 0; } + | '*' { $$ = "*"; } + ; + +getters: + Y_GETTERS + { + CurFieldList = &CurRequest->Getters; + } + '(' id_list ')' ';' + ; + +setters: + Y_SETTERS + { + CurFieldList = &CurRequest->Setters; + } + '(' id_list ')' ';' + ; + +constructor: + Y_ID + { + if (strcmp ($1, CurRequest->GetName ()) != 0) + fprintf (stderr, "unknown name %s in request type %s. Considering as constructor.\n", + $1, CurRequest->GetName ()); + RequestConstructor* c = new RequestConstructor; + CurRequest->AddConstructor (c); + CurFieldList = &c->Params; + } + '(' id_list ')' ';' + ; + +id_list: + /* empty */ + | Y_ID { AddFieldToList ($1); } + | id_list ',' Y_ID { AddFieldToList ($3); } + ; + +%% +#include + +static void +AddFieldToReq (const char* c, const char* t, const char* s, const char* id, const char* impl) +{ + int found; + CcuHashCellOf * hc = Fields.Add (id, &found); + if (found) { + fprintf (stderr, "warning: duplicate field %s in request %s. Ignoring. \n", + id, CurRequest->GetName ()); + } else { + char buf [128]; + if (c || s) { + sprintf (buf, "%s %s %s", (c ? c : ""), t, (s ? s : "")); + t = buf; + } + RequestField* f = new RequestField (id, t, impl); + hc->SetInfo (f); + CurRequest->AddField (f); + } +} + +static void +AddFieldToList (const char* name) +{ + RequestField* f = Fields [name]; + if (!f) { + fprintf (stderr, "warning: unknown field %s in request %s\n", + name, CurRequest->GetName ()); + } else { + CurFieldList->Append (f); + } +} + +int _DoTee = 0; + +int +tee (int x) +{ + if (_DoTee) { + char *s; + switch (x) { + case '(': + s = "("; + break; + case ')': + s = ")"; + break; + case '*': + s = "*"; + break; + case ';': + s = ";"; + break; + case ',': + s = ","; + break; + case '{': + s = "}"; + break; + case '\n': + s = "\n"; + break; + case Y_ID: + s = "ID"; + break; + case Y_CLIENT: + s = "CLIENT"; + break; + case Y_REQUEST: + s = "REQUEST"; + break; + case Y_YIELDS: + s = "->"; + break; + case Y_GETTERS: + s = "GETTERS"; + break; + case Y_SETTERS: + s = "SETTERS"; + break; +/* case Y_: + s = ""; + break; +*/ + default : + s = "???"; + break; + } + printf ("%s ", s); + } + return x; +} + +int +yyerror (const char *s) +{ + error ("%s\n",s); + return (0); +} + +int +yywrap () +{ + return 1; +} + +void +UchReqMgr :: Read (const char* file) +{ + extern void LexInit (); + extern FILE *yyin; + LexInit (); + if (!(yyin = fopen (file,"r"))) { + char err [1024]; + sprintf (err, "Cannot open %s", file); + yyerror (err); + return; + } + CurMgr = this; + if (yyparse ()) + fprintf (stderr, "\nParsing failed on %s\n", file); + fclose (yyin); +} + +extern int LineNo; + +static void +error (const char* s, ...) +{ + va_list ap; + va_start(ap, s); + + fprintf (stderr, "Error at line %d: ", LineNo); + vfprintf (stderr, s, ap); + exit (0); +} diff --git a/comm/OLD/Server.cc b/comm/OLD/Server.cc new file mode 100644 index 0000000..a2e908b --- /dev/null +++ b/comm/OLD/Server.cc @@ -0,0 +1,395 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Servers + * + * $Id$ + * $CurLog$ + */ + +#include "Server.h" + +/*?class UchServer +The class \typ{UchServer} derives from \typ{UchStream}. +It is associated to a channel set to monitor the connected clients in parallel. +Whenever a new client connects to the server, the virtual function \fun{HandleNew} is called. +This function must return a pointer to an object of class \typ{UchClient} (or a derived class of \typ{UchClient}). + +Because the server and the clients may be on different machines with different architectures, +the byte swapping is always done by the server, by convention. (NOTE: byte swapping is not currently implemented) +?*/ + +/*?class UchClient +Class \typ{UchClient} derives from \typ{UchMsgStream}. +It is still an abstract class because the virtual functions \fun{NewMessage} and \fun{ConvertAnswer} +of \typ{UchMsgStream} are not defined in \typ{UchClient}. +Instances of \fun{UchClient} only know that they may belong to an instance of \typ{UchServer}. +Thus the virtual function \fun{Delete} is redefined to achieve a clean removal from the server's list. +?*/ + +// server ports +// constructors, +// destructor +// copy +// + +/*? +Construct an empty server port. +?*/ +UchServer :: UchServer () +: UchStream (), + Clients (), + ChanSet (0) +{ +} + +/*?nodoc?*/ +UchServer :: UchServer (const UchServer& sp) +: UchStream (sp), + Clients (), + ChanSet (0) +{ +} + +/*? +Construct a server port bound to address \var{a}. +When using Internet domain addresses, \var{a} should refer to +the wildcard address so that clients can connect from any machine. +This can be done with the instruction \com{new UchInetAddress(ANYADDR)}. +?*/ +UchServer :: UchServer (UchAddress* a) +: UchStream (a, 0), + Clients (), + ChanSet (0) +{ +} + +/*?nodoc?*/ +UchServer :: ~UchServer () +{ + UchClient* cl = 0; + + while (cl = Clients.First ()) + cl->Delete (); + ChanSet = 0; // will delete it if necessary +} + +/*?nodoc?*/ +UchChannel* +UchServer :: Copy () const +{ + return new UchServer (*this); +} + +/*? +Remove a client from the set of clients connected to the server. +This is normally done automatically whenever the client disconnects itself, +as a side effect of destroying the client. +?*/ +void +UchServer :: RemoveClient (UchClient* cl) +{ + if (Clients.Remove (cl)) { + cl->MyServer = 0; + HandleRemove (cl); + } else + Error (ErrWarn, "UchServer::RemoveClient", "not owned by this server"); +} + +/*? +Bind the socket and listen to it. +Initialize the channel set of the server to \var{cs}, or create a new one if \var{cs} is 0. +The channel set is used by the server to read and process messages from its clients: +whenever a client connects to the server, an instance of \typ{UchClient} is added to the channel set. +Whenever data is readable from a client, it is read in an input buffer and processed as soon +as a full message is received. +Whenever a client closes the connection, it is removed from the channel set of the server. +This function returns TRUE if all went well, else it calls \fun{SysError} and returns FALSE. +You may want to pass your own channel set if you are already multiplexing +i/o on that channel set. For instance, you may have several \typ{UchServer} objects +in your application. +A smart pointer to the channel set is actually kept in the server, so that it can be shared +securely. +?*/ +bool +UchServer :: Setup (UchMultiplexer* cs) +{ + if (Listen () < 0) { + SysError (ErrWarn, "UchServer::Setup"); + return FALSE; + } + SetMode (IORead); + if (! cs) + cs = new UchMultiplexer; + ChanSet = cs; + ChanSet->Add (this); + + return TRUE; +} + +/*? +This function removes the server from its channel set. +This has the effect of ignoring any incoming connections. +This function will also make \fun{Run} exit, if there is no other channel in the channel set of the server. +Unless you added your own channels to this channel set, +this will be the case if there is no client currently connected. +This is the only way to exit properly from \fun{Run}. +?*/ +void +UchServer :: Unlisten () +{ + if (ChanSet) + ChanSet->Remove (*this); +} + + +// handle connections on server port +// + +/*?nodoc?*/ +void +UchServer :: HandleRead () +{ +// cannot redefine Accept to return UchClient*, so this does not work : +// UchClient* cl = Accept (); +// need to delete the channel created by Accept, so this does not work either : +// UchClient* cl = new UchClient (Accept ()); +// so we do this (less elegant ...) : + UchChannel* ch = Accept (); + if (! ch) { + SysError (ErrWarn, "UchServer::HandleRead: Accept"); + return; + } + UchClient* cl = HandleNew (ch); + delete ch; + + cl->SetMode (IOReadSelect); + cl->SetOwner (this); + Clients.Append (cl); + if (ChanSet) + ChanSet->Add (cl); +} + +// Default virtual function for handling new connections +// If a class MY_CLIENT is derived from UchClient, +// a class MY_SERVER must be derived from UchServer, +// with MY_SERVER::HandleNew (UchChannel* ch) +// doing at least a return new MY_CLIENT (ch) +// +/*? +This virtual function is called whenever a new client connects to the server. +The default action is to return \com{new UchClient(ch)}. +This needs to be redefined only if you create a derived class of \typ{UchClient}, +say \typ{MY_CLIENT}, in which case it should be redefined in a derived class +of \typ{UchServer} to return \com{new MY_CLIENT(ch)}. +?*/ +UchClient* +UchServer :: HandleNew (UchChannel* ch) +{ + return new UchClient (ch); +} + +/*? +This virtual function is called whenever a client is removed (\fun{RemoveClient}). +It is a hook for the application to take whatever action; the default action is to do nothing. +When this function is called, the client is already removed from the client list of the server. +?*/ +void +UchServer :: HandleRemove (UchClient*) +{ + // nothing +} + +// default error functions +// +/*?nextdoc?*/ +bool +UchServer :: SysError (errtype how, const char* who, int excl1, int excl2) +{ + return ::SysError (how, who, excl1, excl2); +} + +/*? +Each server port class can have its own error handling routines. +Their default action is to call the global functions \fun{Error} and \fun{SysError}. +They can be called from the following functions: +\fun{Error} can be called from +\fun{RemoveClient} when the client does not belong to this server; +\fun{SysError} can be called from +\fun{Setup} when the socket could not be setup correctly, +and from \fun{HandleRead} +when the connection could not be accepted. +?*/ +void +UchServer :: Error (errtype how, const char* who, const char* what) +{ + ::Error (how, who, what); +} + +/*? +Setup the server if necessary, then scan and process its channel set +by calling \fun{LoopScan} for it. +To have a server active, you need at least to initialize it (and thus know its address), +and then run it. +If you have added your own channels to the channel set of the server, +their \fun{HandleRead} and \fun{HandleWrite} functions will be called normally by \fun{Run}. +?*/ +void +UchServer :: Run () +{ + if (! ChanSet) + if (! Setup ()) + Error (ErrFatal, "UchServer::Run", "could not setup"); + if (ChanSet) + ChanSet->LoopScan (); +} + +/*? +Send a message to all clients currently connected to this server. +If \var{flush} is TRUE, the output buffer of each client is flushed. +?*/ +void +UchServer :: Broadcast (UchMessage& msg, bool flush) +{ + CcuListIterOf li (Clients); + while (++li) + (*li)->Send (msg, flush); +} + +/*? +Send a message to all clients currently connected to this server, except \var{exclude}. +If \var{flush} is TRUE, the output buffer of each client is flushed. +?*/ +void +UchServer :: Broadcast (UchMessage& msg, UchClient* excl, bool flush) +{ + CcuListIterOf li (Clients); + while (++li) + if (*li != excl) + (*li)->Send (msg, flush); +} + +/*?nextdoc?*/ +void +UchServer :: Broadcast (UchMsgBuffer& buf, bool flush) +{ + CcuListIterOf li (Clients); + while (++li) + (*li)->Send (buf, flush); +} + +/*? +These functions are similar to the previous ones, except that they take +a buffer instead of a message. The buffer {\em must} contain a converted message. +It is more efficient to broadcast a buffer than a message because there is less +message conversion overhead. +?*/ +void +UchServer :: Broadcast (UchMsgBuffer& buf, UchClient* excl, bool flush) +{ + CcuListIterOf li (Clients); + while (++li) + if (*li != excl) + (*li)->Send (buf, flush); +} + +// clients: +// + +/*?nodoc?*/ +UchClient :: UchClient () +: UchMsgStream (), + MyServer (0) +{ +} + +/*?nodoc?*/ +UchClient :: UchClient (const UchClient& cl) +: UchMsgStream (cl), + MyServer (0) +{ +} + +/*? +Construct a new client connected to the server on channel \var{ch}. +?*/ +UchClient :: UchClient (UchChannel* ch) +: UchMsgStream (), + MyServer (0) +{ + UchChannel::Open (ch->FilDes ()); +} + +/*?nodoc?*/ +UchClient :: ~UchClient () +{ + if (MyServer) { + UchServer* s = MyServer; + s->Error (ErrWarn, "~UchClient", "client still in a server; deleting anyway ...\n"); + s->RemoveClient (this); + if (s->ChanSet) + s->ChanSet->Remove (*this); // calls the destructor if no more refs + } +} + +/*?nodoc?*/ +UchChannel* +UchClient :: Copy () const +{ + return new UchClient (*this); +} + +/*? +This function must be used to delete a client explicitly. +It is not safe to use the operator delete. +This function is called when an end of file is read from the client; +this means that you usually do not need to call it. +?*/ +void +UchClient :: Delete () +{ + if (MyServer) { + UchServer* s = MyServer; + s->RemoveClient (this); + if (s->ChanSet) + s->ChanSet->Remove (*this); // calls the destructor if no more refs + } +} + +/*?nodoc?*/ +void +UchClient :: SetOwner (UchServer* serv) +{ + if (MyServer) + serv->Error (ErrFatal, "UchClient::SetOwner", "already owned"); + else + MyServer = serv; +} + +#ifdef DOC +//fake entries for inline functions + +/*? +Return the channel set used by the server. +This is useful if you want to add your own channels to the channel set. +?*/ +UchMultiplexer* +UchServer :: GetChanSet () +{ +} + +/*? +Return the server corresponding to a given client. +?*/ +UchServer* +UchClient :: GetServer () +{ +} + +#endif /* DOC */ + diff --git a/comm/OLD/Server.h b/comm/OLD/Server.h new file mode 100644 index 0000000..7f8c597 --- /dev/null +++ b/comm/OLD/Server.h @@ -0,0 +1,77 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Servers + * + * $Id$ + * $CurLog$ + */ + +#ifndef Server_H_ +#define Server_H_ + +#include "ccu/List.h" +#include "MsgStream.h" +#include "Multiplexer.h" +#include "error.h" + +class UchServer; + +class UchClient : public UchMsgStream { +friend class UchServer; + +protected: + UchServer *MyServer; + +public: + UchClient (); + UchClient (const UchClient& cl); + UchClient (UchChannel* ch); + ~UchClient (); + + UchChannel* Copy () const; + void Delete (); + +inline UchServer* GetServer () { return MyServer; } + void SetOwner (UchServer*); +}; + +class UchServer : public UchStream { +friend class UchClient; + +protected: + CcuListOf Clients; + pUchMultiplexer ChanSet; + +public: + UchServer (); + UchServer (const UchServer&); + UchServer (UchAddress*); + ~UchServer (); + + UchChannel* Copy () const; + void HandleRead (); + + bool Setup (UchMultiplexer* cs = 0); +inline UchMultiplexer* GetMultiplexer () { return ChanSet; } + void RemoveClient (UchClient*); + void Run (); + void Unlisten (); + void Broadcast (UchMessage&, bool = FALSE); + void Broadcast (UchMessage&, UchClient*, bool = FALSE); + void Broadcast (UchMsgBuffer&, bool = FALSE); + void Broadcast (UchMsgBuffer&, UchClient*, bool = FALSE); +// called by HandleRead when a new client connects: must build up a client from a channel +virtual UchClient* HandleNew (UchChannel*); +virtual void HandleRemove (UchClient*); +virtual bool SysError (errtype, const char*, int = 0, int = 0); +virtual void Error (errtype, const char*, const char*); +}; + + +#endif /* Server_H_ */ diff --git a/comm/OLD/Service.cc b/comm/OLD/Service.cc new file mode 100644 index 0000000..618214b --- /dev/null +++ b/comm/OLD/Service.cc @@ -0,0 +1,328 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Client side: services + * + * $Id$ + * $CurLog$ + */ + +#include "Service.h" + +/*?class UchService +An object of class \typ{UchService} (derived from \typ{UchMsgStream}) exists in a client process +to represent the server it is connected to. + +The class \typ{UchService} is virtual: you must create subclasses that redefine +at least \fun{NewMessage} from class \fun{UchMsgStream}, and if necessary \fun{ConvertAnswer}. +\fun{NewMessage} should decipher the incoming message, transform it into an event, +and put in into the event queue with \fun{PutEvent}. +It can also handle non event messages (for instance errors). +?*/ + +/*?class UchEvtMsgQueue +An event queue is a linked list of events. +Events are normally appended to the end of the queue and extracted from the beginning. +?*/ + +/*?nodoc?*/ +UchEvtMsgQueue :: UchEvtMsgQueue () +: CcuSmartData (), + Queue () +{ +} + +/*?nodoc?*/ +UchEvtMsgQueue :: ~UchEvtMsgQueue () +{ +} + +#ifdef DOC +/*? +Append an event to the queue. +?*/ +void +UchEvtMsgQueue :: Put (UchEventMsg* msg) +{ +} + +/*? +Put an event back in the queue (therefore it becomes the first event of the queue). +?*/ +void +UchEvtMsgQueue :: PutBack (UchEventMsg* msg) +{ +} + +/*? +Return the first event from the queue and remove it. +?*/ +UchEventMsg* +UchEvtMsgQueue :: Get () +{ +} + +/*? +Return the first event from the queue without removing it. +?*/ +UchEventMsg* +UchEvtMsgQueue :: Peek () +{ +} + +#endif /* DOC */ + + +/*? +Construct an empty service. +?*/ +UchService :: UchService () +: UchMsgStream (), + EvQueue (0) +{ +} + +/*? +Construct a service connected to address \var{a}. +?*/ +UchService :: UchService (UchAddress* a) +: UchMsgStream (0, a), + EvQueue (0) +{ +} + +/*?nodoc?*/ +UchService :: UchService (const UchService& s) +: UchMsgStream (s), + EvQueue (s.EvQueue) +{ +} + +/*?nodoc?*/ +UchService :: ~UchService () +{ + EvQueue = 0; // deletes it +} + +/*?nodoc?*/ +UchChannel* +UchService :: Copy () const +{ + return new UchService (*this); +} + +/*? +Set the event queue to be used to store the incoming events. +If not set, a default event queue is created. +This function is intended to share a queue between several servers. +?*/ +void +UchService :: SetEvQueue (UchEvtMsgQueue* evq) +{ + EvQueue = evq; +} + +/*?nextdoc?*/ +UchEventMsg* +UchService :: PeekEvent (bool wait) +{ + Flush (); + if (!EvQueue) + EvQueue = new UchEvtMsgQueue; + UchEventMsg* ev = EvQueue->Peek (); + if (ev || ! wait) + return ev; + while (! ev) { + HandleRead (); + ev = EvQueue->Peek (); + } + return ev; +} + +/*? +These functions flush the output buffer, and check the events already in the queue. +If there is at least one, it is returned; +\fun{GetEvent} also removes it from the event queue. +If the event queue is empty and \var{wait} is TRUE, the function blocks until an event arrives, +else it returns 0 without blocking. +These functions also create the event queue if it was not set with \fun{SetEvQueue}. +?*/ +UchEventMsg* +UchService :: GetEvent (bool wait) +{ + Flush (); + if (!EvQueue) + EvQueue = new UchEvtMsgQueue; + UchEventMsg* ev = EvQueue->Get (); + if (ev || ! wait) + return ev; + while (! ev) { + HandleRead (); + ev = EvQueue->Get (); + } + return ev; +} + +/*?nextdoc?*/ +void +UchService :: PutEvent (UchEventMsg* ev) +{ + if (! EvQueue) + EvQueue = new UchEvtMsgQueue; + ev->From = this; + EvQueue->Put (ev); +} + +/*? +These functions are similar to the functions \fun{Put} and \fun{PutBack} +on the event queue of the server. +They create the event queue if it was not set with \fun{SetEvQueue}, +and set the event's server. +?*/ +void +UchService :: PutBackEvent (UchEventMsg* ev) +{ + if (! EvQueue) + EvQueue = new UchEvtMsgQueue; + ev->From = this; + EvQueue->PutBack (ev); +} + + +/*?class UchEventMsg +This class derives from \typ{UchMessage}, so it inherits the usual virtual functions +\fun{ReadFrom} and \fun{WriteTo} that must be redefined in each derived class. +An event is created when a client receives an asynchronous message from its server. +Events are linked together in event queues. +Events must derive from this class. +?*/ + +#ifdef DOC +// fake entries for inline functions + +/*? +Construct an event. +?*/ +UchEventMsg :: UchEventMsg () +{ +} + +#endif /* DOC */ + +/*?nodoc?*/ +UchEventMsg :: ~UchEventMsg () +{ +} + +#ifdef DOC + +/*? +Return the server that sent this event. +The service is known only if the event was appended to the event queue with \fun{UchService::PutEvent}, +else it is 0. +?*/ +UchService* +UchEventMsg :: GetService () +{ +} + +#endif /* DOC */ + +/*?class UchGenEvtMsg +This is a sample derived class of \typ{UchEventMsg}. +It defines events that contain a pointer to a \typ{UchMessage}. +This message must be allocated dynamically because it is deleted by the destructor. + +The virtual functions \fun{ReadFrom} and \fun{WriteTo} are defined to act upon the message stored in the event. + +The following example fetches a word from the input buffer, +creates a message depending on its value +(\typ{FOO_MSG} and \typ{BAR_MSG} have been derived from \typ{UchMessage}), +and transfers the data from the buffer to the event with \fun{Get}. + +This piece of code typically appears in the body +of \fun{NewMessage}: +\begin{ccode} +UchGenEvtMsg* ev = new UchGenEvtMsg; +sword type; +if (! buf.Peek (&type)) + return; +switch (type) { + case Foo : + ev->SetMsg (new FOO_MSG); + break; + case Bar : + ev->SetMsg (new BAR_MSG); + break; + ... +} + +if (!buf.Get (ev)) + // protocol error + +PutEvent (ev); +\end{ccode} +?*/ + +#ifdef DOC + +/*?nextdoc?*/ +UchGenEvtMsg :: UchGenEvtMsg () +{ } + +/*? +Construct a generic event. The second constructor sets its message. +The message is deleted when the event is destroyed. +Thus the message must have been allocated dynamically. +?*/ +UchGenEvtMsg :: UchGenEvtMsg (UchMessage* m) +{ } + +#endif /* DOC */ + +/*?nodoc?*/ +UchGenEvtMsg :: ~UchGenEvtMsg () +{ + if (Msg) + delete Msg; +} + + +#ifdef DOC + +/*?nextdoc?*/ +void +UchGenEvtMsg :: SetMsg (UchMessage* m) +{ } + +/*? +Set and get the message associated to the event. +When setting the value, the previous message of the event (if any) is deleted. +?*/ +UchMessage* +UchGenEvtMsg :: GetMsg () +{ } + +#endif /* DOC */ + +/*?nodoc?*/ +void +UchGenEvtMsg :: ReadFrom (UchMsgBuffer& buf, lword l) +{ + if (Msg) + Msg->ReadFrom (buf, l); +} + +/*?nodoc?*/ +void +UchGenEvtMsg :: WriteTo (UchMsgBuffer& buf) +{ + if (Msg) + Msg->WriteTo (buf); +} + diff --git a/comm/OLD/Service.h b/comm/OLD/Service.h new file mode 100644 index 0000000..b97a6cf --- /dev/null +++ b/comm/OLD/Service.h @@ -0,0 +1,100 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Client side: services + * + * $Id$ + * $CurLog$ + */ + +#ifndef Service_H_ +#define Service_H_ + +#include "Message.h" +#include "MsgStream.h" +#include "error.h" +#include "cplus_bugs.h" +#include "ccu/List.h" + +class UchService; + +// events are messages sent by a server that are linked into an event list +// +class UchEventMsg : public UchMessage { +friend class UchService; + +protected: + UchMsgStream* From; + +public: + +inline UchEventMsg () : UchMessage (), From (0) { } + ~UchEventMsg (); + +inline UchMsgStream* GetSource () { return From; } +}; + + +class UchGenEvtMsg : public UchEventMsg { +protected: + UchMessage* Msg; + +public: +inline UchGenEvtMsg () : UchEventMsg () { Msg = 0; } +inline UchGenEvtMsg (UchMessage* m) : UchEventMsg () { Msg = m; } + ~UchGenEvtMsg (); + + void WriteTo (UchMsgBuffer&); + void ReadFrom (UchMsgBuffer&, lword); +inline void SetMsg (UchMessage* m) { if (Msg) delete Msg; Msg = m; } +inline UchMessage* GetMsg () { return Msg; } +}; + +// class for storing and retrieving events +// +class UchEvtMsgQueue : public CcuSmartData { + +protected: + CcuListOf Queue; + +public: + UchEvtMsgQueue (); + ~UchEvtMsgQueue (); + +inline void Put (UchEventMsg* m) { Queue.Append (m); } +inline UchEventMsg* Peek () { return Queue.First (); } +inline UchEventMsg* Get () { return Queue.RemoveFirst (); } +inline void PutBack (UchEventMsg* m) { Queue.Prepend (m); } +inline operator const CcuListOf & () const { return Queue; } +}; + +PointerClass (pUchEvtMsgQueue, UchEvtMsgQueue) + +// UchService is the client's view of a server +// we use smart pointers so that the event queue can be shared +// +class UchService : public UchMsgStream { +protected: + pUchEvtMsgQueue EvQueue; + +public: + UchService (); + UchService (UchAddress*); + UchService (const UchService&); + ~UchService (); + + UchChannel* Copy () const; + + void SetEvQueue (UchEvtMsgQueue*); + UchEventMsg* PeekEvent (bool = TRUE); + UchEventMsg* GetEvent (bool = TRUE); + void PutEvent (UchEventMsg*); + void PutBackEvent (UchEventMsg*); +}; + +#endif /* Service_H_ */ diff --git a/comm/OLD/SimpleMessage.cc b/comm/OLD/SimpleMessage.cc new file mode 100644 index 0000000..1346d22 --- /dev/null +++ b/comm/OLD/SimpleMessage.cc @@ -0,0 +1,47 @@ +#ifdef DOC +// fake entries for inline functions + +/*?class UchSimpleMessage +A simple UchMessage.has a single public field \var{Data} of type \typ{lword}. +?*/ + +/*? +Construct a simple message with data 0. +?*/ +UchSimpleMessage :: UchSimpleMessage () +{} + +/*? +Construct a simple message with data \var{d}. +?*/ +UchSimpleMessage :: UchSimpleMessage (lword d) +{} + +/*?class UchStringMessage +A string UchMessage.has a public field \var{Data} of type \typ{char*}. +It is designed to carry a null-terminated string. +?*/ + +/*? +Construct a simple message with data 0. +?*/ +UchStringMessage :: UchStringMessage () +{} + +/*? +Construct a string message with type data \var{d}. +The string is not copied. +?*/ +UchStringMessage :: UchStringMessage (char* d = 0) +{} + +/*? +Change the string of the message and set it to \var{d}. +The string is not copied. +?*/ +void +UchStringMessage :: SetData (char* d) +{} + +#endif /* DOC */ + diff --git a/comm/OLD/SimpleMessage.h b/comm/OLD/SimpleMessage.h new file mode 100644 index 0000000..b4a00ce --- /dev/null +++ b/comm/OLD/SimpleMessage.h @@ -0,0 +1,23 @@ + +class UchSimpleMessage : public UchMessage { +public: + lword Data; + + UchSimpleMessage () { Data = 0; } + UchSimpleMessage (lword d) { Data = d; } + + void WriteTo (UchMsgBuffer& b) { b << Data; } + void ReadFrom (UchMsgBuffer& b, lword) { b >> Data; } +}; + +class UchStringMessage : public UchMessage { +public: + char* Data; + + UchStringMessage () { Data = 0; } + UchStringMessage (char* d) { Data = d; } + + void WriteTo (UchMsgBuffer& b) { b << Data; } + void ReadFrom (UchMsgBuffer& b, lword l) { Data = (char*) b.Buffer (); b.Flush ((int) l); } + void SetData (char* d) { Data = d; } +}; diff --git a/comm/OLD/TextStream.cc b/comm/OLD/TextStream.cc new file mode 100644 index 0000000..a6d12d3 --- /dev/null +++ b/comm/OLD/TextStream.cc @@ -0,0 +1,1030 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Text streams + * + * $Id$ + * $CurLog$ + */ + +#include "TextStream.h" +#include "TimeOut.h" +#include "PortServer.h" +#include "Multiplexer.h" +#include +#include +#include +#include + +// list is a set of words +// the separator must be the first and last char of the string: +// e.g. :yes:no: /foo/bar/toto/ +// word is a word to be looked up in the list. +// we use the same kind of comparison as in cmpname, provided that the words +// in the list are capitalized. +// windex returns 0 if the word was not found, otherwise its index in the list. +int +windex (const char* list, const char* word) +{ + if (! list || ! word) + return 0; + char sep = *list; + if (! sep) + return 0; + register const char* p; + register const char* w = word; + int ind = 1; + for (p = list+1; *p; p++) { + if (w) { // so far the word matches + if (!*w && *p == sep) // found ! + return ind; + if (*w == *p) // still ok + w++; + else if (isupper (*p)) { + if (*w == '-' || *w == '_') + w++; + if (*p == *w || tolower (*p) == *w) + w++; + else + w = 0; + } else // not this item + w = 0; + } else { + if (*p == sep) { // start new item + ind++; + w = word; + } + } + } + return 0; +} + +// compare two names +// an uppercase letter can match the same uppercase letter, +// or an optional hyphen or underscore followed by the lowercase letter. +// if the name starts with an uppercase, it matches the lowercase +// FooBar == foo-bar, foo_bar, foobar +// returns like strcmp +int +cmpname (register const char* s1, register const char* s2) +{ + for (; *s1 && *s2; s1++, s2++) { + if (*s1 == *s2) + continue; + if (isupper (*s1)) { + if (*s2 == '-' || *s2 == '_') + s2++; + if (*s1 == *s2 || tolower (*s1) == *s2) + continue; + else + break; + } else if (isupper (*s2)) { + if (*s1 == '-' || *s1 == '_') + s1++; + if (*s2 == *s1 || tolower (*s2) == *s1) + continue; + else + break; + } else + break; + } + + if (*s1 == '\0' && *s2 == '\0') + return 0; + if (*s1 == '\0') + return -1; + if (*s2 == '\0') + return 1; + if (tolower (*s1) < tolower (*s2)) + return -1; + return 1; +} + +// sprintf a message (up to 5 args) and return the formatted string +// the address of a static string is returned +const char* +stringf (const char* fmt, const void* a1, const void* a2, const void* a3, const void* a4, const void* a5) +{ + static char msg [1024]; + sprintf (msg, fmt, a1, a2, a3, a4, a5); + return msg; +} + + +void +UchTextWord :: SetVal (const char* val) +{ + // if Sval is null, then it's an integer, whose value is in Ival + // if Sval is non null, then it's a string, whose value is in Sval + Ival = atoi (val); + if (Ival == 0 && strcmp (val, "0") != 0) + Sval = val; // word is a string: atoi returned 0 and val is not "0" + else + Sval = 0; // word is an integer +} + +const char* +UchTextWord :: GetQuotes () const +{ + if (! Sval) + return ""; /* no quotes */ + if (! *Sval) + return "\'\'"; /* empty string */ + if (index (Sval, ' ') == 0) + return ""; /* no quotes */ + if (index (Sval, '\'') == 0) + return "\'\'"; /* 'hello' */ + if (index (Sval, '\"') == 0) + return "\"\""; /* "hello" */ + return 0; /* cannot quote */ +} + + +void +UchTextLine :: NewWord (const char* s, int i) +{ + if (Num >= Max) { + if (Max == 0) { + Max = 16; + Words = new UchTextWord [Max]; + } else { + UchTextWord* old = Words; + Words = new UchTextWord [Max *= 2]; + UchTextWord* po = old; + UchTextWord* pn = Words; + for (int i = 0; i < Num; i++) + *pn++ = *po++; + delete [] old; + } + } + if (s) + Words [Num++].SetVal (s); + else + Words [Num++].SetVal (i); +} + +UchTextLine :: UchTextLine () +: Num (0), + Max (0), + Words (0) +{ +} + +UchTextLine :: ~UchTextLine () +{ + if (Words) + delete [] Words; +} + +void +UchTextLine :: AddTrailer (const UchTextLine& line, int first) +{ + for (; first < line.Num; first++) { + UchTextWord& word = line [first]; + NewWord (word, word); + } +} + +bool +UchTextLine :: Parse (char* line) +{ + enum WHERE { IN_SPACE, IN_WORD, IN_QUOTE }; + char quote = '\0'; + register char* s = line; + register char* word = line; + register WHERE where = IN_SPACE; + + // split a line into words + // a word is a sequence of non blank characters, + // or a quoted string (' or "). + // '(', ')' and ',' are also ignored to allow for function like notation + for (; *s; s++) { + switch (where) { + case IN_WORD: + if (index (" \t(),", *s)) { + *s = '\0'; + where = IN_SPACE; + AddWord (word); + } + break; + case IN_QUOTE: + if (*s == quote) { + *s = '\0'; + where = IN_SPACE; + AddWord (word); + } + break; + case IN_SPACE: + if (*s == '\'' || *s == '\"') { + quote = *s; + *s = '\0'; + where = IN_QUOTE; + word = s+1; + } else + if (index (" \t(),", *s)) { + *s = '\0'; + } else { + where = IN_WORD; + word = s; + } + break; + } + } + if (where != IN_SPACE) + AddWord (word); + if (where == IN_QUOTE) + return FALSE; + return TRUE; +} + +// decompile line into buffer +// if the line does not fit in the buffer, it ends with '...' +// the return value is dest, or a pointer to a static string "..." +// if dest or len is 0. +char* +UchTextLine :: Unparse (char* dest, int len) const +{ + UchTextWord* pa = Words; + char* p = dest; + char* lim = dest + len -1; + + if (! dest || ! len) + return "..."; + + for (int i = 0; i < Num; i++, pa++) { + // convert arg to string + const char* sa; + const char* quotes = ""; + if (pa->IsInt ()) { + char num [32]; + sprintf (num, "%d", int (*pa)); + sa = num; + } else { + sa = *pa; + quotes = pa->GetQuotes (); + if (! quotes) { + fprintf (stderr, "UchTextLine::Unparse, cannot quote word\n"); + quotes = ""; + sa = "(unquotable)"; + } + } + // try to append + if (p + strlen (sa) + strlen (quotes) < lim) { + if (*quotes) + *p++ = quotes [0]; + strcpy (p, sa); + p += strlen (p); + if (*quotes) + *p++ = quotes [1]; + } else { + if (*quotes) + *p++ = quotes [0]; + strncpy (p, sa, (lim - p) - 3); + strcpy (lim -3, "..."); + p = lim; + break; + } + // separator with next one + if (i < Num -1 && p < lim) + *p++ = ' '; + } + *p = '\0'; + return dest; +} + +// unparse the line in a buffer. +// the unparsed string is _not_ terminated by a null +// the return value is a pointer to the beginning of the string in the buffer +char* +UchTextLine :: Unparse (UchMsgBuffer* buf) const +{ + UchTextWord* pa = Words; + char* start = (char*) buf->Buffer (); + + for (int i = 0; i < Num; i++, pa++) { + // convert arg to string and append it + const char* sa; + const char* quotes = ""; + if (pa->IsInt ()) { + char num [32]; + sprintf (num, "%d", int (*pa)); + sa = num; + } else { + sa = *pa; + quotes = pa->GetQuotes (); + if (! quotes) { + fprintf (stderr, "UchTextLine::Unparse, cannot quote word <%s>\n", sa); + quotes = ""; + sa = "(unquotable)"; + } + } + if (*quotes) + buf->Append (quotes [0]); + buf->Append (sa, FALSE /*no newline*/); + if (*quotes) + buf->Append (quotes [1]); + // separator with next one + if (i < Num -1) + buf->Append (' '); + } + return start; +} + +bool +UchTextLine :: Match (const char* cmdname, const char* profile) const +{ + if (Num <= 0) + return FALSE; // no word on line + const char* myname = Words [0]; + if (! myname || cmpname (myname, cmdname) != 0) + return FALSE; // wrong command name + + // check the profile, if it is non null. + // each letter in the profile corresponds to one argument: + // 'i' for integer, 's' for string, 'b' for bool. + // the value for a boolean can be an integer (0=false, otherwise true) + // or a string: yes, y, true, t, on, no, n, false, f, off + // if the last character of the profile is '+', then the extra arguments + // are not checked. + // a null profile is identical to the profile "+". + // an empty profile ("") describes a command with no argument. + // *** we'll probably add features for repeated arguments, + // *** enums (mapping strings to ints), etc. + if (! profile) + return TRUE; + const char* p = profile; + for (int i = 1; i < Num; i++) { + switch (*p) { + case 'i': + if (! Words [i].IsInt ()) + return FALSE; + break; + case 's': + if (! Words [i].IsString ()) + return FALSE; + break; + case 'b': + { + if (Words [i].IsInt ()) + break; + const char* sval = Words [i]; + if (windex (":Yes:Y:True:T:On:", sval)) { + Words[i].SetVal (1); + break; + } else if (windex (":No:N:False:F:Off:", sval)) { + Words[i].SetVal (0); + break; + } + return FALSE; + } + case '+': + if (*(p+1)) // not at end of profile + fprintf (stderr, "UchTextLine::Match, invalid command profile: <%s>\n", profile); + return TRUE; + case '\0': + // profile shorter than arg list + return FALSE; + default: + fprintf (stderr, "UchTextLine::Match, invalid command profile: <%s>\n", profile); + + return FALSE; + } + p++; + } + if (*p) + return FALSE; // arg list shorter than profile + return TRUE; +} + +// return -1 if word is not in the line, otherwise return its index +int +UchTextLine :: Index (const char* word) const +{ + UchTextWord* w = Words; + int i = 0; + + for (; i < Num; i++, w++) + if (w->IsString () && strcmp (*w, word) == 0) + return i; + return -1; +} + +// return -1 if value is not in the line, otherwise return its index +int +UchTextLine :: Index (int value) const +{ + UchTextWord* w = Words; + int i = 0; + + for (; i < Num; i++, w++) + if (w->IsInt () && *w == value) + return i; + return -1; +} + +//---------------- 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 + +/*? +Construct a \typ{UchTextStream}. +?*/ +UchTextStream :: UchTextStream () +: UchStream (), + InBuffer (256), + OutBuffer (256), + MyMpx (0) +{ +} + +/*? +Protected destructor. +\fun{Close} must be used instead to properly close down a \typ{UchTextStream} +?*/ +UchTextStream :: ~UchTextStream () +{ + // nothing +} + +/*? +This virtual function is called by \fun{HandleRead} when an end of file +is read (the argument is then \var{TRUE}), or when an error occured while +reading (the argument is then \var{FALSE}). +The default action is to call \fun{Close}. +?*/ +void +UchTextStream :: Closing (bool) +{ + Close (); +} + +void +UchTextStream :: AddNotify (UchMultiplexer& m) +{ + MyMpx = &m; +} + +void +UchTextStream :: RemoveNotify (UchMultiplexer& m) +{ + if (MyMpx == &m) + MyMpx = 0; +} + +/*? +Read and decode input. When a complete line (terminated either by +a newline or cr-newline) is in the input buffer, it is parsed. +If parsing is successful, \fun{Execute} is called with the line as argument. +If \fun{Execute} returns \var{isCmdUnknown}, \fun{TryPredefined} is called +to decode the predefined commands. +Finally, \fun{ProcessCmdResult} is called and the line is flushed from +the input buffer. +Note that since the parsed line passed to \fun{Execute} contains pointers +to the input buffer, \fun{Execute} {\em must not} keep copies of these +pointers, but copy the contents instead. +?*/ +void +UchTextStream :: HandleRead () +{ + InBuffer.NeedSize (128); + int n = Read (InBuffer); + if (n <= 0) { + Closing (n < 0 ? FALSE : TRUE); + return; + } + + // scan buffer for complete commands + char* start = (char*) InBuffer.Buffer (); + char* stop = start + InBuffer.BufLength (); + for (char* p = start; p < stop; p++) { + if (*p == '\n') { + *p = '\0'; + // check cr-nl sequence + if (p > start && *(p-1) == '\r') + *(p-1) = '\0'; + // parse line, lookup command + UchTextLine line; + cmd_res res = isCmdUnknown; + if (line.Parse (start)) { + if (line.NumWords () == 0) + res = isCmdOk; + else + res = Execute (line); + if (res == isCmdUnknown) + res = TryPredefined (line); + } else + res = isCmdSyntax; + ProcessCmdResult (res, line); + // reset for scanning rest of buffer + start = p+1; + InBuffer.Flush ((byte*) start); + } + } +} + +/*? +Process the result \var{res} of the execution of the line \var{line}. +If \var{res} is one of \var{isCmdSyntax}, \var{isCmdUnknown}, \var{isCmdError}, +a warning message is issued. +If \var{res} is \var{isCmdClose} (resp. \var{isCmdQuit}), the virtual function +\fun{Close} (resp. \fun{Quit}) is called. +If \var{res} is \var{isCmdTerminate} (resp. \var{isCmdAbort}), +the global function \fun{MpxTerminate} (resp. \fun{MpxAbort}) is called. +If \var{res} is \var{isCmdExit}, \fun{exit(1)} is called. +Finally, if \var{res} is \var{isCmdOk}, nothing happens. +?*/ +void +UchTextStream :: ProcessCmdResult (cmd_res res, const UchTextLine& line) +{ + const char* msg = 0; + switch (res) { + case isCmdSyntax: + msg = "syntax error in <%s>"; + break; + case isCmdUnknown: + msg = "unknown command: <%s>"; + break; + case isCmdOk: + return; + case isCmdError: + msg = "error while executing command <%s>"; + break; + case isCmdClose: + // like eof (but smarter for the session) + Close (); + return; + case isCmdQuit: + //quit application + Quit (); + return; + case isCmdTerminate: + // terminate the multiplexer + if (MyMpx) + MyMpx->LoopEnd (); + return; + case isCmdAbort: + // abort the multiplexer +// MpxAbort (); + return; + case isCmdExit: + exit (1); + } + if (msg) { + char buf [64]; + line.Unparse (buf, sizeof (buf)); + fprintf (stderr, "UchTextStream::ProcessCmdResult\n", msg, buf); + } +} + +/*? +If \var{line} is one of the words +\com{Close}, \com{Quit}, \com{Terminate}, \com{Abort}, \com{Exit}, +the corresponding code is returned (\var{isCmdClose}, etc.), +otherwise, \var{isCmdUnknown} is returned. +?*/ +UchTextStream::cmd_res +UchTextStream :: TryPredefined (const UchTextLine& line) +{ + const char* cname = line [0]; + + if (line.NumWords () != 1) + return isCmdUnknown; + switch (windex (":Close:Quit:Terminate:Abort:Exit:", cname)) { + case 1: return isCmdClose; + case 2: return isCmdQuit; + case 3: return isCmdTerminate; + case 4: return isCmdAbort; + case 5: return isCmdExit; + } + return isCmdUnknown; +} + +/*? +This virtual function is called by the members that send commands on the stream. +By defaults, it writes the output buffer on the stream. +Derived classes can redefine this function. +?*/ +void +UchTextStream :: DoSend () +{ + Write (OutBuffer); +} + +/*? +This virtual function is called by \fun{ProcessCmdResult} when a \com{close} +command has been received. +It can be called directly by the application to close down the connection +(remember that the destructor is private and cannot be called directly). +By default it calls \fun{MpxRemoveChannel} to unregister this stream from the +multiplexer. +This will trigger deletion of the stream is the multiplexer holds the last +smart pointer to it. +Derived classes can redefine this function, but the redefinition +should call the default implementation. +?*/ +void +UchTextStream :: Close () +{ + MyMpx->Remove (*this); +} + +/*? +This virtual function is called by \fun{ProcessCmdResult} when a \com{quit} +command has been received. +By default is calls \fun{MpxTerminate}. +Derived classes can redefine this function if they want to terminate more +gracefully. +?*/ +void +UchTextStream :: Quit () +{ + MyMpx->LoopEnd (); +} + +//---------------- 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 (const char* s, const char* h) +: UchTextStream (), + StatusFlag (isLost), + Starter (0), + Closed (FALSE), + User (), + Service (), + Host () +{ + Init (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 +(by calling \fun{MpxAddChannel} with mode \var{IONone}) +the first time the connection is established. +?*/ +UchTextService::status +UchTextService :: Restart () +{ + UchInetAddress* addr = PortServerInquire ("portserv", Service, Host); + if (! addr) { + fprintf (stderr, "UchTextService::Restart, no address in port server\n"); + return StatusFlag = isUnavailable; + } + 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); + MyMpx->Add (this); + } + 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 (MyMpx) { + 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 () +{ + MyMpx->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 () +{ + MyMpx->SetMode (*this, IORead); + if (OutBuffer.BufLength ()) + DoSend (); +} + +/*? +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{UchTextStream::DoSend} +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 :: DoSend () +{ + if (StatusFlag == isRunning) { + Write (OutBuffer); + if (Closed) + MyMpx->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 (const char* s, const char* h) +{ + if (StatusFlag == isRunning) + return; + + Service = s; + Host = h; + + StatusFlag = Restart (); + 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) + MyMpx->Remove (*this); +} + +/*? +Remove this service from the multiplexer, whatever +the state of the connection is. +?*/ +void +UchTextService :: CloseNow () +{ + MyMpx->Remove (*this); +} + +/*? +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; +} diff --git a/comm/OLD/TextStream.h b/comm/OLD/TextStream.h new file mode 100644 index 0000000..cb43fe3 --- /dev/null +++ b/comm/OLD/TextStream.h @@ -0,0 +1,157 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Text streams + * + * $Id$ + * $CurLog$ + */ + +#ifndef TextStream_H_ +#define TextStream_H_ + +#include "cplus_bugs.h" +#include "Stream.h" +#include "MsgBuffer.h" +#include "ccu/String.h" + +class UchTextWord { +protected: + const char* Sval; + int Ival; +public: +inline UchTextWord () : Sval (0), Ival (0) {} +inline UchTextWord (const char* s) { SetVal (s); } +inline UchTextWord (int i) { SetVal (i); } + void SetVal (const char*); +inline void SetVal (int i) { Sval = 0; Ival = i; } +inline bool IsInt () const { return (Sval == 0) ? TRUE : FALSE; } +inline bool IsString () const { return (Sval != 0) ? TRUE : FALSE; } + const char* GetQuotes () const; +inline operator int () const { return Ival; } +inline operator const char* () const { return Sval; } +}; + + +class UchTextLine { +protected: + int Num; + int Max; + UchTextWord* Words; +void NewWord (const char* s, int i); + +public: + UchTextLine (); + ~UchTextLine (); +inline UchTextWord& operator [] (int i) const { return Words [i]; } +inline int NumWords () const { return Num; } +inline void AddWord (const char* s) { NewWord (s, 0); } +inline void AddWord (int i) { NewWord (0, i); } + void AddTrailer (const UchTextLine&, int); +inline UchTextLine& operator << (int i) { AddWord (i); return *this; } +inline UchTextLine& operator << (const char* s) { AddWord (s); return *this; } +inline UchTextLine& operator << (const UchTextLine& l) { AddTrailer (l, 0); return *this; } + bool Parse (char*); + char* Unparse (char* dest, int len) const; + char* Unparse (UchMsgBuffer*) const; + bool Match (const char*, const char* = 0) const; + int Index (const char*) const; + int Index (int) const; +inline bool Contains (const char* w) const { return (Index (w) == -1) ? FALSE : TRUE; } +inline bool Contains (int i) const { return (Index (i) == -1) ? FALSE : TRUE; } +inline void Reset () { Num = 0; } +}; + +class UchTextStream : public UchStream { +public: +enum cmd_res { + isCmdSyntax, // syntax error when parsing command + isCmdUnknown, // unknown command + isCmdOk, // request executed + isCmdError, // problem while executing request + isCmdClose, // close connection + isCmdQuit, // quit server + isCmdTerminate, // terminate multiplexer + isCmdAbort, // abort multiplexer + isCmdExit // call exit + }; +protected: + UchMsgBuffer InBuffer; + UchMsgBuffer OutBuffer; + UchMultiplexer* MyMpx; + +virtual void Closing (bool); + void AddNotify (UchMultiplexer&); + void RemoveNotify (UchMultiplexer&); + void HandleRead (); + void ProcessCmdResult (cmd_res, const UchTextLine&); + cmd_res TryPredefined (const UchTextLine&); +virtual cmd_res Execute (const UchTextLine&) = 0; +virtual void DoSend (); + +public: + UchTextStream (); + ~UchTextStream (); + UchTextStream (const UchTextStream&); + UchChannel* Copy () const; +inline UchMultiplexer* GetMultiplexer () const { return MyMpx; } +virtual void Close (); // 'close' request +virtual void Quit (); // 'quit' request +inline void Append (const char* l) { OutBuffer.Append (l, FALSE); } +inline void Append (const UchTextLine& l) { l.Unparse (&OutBuffer); OutBuffer.Append ('\n');} +inline UchTextStream& operator << (const char* l) { Append (l); return *this; } +inline UchTextStream& operator << (const UchTextLine& l) { Append (l); return *this; } +inline void Send () { DoSend (); } +inline void Send (const char* l) { Append (l); DoSend (); } +inline void Send (const UchTextLine& l) { Append (l); DoSend (); } +}; + +class UchTextService : public UchTextStream { +friend class UchServiceStarter; +public: +enum status { + isUnavailable, // address not found in port server + isError, // could not init connection + isRunning, // connection established + isLost, // no connection (auto-starting) + }; + +protected: + status StatusFlag; + UchServiceStarter* Starter; + bool Closed; + CcuString User; + CcuString Service; + CcuString Host; + + void Closing (bool); + status Restart (); + void AutoStart (int = -1, int = -1); +virtual void LostServer (); +virtual void GotServer (); +virtual void AbandonRestart (); + cmd_res Execute (const UchTextLine&); + void DoSend (); + +public: + UchTextService (const char*, const char* = 0); + UchTextService (); + ~UchTextService (); + void Init (const char*, const char* = 0); + status GetStatus () { return StatusFlag; } + int GetRetryTime (); + int GetMaxRetries (); + void Close (); // close after output buffer emptied + void CloseNow (); // close now + +}; + +// the simplest interface to a server. +extern bool TellServer (const char*, const char*, const char*); + +#endif /* TextStream_H_ */ diff --git a/comm/OLD/dgram.cc b/comm/OLD/dgram.cc new file mode 100644 index 0000000..d6e5c50 --- /dev/null +++ b/comm/OLD/dgram.cc @@ -0,0 +1,717 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Reliable datagrams - to be updated + * + * $Id$ + * $CurLog$ + */ + +#include "MsgBuffer.h" +#include "dgram.h" +#include "error.h" + +#include +#include +#include +#include + +// needed only if DEBUG is active +#include +#define DEBUG //**// +bool DGTrace = FALSE; + +/*?class UchDGRAM +The class \typ{UchDGRAM} is derived from \typ{UchDatagram}. +It adds to a datagram a simple protocol to ensure that all messages sent are received. +It does not prevent message duplication, neither does he ensure that the messages are +received in the order they are sent. + +Each message is sent along with a tag number. The sender keeps each message it sends +until it receives an acknowledge for it. +When a message is received, an acknowledge is sent back. +Unacknowledged messages are resent at regular intervals. +If a message is not acknowledged after a given number of retries, it is simply discarded. +?*/ + +struct PENDING { + UchMsgBuffer* outBuf; + pUchAddress toAddr; + short retries; // for pending + lword id; // for input + + PENDING (UchMsgBuffer* b, UchAddress* a, int r = 0) { outBuf = b; toAddr = a; retries = r; id = 0;} + ~PENDING () { delete outBuf; toAddr = 0; } +}; + +static void +DelPending (void* p) +{ + delete ((PENDING*) p); +} + +/*?hidden?*/ +void +UchDGRAM_TIMER :: Handle (Millisecond) +{ +if (DGTrace) printf ("*** TimeOut ***\n"); + dgram.Expired (); +} + +/*?nodoc?*/ +UchDGRAM_TIMER :: UchDGRAM_TIMER (UchDGRAM& dg) +: CcuBaseTimer (0), + dgram (dg) +{ +} + +UchDGRAM_TIMER :: ~UchDGRAM_TIMER () +{ +} + +/*?hidden?*/ +void +UchDGRAM :: Init () +{ + npending = 0; + ninput = 0; + fromAddr = 0; + retry = 5; + resend = FALSE; + locked = 0; + timeout = 1000; + sync = FALSE; +} + +/*?nextdoc?*/ +UchDGRAM :: UchDGRAM () +: pending (2), input (), timer (*this) +{ + Init (); +} + +/*? +Same constructors as for the class \typ{UchDatagram}; +?*/ +UchDGRAM :: UchDGRAM (UchAddress* b, UchAddress* c) +: UchDatagram (b, c), pending (2), input (), timer (*this) +{ + Init (); +} + +/*? +The destructor of \typ{UchDGRAM} discards all pending and input messages. +A derived class could call \fun{Drain} in its destructor to send all pending messages, +and it could call \fun{Receive} while \fun{NumInput} is non null. +?*/ +UchDGRAM :: ~UchDGRAM () +{ + timer.Stop (); + if (! npending) + return; + for (CcuIdIter iter (pending); iter (); ++iter) { + RemovePending (iter.CurId ()); + if (! npending) + break; + } + if (ninput) { + CcuListIter li (input); + while (++li) + DelPending (*li); + input.Clear (); + } +} + +static const byte DGRAM_ERR = 0; +static const byte DGRAM_SEND = 1; +static const byte DGRAM_ACK = 2; + +// GetInput reads an incoming message +// if its an ack, it is processed +// if it is a normal message, it is stored in the input list +// CheckInput calls GetInput wile there is something to read +// WaitInput returns when a message is in the input list +// Wait waits for the acknowledge of a particular id + +/*?hidden?*/ +int +UchDGRAM :: GetInput (int len) +{ + UchMsgBuffer* buf = new UchMsgBuffer (len); + int n; + while ((n = UchDatagram :: Receive (*buf)) == -1 && errno == EINTR) +DEBUG SysError (ErrWarn, "UchDGRAM::GetInput") + ; + if (n < 0) { + SysError (ErrWarn, "UchDGRAM::GetInput"); + delete buf; + return DGRAM_ERR; + } +DEBUG if (DGTrace) printf ("UchDGRAM :: GetInput : received %d bytes\n", buf->BufLength ()); + + lword id; + buf->Get (&id); + byte s; + buf->Get (&s); +DEBUG if (DGTrace) printf ("UchDGRAM :: GetInput : id %x byte %d\n", id, s); + + if (s == DGRAM_ACK) { +DEBUG if (DGTrace) printf ("UchDGRAM :: GetInput : acknowledged %x\n", id); + RemovePending (id); + delete buf; + return DGRAM_ACK; + } + + PENDING* p = new PENDING (buf, FAddr); + p->id = id; + input.Append (p); + ninput++; + return DGRAM_SEND; +} + +/*?hidden?*/ +bool +UchDGRAM :: CheckInput () +{ + // get any pending messages + for (;;) { + lword np; + if (ioctl (FilDes (), FIONREAD, (char*) &np) < 0) + return FALSE; +DEBUG if (DGTrace) if (np) printf ("UchDGRAM :: CheckInput : FIONREAD says %d\n", np); + if (np == 0) + break; + if (GetInput (int (np)) == DGRAM_ERR) + return FALSE; + } + return TRUE; +} + +/*?hidden?*/ +bool +UchDGRAM :: WaitInput () +{ + // check the input queue + if (ninput) + return TRUE; + + // check pending input + if (! CheckInput ()) + return FALSE; + + // block until something + while (! ninput) + if (GetInput (2048) == DGRAM_ERR) // *** arbitrary max size + return FALSE; + return TRUE; +} + +/*?hidden?*/ +bool +UchDGRAM :: Wait (lword id) +{ +DEBUG if (DGTrace) printf ("UchDGRAM::Wait %x\n", id); + bool ret = FALSE; + bool loop = TRUE; + Lock (); + + PENDING* pend; + while (loop) { + if (! CheckInput ()) + break; + + // did we receive the ack ? + pend = (PENDING*) pending.Get (id); + if (! pend) { + ret = TRUE; + break; + } + + if (resend) { +DEBUG if (DGTrace) printf ("UchDGRAM::Wait : resending\n"); + if (pend->retries <= 0) { + RemovePending (id); + break; + } + + int n = SendBuffer (* pend->outBuf, *pend->toAddr); + if (n < 0) + break; + --pend->retries; + resend = FALSE; + } else +DEBUG if (DGTrace) printf ("UchDGRAM::Wait : waiting\n"), + timer.Wait (); // sets resend to TRUE + } + Unlock (); +DEBUG if (DGTrace) printf ("UchDGRAM::Wait : %s\n", ret ? "done" : "failed"); + return ret; +} + +// SendAck sends an acknowledge +// Expired is called when the resend timer has expired +// PrepareToSend returns a buffer with the header set, +// and adds it to the pending set +// SendBuffer sends a buffer, handling interrupted sys calls +// RemovePending removes a pending message from the table +/*?hidden?*/ +void +UchDGRAM :: SendAck (lword id, UchAddress& addr) +{ + UchMsgBuffer buf; + buf.Append (id); + buf.Append (DGRAM_ACK); + SendBuffer (buf, addr); + fromAddr = & addr; +DEBUG if (DGTrace) printf ("UchDGRAM :: SendAck : acknowledging %x\n", id); +} + +/*?hidden?*/ +void +UchDGRAM :: Expired () +{ + if (locked) + resend = TRUE; + else + Resend (); +} + +/*?hidden?*/ +UchMsgBuffer* +UchDGRAM :: PrepareToSend (UchAddress& to, int retries) +{ + UchMsgBuffer* obuf = new UchMsgBuffer; +/* +UchInetAddress* ia = (UchInetAddress*) &to; +UchAddress* toCopy = new UchInetAddress (ia->Host (), ia->Port ()); +*/ + if (retries == 0) + retries = retry; + PENDING* out = new PENDING (obuf, &to /*toCopy*/, retries); + outId = pending.Store (out); + +DEBUG if (DGTrace) printf ("UchDGRAM :: PrepareToSend : id %x\n", outId); + obuf->Append (outId); + obuf->Append (DGRAM_SEND); + + if (! npending) { + timer.ChangePeriod (timeout); + timer.Restart (); + } + npending++; + + return obuf; +} + +/*?hidden?*/ +int +UchDGRAM :: SendBuffer (UchMsgBuffer& buf, UchAddress& addr) +{ + int n; + while ((n = UchDatagram :: Send (buf, addr, TRUE)) == -1 && errno == EINTR) +DEBUG SysError (ErrWarn, "UchDGRAM::SendBuffer") + ; + return n; +} + +/*?hidden?*/ +void +UchDGRAM :: RemovePending (lword id) +{ + PENDING* pend = (PENDING*) pending.Get (id); + if (! pend) { + Error (ErrWarn, "Receive", "unrecognized ACK"); + return; + } + pending.Remove (id); + delete pend; + npending--; + if (npending == 0) + timer.Stop (); +} + +//---------------- the public functions + +/*?nextdoc?*/ +int +UchDGRAM :: Send (byte* buf, int len, UchAddress& to, bool ack, int retries) +{ +DEBUG if (DGTrace) printf ("UchDGRAM :: Send\n"); + Lock (); + UchMsgBuffer* obuf = PrepareToSend (to, retries); + obuf->Append (buf, len); + int n = SendBuffer (*obuf, to); + CheckInput (); + int ret = (ack || sync) ? (Wait (outId) ? n : -1) : n; + Unlock (); + return ret; +} + +/*?nextdoc?*/ +int +UchDGRAM :: Receive (byte* buf, int len) +{ +DEBUG if (DGTrace) printf ("UchDGRAM :: Receive\n"); + if (! WaitInput ()) + return -1; + PENDING* p = (PENDING*) input.RemoveFirst (); + ninput--; + int n = p->outBuf->BufLength (); + if (len < n) + n = len; + memcpy (buf, p->outBuf->Buffer (), n); + SendAck (p->id, *p->toAddr); + delete p; +DEBUG if (DGTrace) printf ("UchDGRAM :: Receive : received %d bytes\n", n); + return n; +} + +/*?nextdoc?*/ +int +UchDGRAM :: Reply (byte* buf, int len, bool ack, int retries) +{ + if (! fromAddr) + return -1; + return Send (buf, len, *fromAddr, ack, retries); +} + +/*?nextdoc?*/ +int +UchDGRAM :: Send (UchMsgBuffer& buf, UchAddress& to, bool peek, bool ack, int retries) +{ +DEBUG if (DGTrace) printf ("UchDGRAM :: Send\n"); + Lock (); + UchMsgBuffer* obuf = PrepareToSend (to, retries); + obuf->Append (buf.Buffer (), buf.BufLength ()); + if (! peek) + buf.Flush (); + int n = SendBuffer (*obuf, to); + CheckInput (); + int ret = (ack || sync) ? (Wait (outId) ? n : -1) : n; + Unlock (); + return ret; +} + +/*?nextdoc?*/ +int +UchDGRAM :: Receive (UchMsgBuffer& buf) +{ +DEBUG if (DGTrace) printf ("UchDGRAM :: Receive\n"); + if (! WaitInput ()) + return -1; + PENDING* p = (PENDING*) input.RemoveFirst (); + ninput--; + int n = p->outBuf->BufLength (); + buf.Append (p->outBuf->Buffer (), n); + SendAck (p->id, *p->toAddr); + delete p; +DEBUG if (DGTrace) printf ("UchDGRAM :: Receive : received buffer %d bytes\n", n); + return n; +} + +/*? +These functions are similar to the same functions in the class \typ{UchDatagram}, +except that they manage messages acknowledgement. +\fun{Send} saves the message in a buffer so that it can be resent if no acknowledge +is received. +\fun{Receive} acknowledges the received message. +\fun{Reply} sends the message to the sender of the last received message. +If \var{ack} is TRUE, the acknowledge of the message is waited for. +In that case, \fun{Send} returns -1 if the acknowledge is not received. +If \var{retries} is non zero, it specifies a number of retries for this message +different from the default retry number of this dgram. +?*/ +int +UchDGRAM :: Reply (UchMsgBuffer& buf, bool peek, bool ack, int retries) +{ + if (! fromAddr) + return -1; + return Send (buf, *fromAddr, peek, ack, retries); +} + +/*?nextdoc?*/ +bool +UchDGRAM :: Send (UchMessage& msg, UchAddress& to, bool ack, int retries) +{ +DEBUG if (DGTrace) printf ("UchDGRAM :: Send message\n"); + Lock (); + UchMsgBuffer* obuf = PrepareToSend (to, retries); + obuf->Append (msg); +// int l; +// printf ("outBuffer is %d bytes long\n", l = obuf->BufLength ()); +// for (int i = 0; i < l; i++) printf ("%02x ", obuf->Buffer () [i]); +// printf ("\n"); + int n = SendBuffer (*obuf, to); + CheckInput (); + bool ret; + if (ack || sync) + ret = Wait (outId) ? TRUE : FALSE; + else + ret = bool (n != obuf->BufLength ()); + Unlock (); + return ret; +} + +/*?nextdoc?*/ +bool +UchDGRAM :: Receive (UchMessage* msg) +{ +DEBUG if (DGTrace) printf ("UchDGRAM :: Receive message\n"); + if (! WaitInput ()) + return FALSE; + PENDING* p = (PENDING*) input.RemoveFirst (); + ninput--; +// int n; +// printf ("inBuffer is %d bytes long\n", n = p->outBuf->BufLength ()); +// for (int i = 0; i < n; i++) printf ("%02x ", p->outBuf->Buffer () [i]); +// printf ("\n"); + if (! p->outBuf->Get (msg)) + return FALSE; + SendAck (p->id, *p->toAddr); + delete p; +DEBUG if (DGTrace) printf ("UchDGRAM :: Receive : received message\n"); + return TRUE; +} + +/*? +These functions are similar to the previous functions except that they take +a \typ{UchMessage} as argument. +\fun{Send} converts the message in the output buffer. +If \var{ack} is TRUE, the acknowledge of the message is waited for. +In that case, \fun{Send} returns FALSE if the acknowledge is not received. +If \var{retries} is non zero, it specifies a number of retries for this message +different from the default retry number of this dgram. +\fun{Receive} converts the incoming data into the message passed as argument. +\fun{Reply} sends the message to the sender of the last received message. +See also the function \fun{NewMessage} to handle incoming messages. +?*/ +bool +UchDGRAM :: Reply (UchMessage& msg, bool ack, int retries) +{ + if (! fromAddr) + return FALSE; + return Send (msg, *fromAddr, ack, retries); +} + +//----------------------- +#if 0 +bool +UchDGRAM :: Ask (UchMessage& msg, UchAddress& to) +{ +DEBUG if (DGTrace) printf ("UchDGRAM :: Ask\n"); + Lock (); + UchMsgBuffer* obuf = PrepareToSend (to, DGRAM_ASK *******); + obuf->Append (msg); + int n = SendBuffer (*obuf, to); + CheckInput (); + bool ret = Wait (outId) ? TRUE : FALSE; + if (ret) { + // wait answer + for (;;) { + if (! WaitInput ()) + continue; + PENDING* pend = (PENDING*) input.Last (); + if (pend->toAddr == to............. + } + } + Unlock (); + return ret; +} +#endif +//----------------------- + +/*? +Resend all messages for which no acknowledge has been received. +If a UchMessage.has been resent more than the retry number, it is discarded. +When successive messages are to be sent to the same address, only the first +is resent, to avoid connection overflow. +?*/ +void +UchDGRAM :: Resend () +{ +if (DGTrace) printf ("UchDGRAM::Resend\n"); + Lock (); + if (npending) + CheckInput (); + if (! npending) { + resend = FALSE; + Unlock (); + return; + } + + UchAddress* addr = 0; + + PENDING* pend; + for (CcuIdIter iter (pending); pend = (PENDING*) iter (); ++iter) { + if (pend->retries <= 0) { + // the output buffer contains the leading id and type + UchMsgBuffer fake (*pend->outBuf); + lword id; + byte typ; + fake.Get (&id); + fake.Get (&typ); + if (! DiscardNotify (fake, *pend->toAddr)) { + pend->retries = retry; // *** should be controllable ? + continue; + } +if (DGTrace) printf ("UchDGRAM::Resend : abandonning %x\n", iter.CurId ()); + RemovePending (iter.CurId ()); + if (! npending) + break; + } else + if (addr != (UchAddress*) pend->toAddr) { +DEBUG printf ("UchDGRAM::Resend : resending %x\n", iter.CurId ()); + SendBuffer (* pend->outBuf, *pend->toAddr); + --pend->retries; + addr = pend->toAddr; + CheckInput (); + } +DEBUG else printf ("UchDGRAM::Resend : skipping %x\n", iter.CurId ()); + } + + resend = FALSE; + Unlock (); +} + +/*? +This virtual function is called whenever a pending message is about +to be discarded because no acknowledge has been received after +the the default number of retries. +If it returns FALSE, the message is not discarded and its retry count is reset to zero. +Returning FALSE is not very social. +The default action is to return TRUE, thus discarding the pending message. +?*/ +bool +UchDGRAM :: DiscardNotify (UchMsgBuffer&, UchAddress&) +{ + return TRUE; +} + +/*? +Set the interval between retries. +The default value is 1000 (1 second). +?*/ +void +UchDGRAM :: SetRetryTime (Millisecond m) +{ + timeout = m; + if (npending) { + timer.ChangePeriod (m); + timer.Restart (); + } +} + +/*? +Wait until all acknowledges have been received, +or until all messages have been discarded. +?*/ +void +UchDGRAM :: Drain () +{ + if (npending) + CheckInput (); + while (npending) + timer.Wait (); +} + +/*? +This virtual function is called by \fun{HandleRead}. +It must be redefined in a derived class if \fun{HandleRead} is to be used +(for instance if this \typ{UchDGRAM} is put in a channel set). +This function should convert the contents of the buffer into a message, and handle it. +It should return TRUE if the message was correctly handled, else FALSE. +Note that if it returns FALSE, the message will not be acknowledged, and thus it will +be resent later. +The default behaviour of this virtual function is to issue an error message and +to return TRUE. +?*/ +bool +UchDGRAM :: NewMessage (UchMsgBuffer&) +{ + Error (ErrWarn, "UchDGRAM :: NewMessage", "should be defined in derived class"); + return TRUE; +} + +/*? +This is an instance of the virtual function \fun{HandleRead} of class \typ{UchChannel}. +It calls the virtual function \fun{NewMessage} when a message is received. +If \fun{NewMessage} returns TRUE, an acknowledge is sent back. +This functions also handles incoming acknowledges. +?*/ +void +UchDGRAM :: HandleRead () +{ + if (! CheckInput ()) + return; + if (! ninput) + return; + +DEBUG if (DGTrace) printf (">>UchDGRAM :: HandleRead\n"); + PENDING* p = (PENDING*) input.RemoveFirst (); + ninput--; + if (NewMessage (* p->outBuf)) + SendAck (p->id, *p->toAddr); +DEBUG if (DGTrace) printf ("<>UchDGRAM :: HandleSelect\n"); + PENDING* p = (PENDING*) input.RemoveFirst (); + ninput--; + if (NewMessage (* p->outBuf)) + SendAck (p->id, *p->toAddr); +DEBUG if (DGTrace) printf ("< +#include +#include +#include +#include +#include +#include + +//---------------- globals +// if SaveTime > 0, the state is saved to SaveFile +// SaveTime seconds after a modification +// if SaveTime < 0, saving is immediate +// if SaveTime is 0, no save +// if SaveFileIsFd >= 0, it is taken as output file descr instead of SaveFile +const char* SaveFile = 0; +int SaveFileIsFd = -1; +int SaveTime = 0; +int Mod = 0; +int NbEntries = 0; +bool Trace = FALSE; +const char* LoadFile; + +//---------------- constants +// if an auto-launch server has not registered for REG_TIME seconds +// and somebody inquires its address, it is killed and restarted. +#define REG_TIME 60000 +#define REG_STR "minute" + +// if an auto-launch server has been run within the last ALR_TIME seconds +// and has not yet registered and somebody inquires its address, +// is is _not_ run. +#define ALR_TIME 30000 +#define ALR_STR "30 seconds" + +struct Serv { + CcuString Key; + lword Host; + sword Port; + int Ident; + // auto-launch stuff + bool AutoLaunch; + bool Running; + Millisecond LastRun; + Millisecond LastRegistered; + char* CmdLine; // the line + char** RunCmd; // array to pointers to CmdLine (for execvp) +}; + +#ifndef CPLUS_BUG19 +CcuListOf ServList; +#else +CcuList ServList; +#endif + +//---------------- auto-launch stuff +char** +Parse (char* line) +{ + enum WHERE { IN_SPACE, IN_WORD, IN_QUOTE }; + char quote = '\0'; + register char* s = line; + register char* word = line; + register WHERE where = IN_SPACE; + char* words [256]; + int nw = 0; + + // split a line into words + // a word is a sequence of non blank characters, + // or a quoted string (' or "). + // '(', ')' and ',' are also ignored to allow for function like notation + for (; *s; s++) { + switch (where) { + case IN_WORD: + if (index (" \t(),", *s)) { + *s = '\0'; + where = IN_SPACE; + if (nw < 255) + words [nw++] = word; + else + return 0; + } + break; + case IN_QUOTE: + if (*s == quote) { + *s = '\0'; + where = IN_SPACE; + if (nw < 255) + words [nw++] = word; + else + return 0; + } + break; + case IN_SPACE: + if (*s == '\'' || *s == '\"') { + quote = *s; + *s = '\0'; + where = IN_QUOTE; + word = s+1; + } else + if (index (" \t(),", *s)) { + *s = '\0'; + } else { + where = IN_WORD; + word = s; + } + break; + } + } + if (where != IN_SPACE) + if (nw < 255) + words [nw++] = word; + else + return 0; + if (where == IN_QUOTE) + return 0; + + // if no argv specified, argv[0] = executable + if (nw == 3) + words [nw++] = words [2]; + // expected format: key dir exec argv + if (nw < 4) + return 0; + // null terminated array for execvp + words [nw++] = 0; + + // return a freshly allocated array + char** nwords = new char* [nw]; + for (int i = 0; i < nw; i++) + nwords [i] = words [i]; + return nwords; +} + +void +AutoLaunch (Serv* s) +{ + // s->RunCmd [0] service name + // s->RunCmd [1] directory + // s->RunCmd [2] executable + // s->RunCmd [3 ... ] argv [0 ...] + + CcuTimeStamp now; + if (s->LastRun + ALR_TIME > now) { +// Log ("AutoLaunch", "%s already launched in the past %s", s->RunCmd [0], ALR_STR); + return; + } +// Log ("AutoLaunch", "running %s dir=%s, exec=%s", s->RunCmd[0], s->RunCmd[1], s->RunCmd[2]); + if (! fork ()) { + // close everything but stdio + for (int i = 3; i < NFILE; i++) + close (i); + // detach from controlling terminal + setsid (); + // change directory + if (chdir (s->RunCmd [1]) < 0) + SysError (ErrFatal, s->RunCmd [1]); + // exec server + if (execvp (s->RunCmd [2], s->RunCmd + 3) < 0) + SysError (ErrFatal, s->RunCmd [2]); + } + s->LastRun = now; +} + +void +LoadConfig () +{ + char* l = getlogin (); + if (! l) + l = cuserid (0); + if (! l) + Error (ErrFatal, "LoadConfig", "getlogin failed"); + + FILE* f = fopen (LoadFile, "r"); + if (!f) + SysError (ErrFatal, LoadFile); + char line [2048]; + while (fgets (line, sizeof (line), f)) { + // empty line or comment + if (! line [0] || line [0] == '#') + continue; + + // expected format: servername dir exec argv0 ... + + // remove trailing newline and parse + line [strlen (line) -1] = 0; + char* nline = new char [strlen (line) + 1]; + strcpy (nline, line); + char** words = Parse (nline); + if (! words) { + Error (ErrWarn, "LoadConfig", "incorrect line ignored in file"); + delete nline; + continue; + } + Serv* s = new Serv; + char key [256]; // portserv:mbl:audioserver + sprintf (key, "portserv:%s:%s", l, words [0]); + s->Key = key; + s->Host = 0; + s->Port = 0; + s->Ident = 0; + s->AutoLaunch = TRUE; + s->Running = FALSE; + s->LastRun= 0; + s->LastRegistered = 0; + s->CmdLine = nline; + s->RunCmd = words; + ServList.Append (s); +// Log ("LoadConfig", "defined auto-launch server %s dir=%s exec=%s", +// s->RunCmd [0], s->RunCmd [1], s->RunCmd [2]); + } + fclose (f); +} + +void +KillProcesses () +{ + // kill all processes that have been autolaunched, + // assuming that their id is their pid. + Serv* s; +#ifndef CPLUS_BUG19 + CcuListIterOf iter (ServList); + while (s = *++iter) { +#else + CcuListIter iter (ServList); + while (s = (Serv*) *++iter) { +#endif + if (s->AutoLaunch && s->Ident) { +// Log ("KillProcesses", "killing %s pid=%d", s->RunCmd [2], (void*) s->Ident); + kill (s->Ident, SIGTERM); + } + } +} + +Serv* +Register (const char* key, lword host, sword port, int id) +{ + Serv* s = 0; + +#ifndef CPLUS_BUG19 + CcuListIterOf iter (ServList); + while (s = *++iter) { +#else + CcuListIter iter (ServList); + while (s = (Serv*) *++iter) { +#endif + if (strcmp (key, s->Key) == 0) + break; + } + if (! s) { + s = new Serv; + s->Key = key; + s->AutoLaunch = FALSE; + s->LastRun = 0; + s->CmdLine = 0; + s->RunCmd = 0; + ServList.Append (s); + } + s->Host = host; + s->Port = port; + s->Ident = id; + CcuTimeStamp now; + s->LastRegistered = now; + s->Running = TRUE; + return s; +} + +bool +Remove (const char* key, lword host, sword port, int id) +{ + Serv*s; +#ifndef CPLUS_BUG19 + CcuListIterOf iter (ServList); + CcuListIterOf trailing_iter (ServList); +#else + CcuListIter iter (ServList); + CcuListIter trailing_iter (ServList); +#endif + while (++iter) { +#ifndef CPLUS_BUG19 + s = *iter; +#else + s = (Serv*) *iter; +#endif + if (host == s->Host && port == s->Port && id == s->Ident && strcmp (key, s->Key) == 0) { + if (s->AutoLaunch) { + s->Running = FALSE; + s->LastRun = 0; + s->LastRegistered = 0; + s->Host = 0; + s->Port = 0; + s->Ident = 0; + } else { + s->Key = 0; + delete s; + ServList.RemoveAfter (trailing_iter); + } + return TRUE; + } + ++trailing_iter; + } + Error (ErrWarn, key, "not found for remove"); + return FALSE; +} + +Serv* +Inquire (const char* key) +{ + Serv*s; +#ifndef CPLUS_BUG19 + CcuListIterOf iter (ServList); +#else + CcuListIter iter (ServList); +#endif + while (++iter) { +#ifndef CPLUS_BUG19 + s = *iter; +#else + s = (Serv*) *iter; +#endif + + if (strcmp (key, s->Key) != 0) + continue; + + if (!s->AutoLaunch) + return s; + + if (s->Running) { + CcuTimeStamp now; + if (s->LastRegistered + REG_TIME < now) { +// Log ("Inquire", "%s not registered for the last %s: autolaunching it", s->RunCmd [0], REG_STR); + if (s->Ident) + kill (s->Ident, SIGTERM); + s->Host = 0; + s->Port = 0; + s->Ident = 0; + AutoLaunch (s); + } + } else + AutoLaunch (s); + return s; + } + Error (ErrWarn, key, "not found"); + return 0; +} + + +void +Match (const char* key, UchDatagram& portserv) +{ + UchMsgBuffer buf (256); + UchPortServerReq ans (PortServMatch); + int nb = 0; + + CcuRegExp re (key); + if (re.Compile ()) { + Serv*s; +#ifndef CPLUS_BUG19 + CcuListIterOf iter (ServList); + while (++iter) { + s = *iter; +#else + CcuListIter iter (ServList); + while (++iter) { + s = (Serv*) *iter; +#endif + if (re.Match (s->Key)) { +if (Trace) printf ("Match: %s\n", (const char*) s->Key); + ans.Host = s->Host; + ans.Port = s->Port; + ans.SetKey (s->Key); + buf.Append (ans); + portserv.Reply (buf); + nb++; + } + } + } else + nb = -1; + ans.Type = PortServEndMatch; + ans.Ident = nb; + ans.SetKey (0); + buf.Append (ans); + portserv.Reply (buf); +} + +int +Dump () +{ + Serv*s; +#ifndef CPLUS_BUG19 + CcuListIterOf iter (ServList); +#else + CcuListIter iter (ServList); +#endif + while (++iter) { +#ifndef CPLUS_BUG19 + s = *iter; +#else + s = (Serv*) *iter; +#endif + printf ("<%s> 0x%lx:%d (%d)\n", (const char*) s->Key, s->Host, s->Port, s->Ident); + } + return 0; +} + +int +DumpFile () +{ +if (Trace) printf ("dumping\n"); + int fd = SaveFileIsFd; + if (fd < 0 && (fd = creat (SaveFile, 0600)) < 0) { + SysError (ErrWarn, SaveFile); + return 0; + } + UchFilDes Fd (fd); + char buf [256]; + Serv*s; +#ifndef CPLUS_BUG19 + CcuListIterOf iter (ServList); +#else + CcuListIter iter (ServList); +#endif + while (++iter) { +#ifndef CPLUS_BUG19 + s = *iter; +#else + s = (Serv*) *iter; +#endif + sprintf (buf, "0x%lx:%d %s\n", s->Host, s->Port, (const char*) s->Key); + Fd.Write ((byte*) buf, strlen (buf)); + } + Mod = 0; + return 1; +} + +// for handlers +// +void +SigDump (int) +{ + Dump (); +} + +void +SigDumpFile (int) +{ + DumpFile (); +} + +void +Modified () +{ + if (! Mod && SaveTime > 0) { + Mod++; + alarm (SaveTime); + return; + } + if (SaveTime < 0) { + Mod++; + DumpFile (); + } +} + +void +Usage () +{ + Error (ErrUsage, "portserv", "[-t] [-s service] [-quit] [-save file [time]] [-load file]"); +} + +main (int argc, const char** argv) +{ + const char* servname = "portserv"; + int running = 1; + bool qflag = FALSE; + + ProgramName (argv [0]); + while (--argc) { + ++argv; + if (strcmp (*argv, "-quit") == 0) { + qflag = TRUE; + } else + if (strcmp (*argv, "-t") == 0) { + Trace = TRUE; + } else + if (strcmp (*argv, "-s") == 0) { + if (--argc < 1) + Usage (); + servname = *++argv; + } else + if (strcmp (*argv, "-save") == 0) { + if (--argc < 1) + Usage (); + SaveFile = *++argv; + if (strcmp (SaveFile, "-") == 0) + SaveFileIsFd = 1; + else if (strcmp (SaveFile, "--") == 0) + SaveFileIsFd = 2; + + if (argc > 1 && argv [1][0] != '-') { + SaveTime = atoi (*++argv); + --argc; + } else + SaveTime = -1; + } else if (strcmp (*argv, "-load") == 0 || strcmp (*argv, "-l") == 0) { + if (--argc < 1) + Usage (); + LoadFile = *++argv; + } else + Usage (); + } + + // special case: portserv -quit + // send a PortServQuit message to + // the portserver that is running + if (qflag) { + if (SaveFile || LoadFile) + Error (ErrFatal, "-quit", "incompatible with -save or -load"); + UchPortServer ps (servname, 0); + ps.Quit (); + exit (0); + } + + sword servport; + + // get service name + // + struct servent* service = getservbyname (servname, 0); + if (service) + servport = ntohs (service->s_port); + else { + const char* portno = getenv (servname); + if (portno) { + servport = atoi (portno); + } else + Error (ErrFatal, servname, "service not available"); + } + + // load file + // + if (LoadFile) + LoadConfig (); + + // open dgram socket + // +if (Trace) printf ("opening\n"); + UchDatagram portserv (new UchInetAddress (ANYADDR, servport), 0); + if (! portserv.Setup ()) +// Error (ErrFatal, "main", "Setup failed"); + SysError (ErrFatal, "main"); + + signal (SIGHUP, SigDump); + if (SaveTime > 0) + signal (SIGALRM, SigDumpFile); + + // handle requests + // + UchMsgBuffer buf (256); + UchPortServerReq req; + +if (Trace) printf ("looping\n"); + while (running) { + buf.Flush (); + int n = portserv.Receive (buf); + if (n <= 0) { + SysError (ErrFatal, "Receive", EINTR); + continue; + } +if (Trace) printf ("received\n"); + if (! buf.Get (&req)) { + Error (ErrWarn, "Receive", "could not read message"); + continue; + } + + // respond + switch (req.Type) { + case PortServRegister : +if (Trace) printf ("register\n"); + Register (req.Key, req.Host, req.Port, int (req.Ident)); + Modified (); + break; + + case PortServRemove : +if (Trace) printf ("remove\n"); + Remove (req.Key, req.Host, req.Port, int (req.Ident)); + Modified (); + break; + + case PortServInquire : + { +if (Trace) printf ("inquire\n"); + UchPortServerReq ans; + Serv* inq = Inquire (req.Key); + if (inq) { + ans.Type = PortServAnswer; + ans.Host = inq->Host; + ans.Port = inq->Port; + } else { + ans.Type = PortServFail; + ans.Host = 0; + ans.Port = 0; + } + buf.Flush (); + buf.Append (ans); + portserv.Reply (buf); + } + break; + + case PortServMatch : +if (Trace) printf ("match\n"); + Match (req.Key, portserv); + break; + + case PortServDump : +if (Trace) printf ("dump\n"); + Dump (); + break; + + case PortServQuit : +if (Trace) printf ("quit\n"); + if (SaveTime) + DumpFile (); + KillProcesses (); + running = 0; ////////dangerous + break; + + default: +if (Trace) printf ("unknown\n"); + Error (ErrWarn, "Receive", "unknown request"); + } + } +} + diff --git a/comm/OLD/porttest.cc b/comm/OLD/porttest.cc new file mode 100644 index 0000000..c73268d --- /dev/null +++ b/comm/OLD/porttest.cc @@ -0,0 +1,72 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Port server: test program + * + * $Id$ + * $CurLog$ + */ + +#include "PortServer.h" + +#include +#include +#include + +int +fMatch (const char* match, lword host, sword port, const char*) +{ + printf ("0x%lx:%d -> %s\n", host, port, match); + return 1; +} + +main (int argc, char **argv) +{ + char *host = 0; + char *service = "portserv"; + + if (argc > 1 && argv [1] [0] == ':') { + host = argv [1] + 1; + argc--, argv++; + } + if (argc > 1 && argv [1] [0] == '=') { + service = argv [1] + 1; + argc--, argv++; + } + UchPortServer ps (service, host); + + switch (argc) { + case 1: + ps.Dump (); + break; + case 2: + if (strcmp (argv [1], "quit") == 0) + ps.Quit (); + else + if (argv [1] [0] == '?') + ps.Match (argv [1] + 1, fMatch); + else { + UchInetAddress* addr = ps.Inquire (argv [1]); + if (addr) { + printf ("0x%lx:%d\n", addr->Host (), addr->Port ()); + delete addr; + } else + printf ("not found\n"); + } + break; + case 3: { + UchInetAddress addr ("", atoi (argv [2])); + if (argv [1] [0] == '-') + ps.Remove (argv [1] + 1, addr, 987); + else + ps.Register (argv [1], addr, 987); + break; + } + } +} + diff --git a/comm/OLD/reqgen.cc b/comm/OLD/reqgen.cc new file mode 100644 index 0000000..648cc83 --- /dev/null +++ b/comm/OLD/reqgen.cc @@ -0,0 +1,34 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1993 + * Centre d'Etudes de la Navigation Aerienne (CENA) + * + * Request management, by Stephane Chatty + * + * $Id$ + * $CurLog$ + */ + +#include "ReqMgr.h" +#include + +static void +usage () +{ + fprintf (stderr, "usage: reqgen file\n"); +} + +main (int argc, const char** argv) +{ + if (argc !=2) { + usage (); + return 0; + } + UchReqMgr r; + r.Read (argv [1]); + r.DumpHeader ("reqgen.out.h"); + r.DumpSource ("reqgen.out"); +} \ No newline at end of file -- cgit v1.1