summaryrefslogtreecommitdiff
path: root/comm/OLD/PortServer.cc
diff options
context:
space:
mode:
authorchatty1993-04-07 11:50:31 +0000
committerchatty1993-04-07 11:50:31 +0000
commitba066c34dde204aa192d03a23a81356374d93731 (patch)
tree39391f6235d2cf8a59a0634ac5ea430cdd21f5d4 /comm/OLD/PortServer.cc
parent05ab076e1c2a9ca16472f9a6b47b8d22914b3783 (diff)
downloadivy-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.cc410
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);
+}
+
+