diff options
Diffstat (limited to 'src/global/clnt_stream.c')
-rw-r--r-- | src/global/clnt_stream.c | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/src/global/clnt_stream.c b/src/global/clnt_stream.c new file mode 100644 index 0000000..ca3ce4f --- /dev/null +++ b/src/global/clnt_stream.c @@ -0,0 +1,278 @@ +/*++ +/* NAME +/* clnt_stream 3 +/* SUMMARY +/* client endpoint maintenance +/* SYNOPSIS +/* #include <clnt_stream.h> +/* +/* typedef void (*CLNT_STREAM_HANDSHAKE_FN)(VSTREAM *) +/* +/* CLNT_STREAM *clnt_stream_create(class, service, timeout, ttl, +/* handshake) +/* const char *class; +/* const char *service; +/* int timeout; +/* int ttl; +/* CLNT_STREAM_HANDSHAKE_FN *handshake; +/* +/* VSTREAM *clnt_stream_access(clnt_stream) +/* CLNT_STREAM *clnt_stream; +/* +/* void clnt_stream_recover(clnt_stream) +/* CLNT_STREAM *clnt_stream; +/* +/* void clnt_stream_free(clnt_stream) +/* CLNT_STREAM *clnt_stream; +/* DESCRIPTION +/* This module maintains local IPC client endpoints that automatically +/* disconnect after a being idle for a configurable amount of time, +/* that disconnect after a configurable time to live, +/* and that transparently handle most server-initiated disconnects. +/* Server disconnect is detected by read-selecting the client endpoint. +/* The code assumes that the server has disconnected when the endpoint +/* becomes readable. +/* +/* clnt_stream_create() instantiates a client endpoint. +/* +/* clnt_stream_access() returns an open stream to the service specified +/* to clnt_stream_create(). The stream instance may change between calls. +/* This function returns null when the handshake function returned an +/* error. +/* +/* clnt_stream_recover() recovers from a server-initiated disconnect +/* that happened in the middle of an I/O operation. +/* +/* clnt_stream_free() destroys of the specified client endpoint. +/* +/* Arguments: +/* .IP class +/* The service class, private or public. +/* .IP service +/* The service endpoint name. The name is limited to local IPC +/* over sockets or equivalent. +/* .IP timeout +/* Idle time after which the client disconnects. +/* .IP ttl +/* Upper bound on the time that a connection is allowed to persist. +/* .IP handshake +/* Null pointer, or pointer to function that will be called +/* at the start of a new connection and that returns 0 in case +/* of success. +/* DIAGNOSTICS +/* Warnings: communication failure. Fatal error: mail system is down, +/* out of memory. +/* SEE ALSO +/* mail_proto(3h) low-level mail component glue. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstream.h> +#include <events.h> +#include <iostuff.h> + +/* Global library. */ + +#include "mail_proto.h" +#include "mail_params.h" +#include "clnt_stream.h" + +/* Application-specific. */ + + /* + * CLNT_STREAM is an opaque structure. None of the access methods can easily + * be implemented as a macro, and access is not performance critical anyway. + */ +struct CLNT_STREAM { + VSTREAM *vstream; /* buffered I/O */ + int timeout; /* time before client disconnect */ + int ttl; /* time before client disconnect */ + CLNT_STREAM_HANDSHAKE_FN handshake; + char *class; /* server class */ + char *service; /* server name */ +}; + +static void clnt_stream_close(CLNT_STREAM *); + +/* clnt_stream_event - server-initiated disconnect or client-side timeout */ + +static void clnt_stream_event(int unused_event, void *context) +{ + CLNT_STREAM *clnt_stream = (CLNT_STREAM *) context; + + /* + * Sanity check. This routine causes the stream to be closed, so it + * cannot be called when the stream is already closed. + */ + if (clnt_stream->vstream == 0) + msg_panic("clnt_stream_event: stream is closed"); + + clnt_stream_close(clnt_stream); +} + +/* clnt_stream_ttl_event - client-side expiration */ + +static void clnt_stream_ttl_event(int event, void *context) +{ + + /* + * XXX This function is needed only because event_request_timer() cannot + * distinguish between requests that specify the same call-back routine + * and call-back context. The fix is obvious: specify a request ID along + * with the call-back routine, but there is too much code that would have + * to be changed. + * + * XXX Should we be concerned that an overly aggressive optimizer will + * eliminate this function and replace calls to clnt_stream_ttl_event() + * by direct calls to clnt_stream_event()? It should not, because there + * exists code that takes the address of both functions. + */ + clnt_stream_event(event, context); +} + +/* clnt_stream_open - connect to service */ + +static void clnt_stream_open(CLNT_STREAM *clnt_stream) +{ + + /* + * Sanity check. + */ + if (clnt_stream->vstream) + msg_panic("clnt_stream_open: stream is open"); + + /* + * Schedule a read event so that we can clean up when the remote side + * disconnects, and schedule a timer event so that we can cleanup an idle + * connection. Note that both events are handled by the same routine. + * + * Finally, schedule an event to force disconnection even when the + * connection is not idle. This is to prevent one client from clinging on + * to a server forever. + */ + clnt_stream->vstream = mail_connect_wait(clnt_stream->class, + clnt_stream->service); + close_on_exec(vstream_fileno(clnt_stream->vstream), CLOSE_ON_EXEC); + event_enable_read(vstream_fileno(clnt_stream->vstream), clnt_stream_event, + (void *) clnt_stream); + event_request_timer(clnt_stream_event, (void *) clnt_stream, + clnt_stream->timeout); + event_request_timer(clnt_stream_ttl_event, (void *) clnt_stream, + clnt_stream->ttl); +} + +/* clnt_stream_close - disconnect from service */ + +static void clnt_stream_close(CLNT_STREAM *clnt_stream) +{ + + /* + * Sanity check. + */ + if (clnt_stream->vstream == 0) + msg_panic("clnt_stream_close: stream is closed"); + + /* + * Be sure to disable read and timer events. + */ + if (msg_verbose) + msg_info("%s stream disconnect", clnt_stream->service); + event_disable_readwrite(vstream_fileno(clnt_stream->vstream)); + event_cancel_timer(clnt_stream_event, (void *) clnt_stream); + event_cancel_timer(clnt_stream_ttl_event, (void *) clnt_stream); + (void) vstream_fclose(clnt_stream->vstream); + clnt_stream->vstream = 0; +} + +/* clnt_stream_recover - recover from server-initiated disconnect */ + +void clnt_stream_recover(CLNT_STREAM *clnt_stream) +{ + + /* + * Clean up. Don't re-connect until the caller needs it. + */ + if (clnt_stream->vstream) + clnt_stream_close(clnt_stream); +} + +/* clnt_stream_access - access a client stream */ + +VSTREAM *clnt_stream_access(CLNT_STREAM *clnt_stream) +{ + CLNT_STREAM_HANDSHAKE_FN handshake; + + /* + * Open a stream or restart the idle timer. + * + * Important! Do not restart the TTL timer! + */ + if (clnt_stream->vstream == 0) { + clnt_stream_open(clnt_stream); + handshake = clnt_stream->handshake; + } else if (readable(vstream_fileno(clnt_stream->vstream))) { + clnt_stream_close(clnt_stream); + clnt_stream_open(clnt_stream); + handshake = clnt_stream->handshake; + } else { + event_request_timer(clnt_stream_event, (void *) clnt_stream, + clnt_stream->timeout); + handshake = 0; + } + if (handshake != 0 && handshake(clnt_stream->vstream) != 0) + return (0); + return (clnt_stream->vstream); +} + +/* clnt_stream_create - create client stream connection */ + +CLNT_STREAM *clnt_stream_create(const char *class, const char *service, + int timeout, int ttl, + CLNT_STREAM_HANDSHAKE_FN handshake) +{ + CLNT_STREAM *clnt_stream; + + /* + * Don't open the stream until the caller needs it. + */ + clnt_stream = (CLNT_STREAM *) mymalloc(sizeof(*clnt_stream)); + clnt_stream->vstream = 0; + clnt_stream->timeout = timeout; + clnt_stream->ttl = ttl; + clnt_stream->handshake = handshake; + clnt_stream->class = mystrdup(class); + clnt_stream->service = mystrdup(service); + return (clnt_stream); +} + +/* clnt_stream_free - destroy client stream instance */ + +void clnt_stream_free(CLNT_STREAM *clnt_stream) +{ + if (clnt_stream->vstream) + clnt_stream_close(clnt_stream); + myfree(clnt_stream->class); + myfree(clnt_stream->service); + myfree((void *) clnt_stream); +} |