7 /* client endpoint maintenance
9 /* #include <auto_clnt.h>
11 /* AUTO_CLNT *auto_clnt_create(service, timeout, max_idle, max_ttl)
12 /* const char *service;
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;
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.
54 /* The service argument specifies "transport:servername" where
55 /* transport is currently limited to one of the following:
58 /* servername has the form "inet:host:port".
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
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.
70 /* The time limit for sending, receiving, or for connecting
71 /* to a server. Specify a value <=0 to disable the time limit.
73 /* Idle time after which the client disconnects. Specify 0 to
76 /* Upper bound on the time that a connection is allowed to persist.
77 /* Specify 0 to disable the limit.
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.
83 /* Application context that is passed to the open_action routine.
85 /* Warnings: communication failure. Fatal error: out of memory.
89 /* The Secure Mailer license must be distributed with this software.
92 /* IBM T.J. Watson Research
94 /* Yorktown Heights, NY 10598, USA
102 /* Utility library. */
105 #include <mymalloc.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.
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
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";
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
);
190 msg_warn("connect to %s: %m", auto_clnt
->endpoint
);
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
,
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
,
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
,
214 /* auto_clnt_close - disconnect from service */
216 static void auto_clnt_close(AUTO_CLNT
*auto_clnt
)
218 const char *myname
= "auto_clnt_close";
223 if (auto_clnt
->vstream
== 0)
224 msg_panic("%s: stream is closed", myname
);
227 * Be sure to disable read and timer events.
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
);
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
);
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
);
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
;
302 msg_fatal("invalid transport name: %s in service: %s",
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
);