7 /* session cache manager client
11 /* SCACHE *scache_clnt_create(server, timeout, idle_limit, ttl_limit)
12 /* const char *server;
17 /* This module implements the client-side protocol of the
18 /* session cache service.
20 /* scache_clnt_create() creates a session cache service client.
24 /* The session cache service name.
26 /* Time limit for connect, send or receive operations.
28 /* Idle time after which the client disconnects.
30 /* Upper bound on the time that a connection is allowed to persist.
32 /* Fatal error: memory allocation problem;
33 /* warning: communication error;
34 /* panic: internal consistency failure.
36 /* scache(3), generic session cache API
40 /* The Secure Mailer license must be distributed with this software.
43 /* IBM T.J. Watson Research
45 /* Yorktown Heights, NY 10598, USA
53 /* Utility library. */
57 #include <auto_clnt.h>
58 #include <stringops.h>
60 /*#define msg_verbose 1*/
64 #include <mail_proto.h>
65 #include <mail_params.h>
68 /* Application-specific. */
71 * SCACHE_CLNT is a derived type from the SCACHE super-class.
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 */
81 #define STR(x) vstring_str(x)
83 #define SCACHE_MAX_TRIES 2
85 /* scache_clnt_save_endp - save endpoint */
87 static void scache_clnt_save_endp(SCACHE
*scache
, int endp_ttl
,
88 const char *endp_label
,
89 const char *endp_prop
, int fd
)
91 SCACHE_CLNT
*sp
= (SCACHE_CLNT
*) scache
;
92 const char *myname
= "scache_clnt_save_endp";
99 msg_info("%s: endp=%s prop=%s fd=%d",
100 myname
, endp_label
, endp_prop
, fd
);
106 msg_panic("%s: bad endp_ttl: %d", myname
, endp_ttl
);
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.
113 for (tries
= 0; sp
->auto_clnt
!= 0; tries
++) {
114 if ((stream
= auto_clnt_access(sp
->auto_clnt
)) != 0) {
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
,
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
,
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. */
138 if (msg_verbose
&& status
!= 0)
139 msg_warn("%s: descriptor save failed with status %d",
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
);
151 sleep(1); /* XXX make configurable */
152 auto_clnt_recover(sp
->auto_clnt
);
154 /* Always close the descriptor before returning. */
156 msg_warn("%s: close(%d): %m", myname
, fd
);
159 /* scache_clnt_find_endp - look up cached session */
161 static int scache_clnt_find_endp(SCACHE
*scache
, const char *endp_label
,
164 SCACHE_CLNT
*sp
= (SCACHE_CLNT
*) scache
;
165 const char *myname
= "scache_clnt_find_endp";
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.
176 for (tries
= 0; sp
->auto_clnt
!= 0; tries
++) {
177 if ((stream
= auto_clnt_access(sp
->auto_clnt
)) != 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
,
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) {
194 msg_info("%s: not found: %s", myname
, endp_label
);
197 #ifdef CANT_WRITE_BEFORE_SENDING_FD
198 attr_print(stream
, ATTR_FLAG_NONE
,
199 ATTR_TYPE_STR
, MAIL_ATTR_DUMMY
, "",
201 || vstream_fflush(stream
) != 0
202 || read_wait(vstream_fileno(stream
),
203 stream
->timeout
) < 0 || /* XXX */
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. */
211 #ifdef MUST_READ_AFTER_SENDING_FD
212 (void) attr_print(stream
, ATTR_FLAG_NONE
,
213 ATTR_TYPE_STR
, MAIL_ATTR_DUMMY
, "",
215 (void) vstream_fflush(stream
);
218 msg_info("%s: endp=%s prop=%s fd=%d",
219 myname
, endp_label
, STR(endp_prop
), fd
);
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
);
230 sleep(1); /* XXX make configurable */
231 auto_clnt_recover(sp
->auto_clnt
);
236 /* scache_clnt_save_dest - create destination/endpoint association */
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
)
243 SCACHE_CLNT
*sp
= (SCACHE_CLNT
*) scache
;
244 const char *myname
= "scache_clnt_save_dest";
250 msg_info("%s: dest_label=%s dest_prop=%s endp_label=%s",
251 myname
, dest_label
, dest_prop
, endp_label
);
257 msg_panic("%s: bad dest_ttl: %d", myname
, dest_ttl
);
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.
264 for (tries
= 0; sp
->auto_clnt
!= 0; tries
++) {
265 if ((stream
= auto_clnt_access(sp
->auto_clnt
)) != 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
,
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. */
283 if (msg_verbose
&& status
!= 0)
284 msg_warn("%s: destination save failed with status %d",
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
);
296 sleep(1); /* XXX make configurable */
297 auto_clnt_recover(sp
->auto_clnt
);
301 /* scache_clnt_find_dest - look up cached session */
303 static int scache_clnt_find_dest(SCACHE
*scache
, const char *dest_label
,
307 SCACHE_CLNT
*sp
= (SCACHE_CLNT
*) scache
;
308 const char *myname
= "scache_clnt_find_dest";
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.
319 for (tries
= 0; sp
->auto_clnt
!= 0; tries
++) {
320 if ((stream
= auto_clnt_access(sp
->auto_clnt
)) != 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
,
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) {
338 msg_info("%s: not found: %s", myname
, dest_label
);
341 #ifdef CANT_WRITE_BEFORE_SENDING_FD
342 attr_print(stream
, ATTR_FLAG_NONE
,
343 ATTR_TYPE_STR
, MAIL_ATTR_DUMMY
, "",
345 || vstream_fflush(stream
) != 0
346 || read_wait(vstream_fileno(stream
),
347 stream
->timeout
) < 0 || /* XXX */
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. */
355 #ifdef MUST_READ_AFTER_SENDING_FD
356 (void) attr_print(stream
, ATTR_FLAG_NONE
,
357 ATTR_TYPE_STR
, MAIL_ATTR_DUMMY
, "",
359 (void) vstream_fflush(stream
);
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
);
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
);
374 sleep(1); /* XXX make configurable */
375 auto_clnt_recover(sp
->auto_clnt
);
380 /* scache_clnt_size - dummy */
382 static void scache_clnt_size(SCACHE
*unused_scache
, SCACHE_SIZE
*size
)
384 /* XXX Crap in a hurry. */
385 size
->dest_count
= 0;
386 size
->endp_count
= 0;
387 size
->sess_count
= 0;
390 /* scache_clnt_free - destroy cache */
392 static void scache_clnt_free(SCACHE
*scache
)
394 SCACHE_CLNT
*sp
= (SCACHE_CLNT
*) scache
;
397 auto_clnt_free(sp
->auto_clnt
);
398 #ifdef CANT_WRITE_BEFORE_SENDING_FD
399 vstring_free(sp
->dummy
);
404 /* scache_clnt_create - initialize */
406 SCACHE
*scache_clnt_create(const char *server
, int timeout
,
407 int idle_limit
, int ttl_limit
)
409 SCACHE_CLNT
*sp
= (SCACHE_CLNT
*) mymalloc(sizeof(*sp
));
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
;
419 service
= concatenate("local:private/", var_scache_service
, (char *) 0);
420 sp
->auto_clnt
= auto_clnt_create(service
, timeout
, idle_limit
, ttl_limit
);
423 #ifdef CANT_WRITE_BEFORE_SENDING_FD
424 sp
->dummy
= vstring_alloc(1);