Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / auto_clnt.c
blob596ed9b14cc629134a796f8e105082aa26da5ef6
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* auto_clnt 3
6 /* SUMMARY
7 /* client endpoint maintenance
8 /* SYNOPSIS
9 /* #include <auto_clnt.h>
11 /* AUTO_CLNT *auto_clnt_create(service, timeout, max_idle, max_ttl)
12 /* const char *service;
13 /* int timeout;
14 /* int max_idle;
15 /* int max_ttl;
17 /* VSTREAM *auto_clnt_access(auto_clnt)
18 /* AUTO_CLNT *auto_clnt;
20 /* void auto_clnt_recover(auto_clnt)
21 /* AUTO_CLNT *auto_clnt;
23 /* const char *auto_clnt_name(auto_clnt)
24 /* AUTO_CLNT *auto_clnt;
26 /* void auto_clnt_free(auto_clnt)
27 /* AUTO_CLNT *auto_clnt;
28 /* DESCRIPTION
29 /* This module maintains IPC client endpoints that automatically
30 /* disconnect after a being idle for a configurable amount of time,
31 /* that disconnect after a configurable time to live,
32 /* and that transparently handle most server-initiated disconnects.
34 /* This module tries each operation only a limited number of
35 /* times and then reports an error. This is unlike the
36 /* clnt_stream(3) module which will retry forever, so that
37 /* the application never experiences an error.
39 /* auto_clnt_create() instantiates a client endpoint.
41 /* auto_clnt_access() returns an open stream to the service specified
42 /* to auto_clnt_create(). The stream instance may change between calls.
43 /* The result is a null pointer in case of failure.
45 /* auto_clnt_recover() recovers from a server-initiated disconnect
46 /* that happened in the middle of an I/O operation.
48 /* auto_clnt_name() returns the name of the specified client endpoint.
50 /* auto_clnt_free() destroys of the specified client endpoint.
52 /* Arguments:
53 /* .IP service
54 /* The service argument specifies "transport:servername" where
55 /* transport is currently limited to one of the following:
56 /* .RS
57 /* .IP inet
58 /* servername has the form "inet:host:port".
59 /* .IP local
60 /* servername has the form "local:private/servicename" or
61 /* "local:public/servicename". This is the preferred way to
62 /* specify Postfix daemons that are configured as "unix" in
63 /* master.cf.
64 /* .IP unix
65 /* servername has the form "unix:private/servicename" or
66 /* "unix:public/servicename". This does not work on Solaris,
67 /* where Postfix uses STREAMS instead of UNIX-domain sockets.
68 /* .RE
69 /* .IP timeout
70 /* The time limit for sending, receiving, or for connecting
71 /* to a server. Specify a value <=0 to disable the time limit.
72 /* .IP max_idle
73 /* Idle time after which the client disconnects. Specify 0 to
74 /* disable the limit.
75 /* .IP max_ttl
76 /* Upper bound on the time that a connection is allowed to persist.
77 /* Specify 0 to disable the limit.
78 /* .IP open_action
79 /* Application call-back routine that opens a stream or returns a
80 /* null pointer upon failure. In case of success, the call-back routine
81 /* is expected to set the stream pathname to the server endpoint name.
82 /* .IP context
83 /* Application context that is passed to the open_action routine.
84 /* DIAGNOSTICS
85 /* Warnings: communication failure. Fatal error: out of memory.
86 /* LICENSE
87 /* .ad
88 /* .fi
89 /* The Secure Mailer license must be distributed with this software.
90 /* AUTHOR(S)
91 /* Wietse Venema
92 /* IBM T.J. Watson Research
93 /* P.O. Box 704
94 /* Yorktown Heights, NY 10598, USA
95 /*--*/
97 /* System library. */
99 #include <sys_defs.h>
100 #include <string.h>
102 /* Utility library. */
104 #include <msg.h>
105 #include <mymalloc.h>
106 #include <vstream.h>
107 #include <events.h>
108 #include <iostuff.h>
109 #include <connect.h>
110 #include <split_at.h>
111 #include <auto_clnt.h>
113 /* Application-specific. */
116 * AUTO_CLNT is an opaque structure. None of the access methods can easily
117 * be implemented as a macro, and access is not performance critical anyway.
119 struct AUTO_CLNT {
120 VSTREAM *vstream; /* buffered I/O */
121 char *endpoint; /* host:port or pathname */
122 int timeout; /* I/O time limit */
123 int max_idle; /* time before client disconnect */
124 int max_ttl; /* time before client disconnect */
125 int (*connect) (const char *, int, int); /* unix, local, inet */
128 static void auto_clnt_close(AUTO_CLNT *);
130 /* auto_clnt_event - server-initiated disconnect or client-side max_idle */
132 static void auto_clnt_event(int unused_event, char *context)
134 AUTO_CLNT *auto_clnt = (AUTO_CLNT *) context;
137 * Sanity check. This routine causes the stream to be closed, so it
138 * cannot be called when the stream is already closed.
140 if (auto_clnt->vstream == 0)
141 msg_panic("auto_clnt_event: stream is closed");
143 auto_clnt_close(auto_clnt);
146 /* auto_clnt_ttl_event - client-side expiration */
148 static void auto_clnt_ttl_event(int event, char *context)
152 * XXX This function is needed only because event_request_timer() cannot
153 * distinguish between requests that specify the same call-back routine
154 * and call-back context. The fix is obvious: specify a request ID along
155 * with the call-back routine, but there is too much code that would have
156 * to be changed.
158 * XXX Should we be concerned that an overly agressive optimizer will
159 * eliminate this function and replace calls to auto_clnt_ttl_event() by
160 * direct calls to auto_clnt_event()? It should not, because there exists
161 * code that takes the address of both functions.
163 auto_clnt_event(event, context);
166 /* auto_clnt_open - connect to service */
168 static void auto_clnt_open(AUTO_CLNT *auto_clnt)
170 const char *myname = "auto_clnt_open";
171 int fd;
174 * Sanity check.
176 if (auto_clnt->vstream)
177 msg_panic("auto_clnt_open: stream is open");
180 * Schedule a read event so that we can clean up when the remote side
181 * disconnects, and schedule a timer event so that we can cleanup an idle
182 * connection. Note that both events are handled by the same routine.
184 * Finally, schedule an event to force disconnection even when the
185 * connection is not idle. This is to prevent one client from clinging on
186 * to a server forever.
188 fd = auto_clnt->connect(auto_clnt->endpoint, BLOCKING, auto_clnt->timeout);
189 if (fd < 0) {
190 msg_warn("connect to %s: %m", auto_clnt->endpoint);
191 } else {
192 if (msg_verbose)
193 msg_info("%s: connected to %s", myname, auto_clnt->endpoint);
194 auto_clnt->vstream = vstream_fdopen(fd, O_RDWR);
195 vstream_control(auto_clnt->vstream,
196 VSTREAM_CTL_PATH, auto_clnt->endpoint,
197 VSTREAM_CTL_TIMEOUT, auto_clnt->timeout,
198 VSTREAM_CTL_END);
201 if (auto_clnt->vstream != 0) {
202 close_on_exec(vstream_fileno(auto_clnt->vstream), CLOSE_ON_EXEC);
203 event_enable_read(vstream_fileno(auto_clnt->vstream), auto_clnt_event,
204 (char *) auto_clnt);
205 if (auto_clnt->max_idle > 0)
206 event_request_timer(auto_clnt_event, (char *) auto_clnt,
207 auto_clnt->max_idle);
208 if (auto_clnt->max_ttl > 0)
209 event_request_timer(auto_clnt_ttl_event, (char *) auto_clnt,
210 auto_clnt->max_ttl);
214 /* auto_clnt_close - disconnect from service */
216 static void auto_clnt_close(AUTO_CLNT *auto_clnt)
218 const char *myname = "auto_clnt_close";
221 * Sanity check.
223 if (auto_clnt->vstream == 0)
224 msg_panic("%s: stream is closed", myname);
227 * Be sure to disable read and timer events.
229 if (msg_verbose)
230 msg_info("%s: disconnect %s stream",
231 myname, VSTREAM_PATH(auto_clnt->vstream));
232 event_disable_readwrite(vstream_fileno(auto_clnt->vstream));
233 event_cancel_timer(auto_clnt_event, (char *) auto_clnt);
234 event_cancel_timer(auto_clnt_ttl_event, (char *) auto_clnt);
235 (void) vstream_fclose(auto_clnt->vstream);
236 auto_clnt->vstream = 0;
239 /* auto_clnt_recover - recover from server-initiated disconnect */
241 void auto_clnt_recover(AUTO_CLNT *auto_clnt)
245 * Clean up. Don't re-connect until the caller needs it.
247 if (auto_clnt->vstream)
248 auto_clnt_close(auto_clnt);
251 /* auto_clnt_access - access a client stream */
253 VSTREAM *auto_clnt_access(AUTO_CLNT *auto_clnt)
257 * Open a stream or restart the idle timer.
259 * Important! Do not restart the TTL timer!
261 if (auto_clnt->vstream == 0) {
262 auto_clnt_open(auto_clnt);
263 } else {
264 if (auto_clnt->max_idle > 0)
265 event_request_timer(auto_clnt_event, (char *) auto_clnt,
266 auto_clnt->max_idle);
268 return (auto_clnt->vstream);
271 /* auto_clnt_create - create client stream object */
273 AUTO_CLNT *auto_clnt_create(const char *service, int timeout,
274 int max_idle, int max_ttl)
276 const char *myname = "auto_clnt_create";
277 char *transport = mystrdup(service);
278 char *endpoint;
279 AUTO_CLNT *auto_clnt;
282 * Don't open the stream until the caller needs it.
284 if ((endpoint = split_at(transport, ':')) == 0
285 || *endpoint == 0 || *transport == 0)
286 msg_fatal("need service transport:endpoint instead of \"%s\"", service);
287 if (msg_verbose)
288 msg_info("%s: transport=%s endpoint=%s", myname, transport, endpoint);
289 auto_clnt = (AUTO_CLNT *) mymalloc(sizeof(*auto_clnt));
290 auto_clnt->vstream = 0;
291 auto_clnt->endpoint = mystrdup(endpoint);
292 auto_clnt->timeout = timeout;
293 auto_clnt->max_idle = max_idle;
294 auto_clnt->max_ttl = max_ttl;
295 if (strcmp(transport, "inet") == 0) {
296 auto_clnt->connect = inet_connect;
297 } else if (strcmp(transport, "local") == 0) {
298 auto_clnt->connect = LOCAL_CONNECT;
299 } else if (strcmp(transport, "unix") == 0) {
300 auto_clnt->connect = unix_connect;
301 } else {
302 msg_fatal("invalid transport name: %s in service: %s",
303 transport, service);
305 myfree(transport);
306 return (auto_clnt);
309 /* auto_clnt_name - return client stream name */
311 const char *auto_clnt_name(AUTO_CLNT *auto_clnt)
313 return (auto_clnt->endpoint);
316 /* auto_clnt_free - destroy client stream instance */
318 void auto_clnt_free(AUTO_CLNT *auto_clnt)
320 if (auto_clnt->vstream)
321 auto_clnt_close(auto_clnt);
322 myfree(auto_clnt->endpoint);
323 myfree((char *) auto_clnt);