xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/scache_clnt.c (revision b757af438b42b93f8c6571f026d8b8ef3eaf5fc9)
1 /*	$NetBSD: scache_clnt.c,v 1.1.1.2 2011/03/02 19:32:18 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	scache_clnt 3
6 /* SUMMARY
7 /*	session cache manager client
8 /* SYNOPSIS
9 /*	#include <scache.h>
10 /* DESCRIPTION
11 /*	SCACHE *scache_clnt_create(server, timeout, idle_limit, ttl_limit)
12 /*	const char *server;
13 /*	int	timeout;
14 /*	int	idle_limit;
15 /*	int	ttl_limit;
16 /* DESCRIPTION
17 /*	This module implements the client-side protocol of the
18 /*	session cache service.
19 /*
20 /*	scache_clnt_create() creates a session cache service client.
21 /*
22 /*	Arguments:
23 /* .IP server
24 /*	The session cache service name.
25 /* .IP timeout
26 /*	Time limit for connect, send or receive operations.
27 /* .IP idle_limit
28 /*	Idle time after which the client disconnects.
29 /* .IP ttl_limit
30 /*	Upper bound on the time that a connection is allowed to persist.
31 /* DIAGNOSTICS
32 /*	Fatal error: memory allocation problem;
33 /*	warning: communication error;
34 /*	panic: internal consistency failure.
35 /* SEE ALSO
36 /*	scache(3), generic session cache API
37 /* LICENSE
38 /* .ad
39 /* .fi
40 /*	The Secure Mailer license must be distributed with this software.
41 /* AUTHOR(S)
42 /*	Wietse Venema
43 /*	IBM T.J. Watson Research
44 /*	P.O. Box 704
45 /*	Yorktown Heights, NY 10598, USA
46 /*--*/
47 
48 /* System library. */
49 
50 #include <sys_defs.h>
51 #include <errno.h>
52 
53 /* Utility library. */
54 
55 #include <msg.h>
56 #include <mymalloc.h>
57 #include <auto_clnt.h>
58 #include <stringops.h>
59 
60 /*#define msg_verbose 1*/
61 
62 /* Global library. */
63 
64 #include <mail_proto.h>
65 #include <mail_params.h>
66 #include <scache.h>
67 
68 /* Application-specific. */
69 
70  /*
71   * SCACHE_CLNT is a derived type from the SCACHE super-class.
72   */
73 typedef struct {
74     SCACHE  scache[1];			/* super-class */
75     AUTO_CLNT *auto_clnt;		/* client endpoint */
76 #ifdef CANT_WRITE_BEFORE_SENDING_FD
77     VSTRING *dummy;			/* dummy buffer */
78 #endif
79 } SCACHE_CLNT;
80 
81 #define STR(x) vstring_str(x)
82 
83 #define SCACHE_MAX_TRIES	2
84 
85 /* scache_clnt_save_endp - save endpoint */
86 
87 static void scache_clnt_save_endp(SCACHE *scache, int endp_ttl,
88 				          const char *endp_label,
89 				          const char *endp_prop, int fd)
90 {
91     SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
92     const char *myname = "scache_clnt_save_endp";
93     VSTREAM *stream;
94     int     status;
95     int     tries;
96     int     count = 0;
97 
98     if (msg_verbose)
99 	msg_info("%s: endp=%s prop=%s fd=%d",
100 		 myname, endp_label, endp_prop, fd);
101 
102     /*
103      * Sanity check.
104      */
105     if (endp_ttl <= 0)
106 	msg_panic("%s: bad endp_ttl: %d", myname, endp_ttl);
107 
108     /*
109      * Try a few times before disabling the cache. We use synchronous calls;
110      * the session cache service is CPU bound and making the client
111      * asynchronous would just complicate the code.
112      */
113     for (tries = 0; sp->auto_clnt != 0; tries++) {
114 	if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) {
115 	    errno = 0;
116 	    count += 1;
117 	    if (attr_print(stream, ATTR_FLAG_NONE,
118 			 ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_SAVE_ENDP,
119 			   ATTR_TYPE_INT, MAIL_ATTR_TTL, endp_ttl,
120 			   ATTR_TYPE_STR, MAIL_ATTR_LABEL, endp_label,
121 			   ATTR_TYPE_STR, MAIL_ATTR_PROP, endp_prop,
122 			   ATTR_TYPE_END) != 0
123 		|| vstream_fflush(stream)
124 #ifdef CANT_WRITE_BEFORE_SENDING_FD
125 		|| attr_scan(stream, ATTR_FLAG_STRICT,
126 			     ATTR_TYPE_STR, MAIL_ATTR_DUMMY, sp->dummy,
127 			     ATTR_TYPE_END) != 1
128 #endif
129 		|| LOCAL_SEND_FD(vstream_fileno(stream), fd) < 0
130 		|| attr_scan(stream, ATTR_FLAG_STRICT,
131 			     ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
132 			     ATTR_TYPE_END) != 1) {
133 		if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
134 		    msg_warn("problem talking to service %s: %m",
135 			     VSTREAM_PATH(stream));
136 		/* Give up or recover. */
137 	    } else {
138 		if (msg_verbose && status != 0)
139 		    msg_warn("%s: descriptor save failed with status %d",
140 			     myname, status);
141 		break;
142 	    }
143 	}
144 	/* Give up or recover. */
145 	if (tries >= SCACHE_MAX_TRIES - 1) {
146 	    msg_warn("disabling connection caching");
147 	    auto_clnt_free(sp->auto_clnt);
148 	    sp->auto_clnt = 0;
149 	    break;
150 	}
151 	sleep(1);				/* XXX make configurable */
152 	auto_clnt_recover(sp->auto_clnt);
153     }
154     /* Always close the descriptor before returning. */
155     if (close(fd) < 0)
156 	msg_warn("%s: close(%d): %m", myname, fd);
157 }
158 
159 /* scache_clnt_find_endp - look up cached session */
160 
161 static int scache_clnt_find_endp(SCACHE *scache, const char *endp_label,
162 				         VSTRING *endp_prop)
163 {
164     SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
165     const char *myname = "scache_clnt_find_endp";
166     VSTREAM *stream;
167     int     status;
168     int     tries;
169     int     fd;
170 
171     /*
172      * Try a few times before disabling the cache. We use synchronous calls;
173      * the session cache service is CPU bound and making the client
174      * asynchronous would just complicate the code.
175      */
176     for (tries = 0; sp->auto_clnt != 0; tries++) {
177 	if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) {
178 	    errno = 0;
179 	    if (attr_print(stream, ATTR_FLAG_NONE,
180 			 ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_FIND_ENDP,
181 			   ATTR_TYPE_STR, MAIL_ATTR_LABEL, endp_label,
182 			   ATTR_TYPE_END) != 0
183 		|| vstream_fflush(stream)
184 		|| attr_scan(stream, ATTR_FLAG_STRICT,
185 			     ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
186 			     ATTR_TYPE_STR, MAIL_ATTR_PROP, endp_prop,
187 			     ATTR_TYPE_END) != 2) {
188 		if (msg_verbose || (errno != EPIPE && errno != ENOENT))
189 		    msg_warn("problem talking to service %s: %m",
190 			     VSTREAM_PATH(stream));
191 		/* Give up or recover. */
192 	    } else if (status != 0) {
193 		if (msg_verbose)
194 		    msg_info("%s: not found: %s", myname, endp_label);
195 		return (-1);
196 	    } else if (
197 #ifdef CANT_WRITE_BEFORE_SENDING_FD
198 		       attr_print(stream, ATTR_FLAG_NONE,
199 				  ATTR_TYPE_STR, MAIL_ATTR_DUMMY, "",
200 				  ATTR_TYPE_END) != 0
201 		       || vstream_fflush(stream) != 0
202 		       || read_wait(vstream_fileno(stream),
203 				    stream->timeout) < 0 ||	/* XXX */
204 #endif
205 		       (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0) {
206 		if (msg_verbose || (errno != EPIPE && errno != ENOENT))
207 		    msg_warn("problem talking to service %s: %m",
208 			     VSTREAM_PATH(stream));
209 		/* Give up or recover. */
210 	    } else {
211 #ifdef MUST_READ_AFTER_SENDING_FD
212 		(void) attr_print(stream, ATTR_FLAG_NONE,
213 				  ATTR_TYPE_STR, MAIL_ATTR_DUMMY, "",
214 				  ATTR_TYPE_END);
215 		(void) vstream_fflush(stream);
216 #endif
217 		if (msg_verbose)
218 		    msg_info("%s: endp=%s prop=%s fd=%d",
219 			     myname, endp_label, STR(endp_prop), fd);
220 		return (fd);
221 	    }
222 	}
223 	/* Give up or recover. */
224 	if (tries >= SCACHE_MAX_TRIES - 1) {
225 	    msg_warn("disabling connection caching");
226 	    auto_clnt_free(sp->auto_clnt);
227 	    sp->auto_clnt = 0;
228 	    return (-1);
229 	}
230 	sleep(1);				/* XXX make configurable */
231 	auto_clnt_recover(sp->auto_clnt);
232     }
233     return (-1);
234 }
235 
236 /* scache_clnt_save_dest - create destination/endpoint association */
237 
238 static void scache_clnt_save_dest(SCACHE *scache, int dest_ttl,
239 				          const char *dest_label,
240 				          const char *dest_prop,
241 				          const char *endp_label)
242 {
243     SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
244     const char *myname = "scache_clnt_save_dest";
245     VSTREAM *stream;
246     int     status;
247     int     tries;
248 
249     if (msg_verbose)
250 	msg_info("%s: dest_label=%s dest_prop=%s endp_label=%s",
251 		 myname, dest_label, dest_prop, endp_label);
252 
253     /*
254      * Sanity check.
255      */
256     if (dest_ttl <= 0)
257 	msg_panic("%s: bad dest_ttl: %d", myname, dest_ttl);
258 
259     /*
260      * Try a few times before disabling the cache. We use synchronous calls;
261      * the session cache service is CPU bound and making the client
262      * asynchronous would just complicate the code.
263      */
264     for (tries = 0; sp->auto_clnt != 0; tries++) {
265 	if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) {
266 	    errno = 0;
267 	    if (attr_print(stream, ATTR_FLAG_NONE,
268 			 ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_SAVE_DEST,
269 			   ATTR_TYPE_INT, MAIL_ATTR_TTL, dest_ttl,
270 			   ATTR_TYPE_STR, MAIL_ATTR_LABEL, dest_label,
271 			   ATTR_TYPE_STR, MAIL_ATTR_PROP, dest_prop,
272 			   ATTR_TYPE_STR, MAIL_ATTR_LABEL, endp_label,
273 			   ATTR_TYPE_END) != 0
274 		|| vstream_fflush(stream)
275 		|| attr_scan(stream, ATTR_FLAG_STRICT,
276 			     ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
277 			     ATTR_TYPE_END) != 1) {
278 		if (msg_verbose || (errno != EPIPE && errno != ENOENT))
279 		    msg_warn("problem talking to service %s: %m",
280 			     VSTREAM_PATH(stream));
281 		/* Give up or recover. */
282 	    } else {
283 		if (msg_verbose && status != 0)
284 		    msg_warn("%s: destination save failed with status %d",
285 			     myname, status);
286 		break;
287 	    }
288 	}
289 	/* Give up or recover. */
290 	if (tries >= SCACHE_MAX_TRIES - 1) {
291 	    msg_warn("disabling connection caching");
292 	    auto_clnt_free(sp->auto_clnt);
293 	    sp->auto_clnt = 0;
294 	    break;
295 	}
296 	sleep(1);				/* XXX make configurable */
297 	auto_clnt_recover(sp->auto_clnt);
298     }
299 }
300 
301 /* scache_clnt_find_dest - look up cached session */
302 
303 static int scache_clnt_find_dest(SCACHE *scache, const char *dest_label,
304 				         VSTRING *dest_prop,
305 				         VSTRING *endp_prop)
306 {
307     SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
308     const char *myname = "scache_clnt_find_dest";
309     VSTREAM *stream;
310     int     status;
311     int     tries;
312     int     fd;
313 
314     /*
315      * Try a few times before disabling the cache. We use synchronous calls;
316      * the session cache service is CPU bound and making the client
317      * asynchronous would just complicate the code.
318      */
319     for (tries = 0; sp->auto_clnt != 0; tries++) {
320 	if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) {
321 	    errno = 0;
322 	    if (attr_print(stream, ATTR_FLAG_NONE,
323 			 ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_FIND_DEST,
324 			   ATTR_TYPE_STR, MAIL_ATTR_LABEL, dest_label,
325 			   ATTR_TYPE_END) != 0
326 		|| vstream_fflush(stream)
327 		|| attr_scan(stream, ATTR_FLAG_STRICT,
328 			     ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
329 			     ATTR_TYPE_STR, MAIL_ATTR_PROP, dest_prop,
330 			     ATTR_TYPE_STR, MAIL_ATTR_PROP, endp_prop,
331 			     ATTR_TYPE_END) != 3) {
332 		if (msg_verbose || (errno != EPIPE && errno != ENOENT))
333 		    msg_warn("problem talking to service %s: %m",
334 			     VSTREAM_PATH(stream));
335 		/* Give up or recover. */
336 	    } else if (status != 0) {
337 		if (msg_verbose)
338 		    msg_info("%s: not found: %s", myname, dest_label);
339 		return (-1);
340 	    } else if (
341 #ifdef CANT_WRITE_BEFORE_SENDING_FD
342 		       attr_print(stream, ATTR_FLAG_NONE,
343 				  ATTR_TYPE_STR, MAIL_ATTR_DUMMY, "",
344 				  ATTR_TYPE_END) != 0
345 		       || vstream_fflush(stream) != 0
346 		       || read_wait(vstream_fileno(stream),
347 				    stream->timeout) < 0 ||	/* XXX */
348 #endif
349 		       (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0) {
350 		if (msg_verbose || (errno != EPIPE && errno != ENOENT))
351 		    msg_warn("problem talking to service %s: %m",
352 			     VSTREAM_PATH(stream));
353 		/* Give up or recover. */
354 	    } else {
355 #ifdef MUST_READ_AFTER_SENDING_FD
356 		(void) attr_print(stream, ATTR_FLAG_NONE,
357 				  ATTR_TYPE_STR, MAIL_ATTR_DUMMY, "",
358 				  ATTR_TYPE_END);
359 		(void) vstream_fflush(stream);
360 #endif
361 		if (msg_verbose)
362 		    msg_info("%s: dest=%s dest_prop=%s endp_prop=%s fd=%d",
363 		    myname, dest_label, STR(dest_prop), STR(endp_prop), fd);
364 		return (fd);
365 	    }
366 	}
367 	/* Give up or recover. */
368 	if (tries >= SCACHE_MAX_TRIES - 1) {
369 	    msg_warn("disabling connection caching");
370 	    auto_clnt_free(sp->auto_clnt);
371 	    sp->auto_clnt = 0;
372 	    return (-1);
373 	}
374 	sleep(1);				/* XXX make configurable */
375 	auto_clnt_recover(sp->auto_clnt);
376     }
377     return (-1);
378 }
379 
380 /* scache_clnt_size - dummy */
381 
382 static void scache_clnt_size(SCACHE *unused_scache, SCACHE_SIZE *size)
383 {
384     /* XXX Crap in a hurry. */
385     size->dest_count = 0;
386     size->endp_count = 0;
387     size->sess_count = 0;
388 }
389 
390 /* scache_clnt_free - destroy cache */
391 
392 static void scache_clnt_free(SCACHE *scache)
393 {
394     SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
395 
396     if (sp->auto_clnt)
397 	auto_clnt_free(sp->auto_clnt);
398 #ifdef CANT_WRITE_BEFORE_SENDING_FD
399     vstring_free(sp->dummy);
400 #endif
401     myfree((char *) sp);
402 }
403 
404 /* scache_clnt_create - initialize */
405 
406 SCACHE *scache_clnt_create(const char *server, int timeout,
407 			           int idle_limit, int ttl_limit)
408 {
409     SCACHE_CLNT *sp = (SCACHE_CLNT *) mymalloc(sizeof(*sp));
410     char   *service;
411 
412     sp->scache->save_endp = scache_clnt_save_endp;
413     sp->scache->find_endp = scache_clnt_find_endp;
414     sp->scache->save_dest = scache_clnt_save_dest;
415     sp->scache->find_dest = scache_clnt_find_dest;
416     sp->scache->size = scache_clnt_size;
417     sp->scache->free = scache_clnt_free;
418 
419     service = concatenate("local:private/", server, (char *) 0);
420     sp->auto_clnt = auto_clnt_create(service, timeout, idle_limit, ttl_limit);
421     myfree(service);
422 
423 #ifdef CANT_WRITE_BEFORE_SENDING_FD
424     sp->dummy = vstring_alloc(1);
425 #endif
426 
427     return (sp->scache);
428 }
429