Postfix3.3.1
auto_clnt.c
[詳解]
1 /*++
2 /* NAME
3 /* auto_clnt 3
4 /* SUMMARY
5 /* client endpoint maintenance
6 /* SYNOPSIS
7 /* #include <auto_clnt.h>
8 /*
9 /* AUTO_CLNT *auto_clnt_create(service, timeout, max_idle, max_ttl)
10 /* const char *service;
11 /* int timeout;
12 /* int max_idle;
13 /* int max_ttl;
14 /*
15 /* VSTREAM *auto_clnt_access(auto_clnt)
16 /* AUTO_CLNT *auto_clnt;
17 /*
18 /* void auto_clnt_recover(auto_clnt)
19 /* AUTO_CLNT *auto_clnt;
20 /*
21 /* const char *auto_clnt_name(auto_clnt)
22 /* AUTO_CLNT *auto_clnt;
23 /*
24 /* void auto_clnt_free(auto_clnt)
25 /* AUTO_CLNT *auto_clnt;
26 /* DESCRIPTION
27 /* This module maintains IPC client endpoints that automatically
28 /* disconnect after a being idle for a configurable amount of time,
29 /* that disconnect after a configurable time to live,
30 /* and that transparently handle most server-initiated disconnects.
31 /*
32 /* This module tries each operation only a limited number of
33 /* times and then reports an error. This is unlike the
34 /* clnt_stream(3) module which will retry forever, so that
35 /* the application never experiences an error.
36 /*
37 /* auto_clnt_create() instantiates a client endpoint.
38 /*
39 /* auto_clnt_access() returns an open stream to the service specified
40 /* to auto_clnt_create(). The stream instance may change between calls.
41 /* The result is a null pointer in case of failure.
42 /*
43 /* auto_clnt_recover() recovers from a server-initiated disconnect
44 /* that happened in the middle of an I/O operation.
45 /*
46 /* auto_clnt_name() returns the name of the specified client endpoint.
47 /*
48 /* auto_clnt_free() destroys of the specified client endpoint.
49 /*
50 /* Arguments:
51 /* .IP service
52 /* The service argument specifies "transport:servername" where
53 /* transport is currently limited to one of the following:
54 /* .RS
55 /* .IP inet
56 /* servername has the form "inet:host:port".
57 /* .IP local
58 /* servername has the form "local:private/servicename" or
59 /* "local:public/servicename". This is the preferred way to
60 /* specify Postfix daemons that are configured as "unix" in
61 /* master.cf.
62 /* .IP unix
63 /* servername has the form "unix:private/servicename" or
64 /* "unix:public/servicename". This does not work on Solaris,
65 /* where Postfix uses STREAMS instead of UNIX-domain sockets.
66 /* .RE
67 /* .IP timeout
68 /* The time limit for sending, receiving, or for connecting
69 /* to a server. Specify a value <=0 to disable the time limit.
70 /* .IP max_idle
71 /* Idle time after which the client disconnects. Specify 0 to
72 /* disable the limit.
73 /* .IP max_ttl
74 /* Upper bound on the time that a connection is allowed to persist.
75 /* Specify 0 to disable the limit.
76 /* .IP open_action
77 /* Application call-back routine that opens a stream or returns a
78 /* null pointer upon failure. In case of success, the call-back routine
79 /* is expected to set the stream pathname to the server endpoint name.
80 /* .IP context
81 /* Application context that is passed to the open_action routine.
82 /* DIAGNOSTICS
83 /* Warnings: communication failure. Fatal error: out of memory.
84 /* LICENSE
85 /* .ad
86 /* .fi
87 /* The Secure Mailer license must be distributed with this software.
88 /* AUTHOR(S)
89 /* Wietse Venema
90 /* IBM T.J. Watson Research
91 /* P.O. Box 704
92 /* Yorktown Heights, NY 10598, USA
93 /*--*/
94 
95 /* System library. */
96 
97 #include <sys_defs.h>
98 #include <string.h>
99 
100 /* Utility library. */
101 
102 #include <msg.h>
103 #include <mymalloc.h>
104 #include <vstream.h>
105 #include <events.h>
106 #include <iostuff.h>
107 #include <connect.h>
108 #include <split_at.h>
109 #include <auto_clnt.h>
110 
111 /* Application-specific. */
112 
113  /*
114  * AUTO_CLNT is an opaque structure. None of the access methods can easily
115  * be implemented as a macro, and access is not performance critical anyway.
116  */
117 struct AUTO_CLNT {
118  VSTREAM *vstream; /* buffered I/O */
119  char *endpoint; /* host:port or pathname */
120  int timeout; /* I/O time limit */
121  int max_idle; /* time before client disconnect */
122  int max_ttl; /* time before client disconnect */
123  int (*connect) (const char *, int, int); /* unix, local, inet */
124 };
125 
126 static void auto_clnt_close(AUTO_CLNT *);
127 
128 /* auto_clnt_event - server-initiated disconnect or client-side max_idle */
129 
130 static void auto_clnt_event(int unused_event, void *context)
131 {
132  AUTO_CLNT *auto_clnt = (AUTO_CLNT *) context;
133 
134  /*
135  * Sanity check. This routine causes the stream to be closed, so it
136  * cannot be called when the stream is already closed.
137  */
138  if (auto_clnt->vstream == 0)
139  msg_panic("auto_clnt_event: stream is closed");
140 
141  auto_clnt_close(auto_clnt);
142 }
143 
144 /* auto_clnt_ttl_event - client-side expiration */
145 
146 static void auto_clnt_ttl_event(int event, void *context)
147 {
148 
149  /*
150  * XXX This function is needed only because event_request_timer() cannot
151  * distinguish between requests that specify the same call-back routine
152  * and call-back context. The fix is obvious: specify a request ID along
153  * with the call-back routine, but there is too much code that would have
154  * to be changed.
155  *
156  * XXX Should we be concerned that an overly aggressive optimizer will
157  * eliminate this function and replace calls to auto_clnt_ttl_event() by
158  * direct calls to auto_clnt_event()? It should not, because there exists
159  * code that takes the address of both functions.
160  */
161  auto_clnt_event(event, context);
162 }
163 
164 /* auto_clnt_open - connect to service */
165 
166 static void auto_clnt_open(AUTO_CLNT *auto_clnt)
167 {
168  const char *myname = "auto_clnt_open";
169  int fd;
170 
171  /*
172  * Sanity check.
173  */
174  if (auto_clnt->vstream)
175  msg_panic("auto_clnt_open: stream is open");
176 
177  /*
178  * Schedule a read event so that we can clean up when the remote side
179  * disconnects, and schedule a timer event so that we can cleanup an idle
180  * connection. Note that both events are handled by the same routine.
181  *
182  * Finally, schedule an event to force disconnection even when the
183  * connection is not idle. This is to prevent one client from clinging on
184  * to a server forever.
185  */
186  fd = auto_clnt->connect(auto_clnt->endpoint, BLOCKING, auto_clnt->timeout);
187  if (fd < 0) {
188  msg_warn("connect to %s: %m", auto_clnt->endpoint);
189  } else {
190  if (msg_verbose)
191  msg_info("%s: connected to %s", myname, auto_clnt->endpoint);
192  auto_clnt->vstream = vstream_fdopen(fd, O_RDWR);
193  vstream_control(auto_clnt->vstream,
194  CA_VSTREAM_CTL_PATH(auto_clnt->endpoint),
195  CA_VSTREAM_CTL_TIMEOUT(auto_clnt->timeout),
197  }
198 
199  if (auto_clnt->vstream != 0) {
201  event_enable_read(vstream_fileno(auto_clnt->vstream), auto_clnt_event,
202  (void *) auto_clnt);
203  if (auto_clnt->max_idle > 0)
204  event_request_timer(auto_clnt_event, (void *) auto_clnt,
205  auto_clnt->max_idle);
206  if (auto_clnt->max_ttl > 0)
207  event_request_timer(auto_clnt_ttl_event, (void *) auto_clnt,
208  auto_clnt->max_ttl);
209  }
210 }
211 
212 /* auto_clnt_close - disconnect from service */
213 
214 static void auto_clnt_close(AUTO_CLNT *auto_clnt)
215 {
216  const char *myname = "auto_clnt_close";
217 
218  /*
219  * Sanity check.
220  */
221  if (auto_clnt->vstream == 0)
222  msg_panic("%s: stream is closed", myname);
223 
224  /*
225  * Be sure to disable read and timer events.
226  */
227  if (msg_verbose)
228  msg_info("%s: disconnect %s stream",
229  myname, VSTREAM_PATH(auto_clnt->vstream));
231  event_cancel_timer(auto_clnt_event, (void *) auto_clnt);
232  event_cancel_timer(auto_clnt_ttl_event, (void *) auto_clnt);
233  (void) vstream_fclose(auto_clnt->vstream);
234  auto_clnt->vstream = 0;
235 }
236 
237 /* auto_clnt_recover - recover from server-initiated disconnect */
238 
239 void auto_clnt_recover(AUTO_CLNT *auto_clnt)
240 {
241 
242  /*
243  * Clean up. Don't re-connect until the caller needs it.
244  */
245  if (auto_clnt->vstream)
246  auto_clnt_close(auto_clnt);
247 }
248 
249 /* auto_clnt_access - access a client stream */
250 
252 {
253 
254  /*
255  * Open a stream or restart the idle timer.
256  *
257  * Important! Do not restart the TTL timer!
258  */
259  if (auto_clnt->vstream == 0) {
260  auto_clnt_open(auto_clnt);
261  } else {
262  if (auto_clnt->max_idle > 0)
263  event_request_timer(auto_clnt_event, (void *) auto_clnt,
264  auto_clnt->max_idle);
265  }
266  return (auto_clnt->vstream);
267 }
268 
269 /* auto_clnt_create - create client stream object */
270 
271 AUTO_CLNT *auto_clnt_create(const char *service, int timeout,
272  int max_idle, int max_ttl)
273 {
274  const char *myname = "auto_clnt_create";
275  char *transport = mystrdup(service);
276  char *endpoint;
277  AUTO_CLNT *auto_clnt;
278 
279  /*
280  * Don't open the stream until the caller needs it.
281  */
282  if ((endpoint = split_at(transport, ':')) == 0
283  || *endpoint == 0 || *transport == 0)
284  msg_fatal("need service transport:endpoint instead of \"%s\"", service);
285  if (msg_verbose)
286  msg_info("%s: transport=%s endpoint=%s", myname, transport, endpoint);
287  auto_clnt = (AUTO_CLNT *) mymalloc(sizeof(*auto_clnt));
288  auto_clnt->vstream = 0;
289  auto_clnt->endpoint = mystrdup(endpoint);
290  auto_clnt->timeout = timeout;
291  auto_clnt->max_idle = max_idle;
292  auto_clnt->max_ttl = max_ttl;
293  if (strcmp(transport, "inet") == 0) {
294  auto_clnt->connect = inet_connect;
295  } else if (strcmp(transport, "local") == 0) {
296  auto_clnt->connect = LOCAL_CONNECT;
297  } else if (strcmp(transport, "unix") == 0) {
298  auto_clnt->connect = unix_connect;
299  } else {
300  msg_fatal("invalid transport name: %s in service: %s",
301  transport, service);
302  }
303  myfree(transport);
304  return (auto_clnt);
305 }
306 
307 /* auto_clnt_name - return client stream name */
308 
309 const char *auto_clnt_name(AUTO_CLNT *auto_clnt)
310 {
311  return (auto_clnt->endpoint);
312 }
313 
314 /* auto_clnt_free - destroy client stream instance */
315 
316 void auto_clnt_free(AUTO_CLNT *auto_clnt)
317 {
318  if (auto_clnt->vstream)
319  auto_clnt_close(auto_clnt);
320  myfree(auto_clnt->endpoint);
321  myfree((void *) auto_clnt);
322 }
int msg_verbose
Definition: msg.c:177
void event_enable_read(int fd, EVENT_NOTIFY_RDWR_FN callback, void *context)
Definition: events.c:729
VSTREAM * auto_clnt_access(AUTO_CLNT *auto_clnt)
Definition: auto_clnt.c:251
void myfree(void *ptr)
Definition: mymalloc.c:207
#define CA_VSTREAM_CTL_TIMEOUT(v)
Definition: vstream.h:163
char * mystrdup(const char *str)
Definition: mymalloc.c:225
const char * auto_clnt_name(AUTO_CLNT *auto_clnt)
Definition: auto_clnt.c:309
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define VSTREAM_PATH(vp)
Definition: vstream.h:126
int(* connect)(const char *, int, int)
Definition: auto_clnt.c:123
#define LOCAL_CONNECT
Definition: sys_defs.h:1424
int inet_connect(const char *, int, int)
Definition: inet_connect.c:77
AUTO_CLNT * auto_clnt_create(const char *service, int timeout, int max_idle, int max_ttl)
Definition: auto_clnt.c:271
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
int unix_connect(const char *, int, int)
Definition: unix_connect.c:59
int max_idle
Definition: auto_clnt.c:121
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTREAM * vstream
Definition: auto_clnt.c:118
void auto_clnt_free(AUTO_CLNT *auto_clnt)
Definition: auto_clnt.c:316
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
int max_ttl
Definition: auto_clnt.c:122
int int
Definition: smtpd_proxy.h:21
void auto_clnt_recover(AUTO_CLNT *auto_clnt)
Definition: auto_clnt.c:239
time_t event_request_timer(EVENT_NOTIFY_TIME_FN callback, void *context, int delay)
Definition: events.c:894
char * split_at(char *string, int delimiter)
Definition: split_at.c:53
#define vstream_fileno(vp)
Definition: vstream.h:115
#define CA_VSTREAM_CTL_END
Definition: vstream.h:155
#define CLOSE_ON_EXEC
Definition: iostuff.h:51
#define CA_VSTREAM_CTL_PATH(v)
Definition: vstream.h:158
char * endpoint
Definition: auto_clnt.c:119
void vstream_control(VSTREAM *stream, int name,...)
Definition: vstream.c:1372
void event_disable_readwrite(int fd)
Definition: events.c:839
#define BLOCKING
Definition: iostuff.h:48
int timeout
Definition: auto_clnt.c:120
int event_cancel_timer(EVENT_NOTIFY_TIME_FN callback, void *context)
Definition: events.c:965
int close_on_exec(int fd, int on)
Definition: close_on_exec.c:49
VSTREAM * vstream_fdopen(int fd, int flags)
Definition: vstream.c:1204
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
void msg_info(const char *fmt,...)
Definition: msg.c:199