1/*++ 2/* NAME 3/* clnt_stream 3 4/* SUMMARY 5/* client endpoint maintenance 6/* SYNOPSIS 7/* #include <clnt_stream.h> 8/* 9/* CLNT_STREAM *clnt_stream_create(class, service, timeout, ttl) 10/* const char *class; 11/* const char *service; 12/* int timeout; 13/* int ttl; 14/* 15/* VSTREAM *clnt_stream_access(clnt_stream) 16/* CLNT_STREAM *clnt_stream; 17/* 18/* void clnt_stream_recover(clnt_stream) 19/* CLNT_STREAM *clnt_stream; 20/* 21/* void clnt_stream_free(clnt_stream) 22/* CLNT_STREAM *clnt_stream; 23/* DESCRIPTION 24/* This module maintains local IPC client endpoints that automatically 25/* disconnect after a being idle for a configurable amount of time, 26/* that disconnect after a configurable time to live, 27/* and that transparently handle most server-initiated disconnects. 28/* Server disconnect is detected by read-selecting the client endpoint. 29/* The code assumes that the server has disconnected when the endpoint 30/* becomes readable. 31/* 32/* clnt_stream_create() instantiates a client endpoint. 33/* 34/* clnt_stream_access() returns an open stream to the service specified 35/* to clnt_stream_create(). The stream instance may change between calls. 36/* 37/* clnt_stream_recover() recovers from a server-initiated disconnect 38/* that happened in the middle of an I/O operation. 39/* 40/* clnt_stream_free() destroys of the specified client endpoint. 41/* 42/* Arguments: 43/* .IP class 44/* The service class, private or public. 45/* .IP service 46/* The service endpoint name. The name is limited to local IPC 47/* over sockets or equivalent. 48/* .IP timeout 49/* Idle time after which the client disconnects. 50/* .IP ttl 51/* Upper bound on the time that a connection is allowed to persist. 52/* DIAGNOSTICS 53/* Warnings: communication failure. Fatal error: mail system is down, 54/* out of memory. 55/* SEE ALSO 56/* mail_proto(3h) low-level mail component glue. 57/* LICENSE 58/* .ad 59/* .fi 60/* The Secure Mailer license must be distributed with this software. 61/* AUTHOR(S) 62/* Wietse Venema 63/* IBM T.J. Watson Research 64/* P.O. Box 704 65/* Yorktown Heights, NY 10598, USA 66/*--*/ 67 68/* System library. */ 69 70#include <sys_defs.h> 71 72/* Utility library. */ 73 74#include <msg.h> 75#include <mymalloc.h> 76#include <vstream.h> 77#include <events.h> 78#include <iostuff.h> 79 80/* Global library. */ 81 82#include "mail_proto.h" 83#include "mail_params.h" 84#include "clnt_stream.h" 85 86/* Application-specific. */ 87 88 /* 89 * CLNT_STREAM is an opaque structure. None of the access methods can easily 90 * be implemented as a macro, and access is not performance critical anyway. 91 */ 92struct CLNT_STREAM { 93 VSTREAM *vstream; /* buffered I/O */ 94 int timeout; /* time before client disconnect */ 95 int ttl; /* time before client disconnect */ 96 char *class; /* server class */ 97 char *service; /* server name */ 98}; 99 100static void clnt_stream_close(CLNT_STREAM *); 101 102/* clnt_stream_event - server-initiated disconnect or client-side timeout */ 103 104static void clnt_stream_event(int unused_event, char *context) 105{ 106 CLNT_STREAM *clnt_stream = (CLNT_STREAM *) context; 107 108 /* 109 * Sanity check. This routine causes the stream to be closed, so it 110 * cannot be called when the stream is already closed. 111 */ 112 if (clnt_stream->vstream == 0) 113 msg_panic("clnt_stream_event: stream is closed"); 114 115 clnt_stream_close(clnt_stream); 116} 117 118/* clnt_stream_ttl_event - client-side expiration */ 119 120static void clnt_stream_ttl_event(int event, char *context) 121{ 122 123 /* 124 * XXX This function is needed only because event_request_timer() cannot 125 * distinguish between requests that specify the same call-back routine 126 * and call-back context. The fix is obvious: specify a request ID along 127 * with the call-back routine, but there is too much code that would have 128 * to be changed. 129 * 130 * XXX Should we be concerned that an overly agressive optimizer will 131 * eliminate this function and replace calls to clnt_stream_ttl_event() 132 * by direct calls to clnt_stream_event()? It should not, because there 133 * exists code that takes the address of both functions. 134 */ 135 clnt_stream_event(event, context); 136} 137 138/* clnt_stream_open - connect to service */ 139 140static void clnt_stream_open(CLNT_STREAM *clnt_stream) 141{ 142 143 /* 144 * Sanity check. 145 */ 146 if (clnt_stream->vstream) 147 msg_panic("clnt_stream_open: stream is open"); 148 149 /* 150 * Schedule a read event so that we can clean up when the remote side 151 * disconnects, and schedule a timer event so that we can cleanup an idle 152 * connection. Note that both events are handled by the same routine. 153 * 154 * Finally, schedule an event to force disconnection even when the 155 * connection is not idle. This is to prevent one client from clinging on 156 * to a server forever. 157 */ 158 clnt_stream->vstream = mail_connect_wait(clnt_stream->class, 159 clnt_stream->service); 160 close_on_exec(vstream_fileno(clnt_stream->vstream), CLOSE_ON_EXEC); 161 event_enable_read(vstream_fileno(clnt_stream->vstream), clnt_stream_event, 162 (char *) clnt_stream); 163 event_request_timer(clnt_stream_event, (char *) clnt_stream, 164 clnt_stream->timeout); 165 event_request_timer(clnt_stream_ttl_event, (char *) clnt_stream, 166 clnt_stream->ttl); 167} 168 169/* clnt_stream_close - disconnect from service */ 170 171static void clnt_stream_close(CLNT_STREAM *clnt_stream) 172{ 173 174 /* 175 * Sanity check. 176 */ 177 if (clnt_stream->vstream == 0) 178 msg_panic("clnt_stream_close: stream is closed"); 179 180 /* 181 * Be sure to disable read and timer events. 182 */ 183 if (msg_verbose) 184 msg_info("%s stream disconnect", clnt_stream->service); 185 event_disable_readwrite(vstream_fileno(clnt_stream->vstream)); 186 event_cancel_timer(clnt_stream_event, (char *) clnt_stream); 187 event_cancel_timer(clnt_stream_ttl_event, (char *) clnt_stream); 188 (void) vstream_fclose(clnt_stream->vstream); 189 clnt_stream->vstream = 0; 190} 191 192/* clnt_stream_recover - recover from server-initiated disconnect */ 193 194void clnt_stream_recover(CLNT_STREAM *clnt_stream) 195{ 196 197 /* 198 * Clean up. Don't re-connect until the caller needs it. 199 */ 200 if (clnt_stream->vstream) 201 clnt_stream_close(clnt_stream); 202} 203 204/* clnt_stream_access - access a client stream */ 205 206VSTREAM *clnt_stream_access(CLNT_STREAM *clnt_stream) 207{ 208 209 /* 210 * Open a stream or restart the idle timer. 211 * 212 * Important! Do not restart the TTL timer! 213 */ 214 if (clnt_stream->vstream == 0) { 215 clnt_stream_open(clnt_stream); 216 } else if (readable(vstream_fileno(clnt_stream->vstream))) { 217 clnt_stream_close(clnt_stream); 218 clnt_stream_open(clnt_stream); 219 } else { 220 event_request_timer(clnt_stream_event, (char *) clnt_stream, 221 clnt_stream->timeout); 222 } 223 return (clnt_stream->vstream); 224} 225 226/* clnt_stream_create - create client stream connection */ 227 228CLNT_STREAM *clnt_stream_create(const char *class, const char *service, 229 int timeout, int ttl) 230{ 231 CLNT_STREAM *clnt_stream; 232 233 /* 234 * Don't open the stream until the caller needs it. 235 */ 236 clnt_stream = (CLNT_STREAM *) mymalloc(sizeof(*clnt_stream)); 237 clnt_stream->vstream = 0; 238 clnt_stream->timeout = timeout; 239 clnt_stream->ttl = ttl; 240 clnt_stream->class = mystrdup(class); 241 clnt_stream->service = mystrdup(service); 242 return (clnt_stream); 243} 244 245/* clnt_stream_free - destroy client stream instance */ 246 247void clnt_stream_free(CLNT_STREAM *clnt_stream) 248{ 249 if (clnt_stream->vstream) 250 clnt_stream_close(clnt_stream); 251 myfree(clnt_stream->class); 252 myfree(clnt_stream->service); 253 myfree((char *) clnt_stream); 254} 255