xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/clnt_stream.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
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