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