Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / clnt_stream.c
blob65d0be2aabc6edacb240668cc9ecbf345c759eae
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* clnt_stream 3
6 /* SUMMARY
7 /* client endpoint maintenance
8 /* SYNOPSIS
9 /* #include <clnt_stream.h>
11 /* CLNT_STREAM *clnt_stream_create(class, service, timeout, ttl)
12 /* const char *class;
13 /* const char *service;
14 /* int timeout;
15 /* int ttl;
17 /* VSTREAM *clnt_stream_access(clnt_stream)
18 /* CLNT_STREAM *clnt_stream;
20 /* void clnt_stream_recover(clnt_stream)
21 /* CLNT_STREAM *clnt_stream;
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.
34 /* clnt_stream_create() instantiates a client endpoint.
36 /* clnt_stream_access() returns an open stream to the service specified
37 /* to clnt_stream_create(). The stream instance may change between calls.
39 /* clnt_stream_recover() recovers from a server-initiated disconnect
40 /* that happened in the middle of an I/O operation.
42 /* clnt_stream_free() destroys of the specified client endpoint.
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 /*--*/
70 /* System library. */
72 #include <sys_defs.h>
74 /* Utility library. */
76 #include <msg.h>
77 #include <mymalloc.h>
78 #include <vstream.h>
79 #include <events.h>
80 #include <iostuff.h>
82 /* Global library. */
84 #include "mail_proto.h"
85 #include "mail_params.h"
86 #include "clnt_stream.h"
88 /* Application-specific. */
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.
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 */
102 static void clnt_stream_close(CLNT_STREAM *);
104 /* clnt_stream_event - server-initiated disconnect or client-side timeout */
106 static void clnt_stream_event(int unused_event, char *context)
108 CLNT_STREAM *clnt_stream = (CLNT_STREAM *) context;
111 * Sanity check. This routine causes the stream to be closed, so it
112 * cannot be called when the stream is already closed.
114 if (clnt_stream->vstream == 0)
115 msg_panic("clnt_stream_event: stream is closed");
117 clnt_stream_close(clnt_stream);
120 /* clnt_stream_ttl_event - client-side expiration */
122 static void clnt_stream_ttl_event(int event, char *context)
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.
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.
137 clnt_stream_event(event, context);
140 /* clnt_stream_open - connect to service */
142 static void clnt_stream_open(CLNT_STREAM *clnt_stream)
146 * Sanity check.
148 if (clnt_stream->vstream)
149 msg_panic("clnt_stream_open: stream is open");
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.
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.
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);
171 /* clnt_stream_close - disconnect from service */
173 static void clnt_stream_close(CLNT_STREAM *clnt_stream)
177 * Sanity check.
179 if (clnt_stream->vstream == 0)
180 msg_panic("clnt_stream_close: stream is closed");
183 * Be sure to disable read and timer events.
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;
194 /* clnt_stream_recover - recover from server-initiated disconnect */
196 void clnt_stream_recover(CLNT_STREAM *clnt_stream)
200 * Clean up. Don't re-connect until the caller needs it.
202 if (clnt_stream->vstream)
203 clnt_stream_close(clnt_stream);
206 /* clnt_stream_access - access a client stream */
208 VSTREAM *clnt_stream_access(CLNT_STREAM *clnt_stream)
212 * Open a stream or restart the idle timer.
214 * Important! Do not restart the TTL timer!
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);
225 return (clnt_stream->vstream);
228 /* clnt_stream_create - create client stream connection */
230 CLNT_STREAM *clnt_stream_create(const char *class, const char *service,
231 int timeout, int ttl)
233 CLNT_STREAM *clnt_stream;
236 * Don't open the stream until the caller needs it.
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);
247 /* clnt_stream_free - destroy client stream instance */
249 void clnt_stream_free(CLNT_STREAM *clnt_stream)
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);