diff options
author | chatty | 1993-04-07 11:50:31 +0000 |
---|---|---|
committer | chatty | 1993-04-07 11:50:31 +0000 |
commit | ba066c34dde204aa192d03a23a81356374d93731 (patch) | |
tree | 39391f6235d2cf8a59a0634ac5ea430cdd21f5d4 /comm/OLD/PortServer.cc | |
parent | 05ab076e1c2a9ca16472f9a6b47b8d22914b3783 (diff) | |
download | ivy-league-ba066c34dde204aa192d03a23a81356374d93731.zip ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.gz ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.bz2 ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.xz |
Initial revision
Diffstat (limited to 'comm/OLD/PortServer.cc')
-rw-r--r-- | comm/OLD/PortServer.cc | 410 |
1 files changed, 410 insertions, 0 deletions
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 <stdlib.h> +#include <string.h> +// sometimes, cuserid is declared in stdio.h ... +#include <stdio.h> +#include <netdb.h> + +/*?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); +} + + |