Home | History | Annotate | Line # | Download | only in scache
      1 /*	$NetBSD: scache.c,v 1.4 2022/10/08 16:12:49 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	scache 8
      6 /* SUMMARY
      7 /*	Postfix shared connection cache server
      8 /* SYNOPSIS
      9 /*	\fBscache\fR [generic Postfix daemon options]
     10 /* DESCRIPTION
     11 /*	The \fBscache\fR(8) server maintains a shared multi-connection
     12 /*	cache. This information can be used by, for example, Postfix
     13 /*	SMTP clients or other Postfix delivery agents.
     14 /*
     15 /*	The connection cache is organized into logical destination
     16 /*	names, physical endpoint names, and connections.
     17 /*
     18 /*	As a specific example, logical SMTP destinations specify
     19 /*	(transport, domain, port), and physical SMTP endpoints
     20 /*	specify (transport, IP address, port).  An SMTP connection
     21 /*	may be saved after a successful mail transaction.
     22 /*
     23 /*	In the general case, one logical destination may refer to
     24 /*	zero or more physical endpoints, one physical endpoint may
     25 /*	be referenced by zero or more logical destinations, and
     26 /*	one endpoint may refer to zero or more connections.
     27 /*
     28 /*	The exact syntax of a logical destination or endpoint name
     29 /*	is application dependent; the \fBscache\fR(8) server does
     30 /*	not care.  A connection is stored as a file descriptor together
     31 /*	with application-dependent information that is needed to
     32 /*	re-activate a connection object. Again, the \fBscache\fR(8)
     33 /*	server is completely unaware of the details of that
     34 /*	information.
     35 /*
     36 /*	All information is stored with a finite time to live (ttl).
     37 /*	The connection cache daemon terminates when no client is
     38 /*	connected for \fBmax_idle\fR time units.
     39 /*
     40 /*	This server implements the following requests:
     41 /* .IP "\fBsave_endp\fI ttl endpoint endpoint_properties file_descriptor\fR"
     42 /*	Save the specified file descriptor and connection property data
     43 /*	under the specified endpoint name. The endpoint properties
     44 /*	are used by the client to re-activate a passivated connection
     45 /*	object.
     46 /* .IP "\fBfind_endp\fI endpoint\fR"
     47 /*	Look up cached properties and a cached file descriptor for the
     48 /*	specified endpoint.
     49 /* .IP "\fBsave_dest\fI ttl destination destination_properties endpoint\fR"
     50 /*	Save the binding between a logical destination and an
     51 /*	endpoint under the destination name, together with destination
     52 /*	specific connection properties. The destination properties
     53 /*	are used by the client to re-activate a passivated connection
     54 /*	object.
     55 /* .IP "\fBfind_dest\fI destination\fR"
     56 /*	Look up cached destination properties, cached endpoint properties,
     57 /*	and a cached file descriptor for the specified logical destination.
     58 /* SECURITY
     59 /* .ad
     60 /* .fi
     61 /*	The \fBscache\fR(8) server is not security-sensitive. It does not
     62 /*	talk to the network, and it does not talk to local users.
     63 /*	The \fBscache\fR(8) server can run chrooted at fixed low privilege.
     64 /*
     65 /*	The \fBscache\fR(8) server is not a trusted process. It must
     66 /*	not be used to store information that is security sensitive.
     67 /* DIAGNOSTICS
     68 /*	Problems and transactions are logged to \fBsyslogd\fR(8)
     69 /*	or \fBpostlogd\fR(8).
     70 /* BUGS
     71 /*	The session cache cannot be shared among multiple machines.
     72 /*
     73 /*	When a connection expires from the cache, it is closed without
     74 /*	the appropriate protocol specific handshake.
     75 /* CONFIGURATION PARAMETERS
     76 /* .ad
     77 /* .fi
     78 /*	Changes to \fBmain.cf\fR are picked up automatically as \fBscache\fR(8)
     79 /*	processes run for only a limited amount of time. Use the command
     80 /*	"\fBpostfix reload\fR" to speed up a change.
     81 /*
     82 /*	The text below provides only a parameter summary. See
     83 /*	\fBpostconf\fR(5) for more details including examples.
     84 /* RESOURCE CONTROLS
     85 /* .ad
     86 /* .fi
     87 /* .IP "\fBconnection_cache_ttl_limit (2s)\fR"
     88 /*	The maximal time-to-live value that the \fBscache\fR(8) connection
     89 /*	cache server
     90 /*	allows.
     91 /* .IP "\fBconnection_cache_status_update_time (600s)\fR"
     92 /*	How frequently the \fBscache\fR(8) server logs usage statistics with
     93 /*	connection cache hit and miss rates for logical destinations and for
     94 /*	physical endpoints.
     95 /* MISCELLANEOUS CONTROLS
     96 /* .ad
     97 /* .fi
     98 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
     99 /*	The default location of the Postfix main.cf and master.cf
    100 /*	configuration files.
    101 /* .IP "\fBdaemon_timeout (18000s)\fR"
    102 /*	How much time a Postfix daemon process may take to handle a
    103 /*	request before it is terminated by a built-in watchdog timer.
    104 /* .IP "\fBipc_timeout (3600s)\fR"
    105 /*	The time limit for sending or receiving information over an internal
    106 /*	communication channel.
    107 /* .IP "\fBmax_idle (100s)\fR"
    108 /*	The maximum amount of time that an idle Postfix daemon process waits
    109 /*	for an incoming connection before terminating voluntarily.
    110 /* .IP "\fBprocess_id (read-only)\fR"
    111 /*	The process ID of a Postfix command or daemon process.
    112 /* .IP "\fBprocess_name (read-only)\fR"
    113 /*	The process name of a Postfix command or daemon process.
    114 /* .IP "\fBsyslog_facility (mail)\fR"
    115 /*	The syslog facility of Postfix logging.
    116 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
    117 /*	A prefix that is prepended to the process name in syslog
    118 /*	records, so that, for example, "smtpd" becomes "prefix/smtpd".
    119 /* .PP
    120 /*	Available in Postfix 3.3 and later:
    121 /* .IP "\fBservice_name (read-only)\fR"
    122 /*	The master.cf service name of a Postfix daemon process.
    123 /* SEE ALSO
    124 /*	smtp(8), SMTP client
    125 /*	postconf(5), configuration parameters
    126 /*	master(8), process manager
    127 /*	postlogd(8), Postfix logging
    128 /*	syslogd(8), system logging
    129 /* README FILES
    130 /* .ad
    131 /* .fi
    132 /*	Use "\fBpostconf readme_directory\fR" or
    133 /*	"\fBpostconf html_directory\fR" to locate this information.
    134 /* .na
    135 /* .nf
    136 /*	CONNECTION_CACHE_README, Postfix connection cache
    137 /* LICENSE
    138 /* .ad
    139 /* .fi
    140 /*	The Secure Mailer license must be distributed with this software.
    141 /* HISTORY
    142 /*	This service was introduced with Postfix version 2.2.
    143 /* AUTHOR(S)
    144 /*	Wietse Venema
    145 /*	IBM T.J. Watson Research
    146 /*	P.O. Box 704
    147 /*	Yorktown Heights, NY 10598, USA
    148 /*
    149 /*	Wietse Venema
    150 /*	Google, Inc.
    151 /*	111 8th Avenue
    152 /*	New York, NY 10011, USA
    153 /*--*/
    154 
    155 /* System library. */
    156 
    157 #include <sys_defs.h>
    158 #include <time.h>
    159 
    160 /* Utility library. */
    161 
    162 #include <msg.h>
    163 #include <iostuff.h>
    164 #include <htable.h>
    165 #include <ring.h>
    166 #include <events.h>
    167 
    168 /* Global library. */
    169 
    170 #include <mail_params.h>
    171 #include <mail_version.h>
    172 #include <mail_proto.h>
    173 #include <scache.h>
    174 
    175 /* Single server skeleton. */
    176 
    177 #include <mail_server.h>
    178 #include <mail_conf.h>
    179 
    180 /* Application-specific. */
    181 
    182  /*
    183   * Tunable parameters.
    184   */
    185 int     var_scache_ttl_lim;
    186 int     var_scache_stat_time;
    187 
    188  /*
    189   * Request parameters.
    190   */
    191 static VSTRING *scache_request;
    192 static VSTRING *scache_dest_label;
    193 static VSTRING *scache_dest_prop;
    194 static VSTRING *scache_endp_label;
    195 static VSTRING *scache_endp_prop;
    196 
    197 #ifdef CANT_WRITE_BEFORE_SENDING_FD
    198 static VSTRING *scache_dummy;
    199 
    200 #endif
    201 
    202  /*
    203   * Session cache instance.
    204   */
    205 static SCACHE *scache;
    206 
    207  /*
    208   * Statistics.
    209   */
    210 static int scache_dest_hits;
    211 static int scache_dest_miss;
    212 static int scache_dest_count;
    213 static int scache_endp_hits;
    214 static int scache_endp_miss;
    215 static int scache_endp_count;
    216 static int scache_sess_count;
    217 time_t  scache_start_time;
    218 
    219  /*
    220   * Silly little macros.
    221   */
    222 #define STR(x)			vstring_str(x)
    223 #define VSTREQ(x,y)		(strcmp(STR(x),y) == 0)
    224 
    225 /* scache_save_endp_service - protocol to save endpoint->stream binding */
    226 
    227 static void scache_save_endp_service(VSTREAM *client_stream)
    228 {
    229     const char *myname = "scache_save_endp_service";
    230     int     ttl;
    231     int     fd;
    232     SCACHE_SIZE size;
    233 
    234     if (attr_scan(client_stream,
    235 		  ATTR_FLAG_STRICT,
    236 		  RECV_ATTR_INT(MAIL_ATTR_TTL, &ttl),
    237 		  RECV_ATTR_STR(MAIL_ATTR_LABEL, scache_endp_label),
    238 		  RECV_ATTR_STR(MAIL_ATTR_PROP, scache_endp_prop),
    239 		  ATTR_TYPE_END) != 3
    240 	|| ttl <= 0) {
    241 	msg_warn("%s: bad or missing request parameter", myname);
    242 	attr_print(client_stream, ATTR_FLAG_NONE,
    243 		   SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_BAD),
    244 		   ATTR_TYPE_END);
    245 	return;
    246     } else if (
    247 #ifdef CANT_WRITE_BEFORE_SENDING_FD
    248 	       attr_print(client_stream, ATTR_FLAG_NONE,
    249 			  SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""),
    250 			  ATTR_TYPE_END) != 0
    251 	       || vstream_fflush(client_stream) != 0
    252 	       || read_wait(vstream_fileno(client_stream),
    253 			    client_stream->timeout) < 0	/* XXX */
    254 	       ||
    255 #endif
    256 	       (fd = LOCAL_RECV_FD(vstream_fileno(client_stream))) < 0) {
    257 	msg_warn("%s: unable to receive file descriptor: %m", myname);
    258 	(void) attr_print(client_stream, ATTR_FLAG_NONE,
    259 			  SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_FAIL),
    260 			  ATTR_TYPE_END);
    261 	return;
    262     } else {
    263 	scache_save_endp(scache,
    264 			 ttl > var_scache_ttl_lim ? var_scache_ttl_lim : ttl,
    265 			 STR(scache_endp_label), STR(scache_endp_prop), fd);
    266 	(void) attr_print(client_stream, ATTR_FLAG_NONE,
    267 			  SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_OK),
    268 			  ATTR_TYPE_END);
    269 	scache_size(scache, &size);
    270 	if (size.endp_count > scache_endp_count)
    271 	    scache_endp_count = size.endp_count;
    272 	if (size.sess_count > scache_sess_count)
    273 	    scache_sess_count = size.sess_count;
    274 	return;
    275     }
    276 }
    277 
    278 /* scache_find_endp_service - protocol to find connection for endpoint */
    279 
    280 static void scache_find_endp_service(VSTREAM *client_stream)
    281 {
    282     const char *myname = "scache_find_endp_service";
    283     int     fd;
    284 
    285     if (attr_scan(client_stream,
    286 		  ATTR_FLAG_STRICT,
    287 		  RECV_ATTR_STR(MAIL_ATTR_LABEL, scache_endp_label),
    288 		  ATTR_TYPE_END) != 1) {
    289 	msg_warn("%s: bad or missing request parameter", myname);
    290 	attr_print(client_stream, ATTR_FLAG_NONE,
    291 		   SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_BAD),
    292 		   SEND_ATTR_STR(MAIL_ATTR_PROP, ""),
    293 		   ATTR_TYPE_END);
    294 	return;
    295     } else if ((fd = scache_find_endp(scache, STR(scache_endp_label),
    296 				      scache_endp_prop)) < 0) {
    297 	attr_print(client_stream, ATTR_FLAG_NONE,
    298 		   SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_FAIL),
    299 		   SEND_ATTR_STR(MAIL_ATTR_PROP, ""),
    300 		   ATTR_TYPE_END);
    301 	scache_endp_miss++;
    302 	return;
    303     } else {
    304 	attr_print(client_stream, ATTR_FLAG_NONE,
    305 		   SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_OK),
    306 		   SEND_ATTR_STR(MAIL_ATTR_PROP, STR(scache_endp_prop)),
    307 		   ATTR_TYPE_END);
    308 	if (vstream_fflush(client_stream) != 0
    309 #ifdef CANT_WRITE_BEFORE_SENDING_FD
    310 	    || attr_scan(client_stream, ATTR_FLAG_STRICT,
    311 			 RECV_ATTR_STR(MAIL_ATTR_DUMMY, scache_dummy),
    312 			 ATTR_TYPE_END) != 1
    313 #endif
    314 	    || LOCAL_SEND_FD(vstream_fileno(client_stream), fd) < 0
    315 #ifdef MUST_READ_AFTER_SENDING_FD
    316 	    || attr_scan(client_stream, ATTR_FLAG_STRICT,
    317 			 RECV_ATTR_STR(MAIL_ATTR_DUMMY, scache_dummy),
    318 			 ATTR_TYPE_END) != 1
    319 #endif
    320 	    )
    321 	    msg_warn("%s: cannot send file descriptor: %m", myname);
    322 	if (close(fd) < 0)
    323 	    msg_warn("close(%d): %m", fd);
    324 	scache_endp_hits++;
    325 	return;
    326     }
    327 }
    328 
    329 /* scache_save_dest_service - protocol to save destination->endpoint binding */
    330 
    331 static void scache_save_dest_service(VSTREAM *client_stream)
    332 {
    333     const char *myname = "scache_save_dest_service";
    334     int     ttl;
    335     SCACHE_SIZE size;
    336 
    337     if (attr_scan(client_stream,
    338 		  ATTR_FLAG_STRICT,
    339 		  RECV_ATTR_INT(MAIL_ATTR_TTL, &ttl),
    340 		  RECV_ATTR_STR(MAIL_ATTR_LABEL, scache_dest_label),
    341 		  RECV_ATTR_STR(MAIL_ATTR_PROP, scache_dest_prop),
    342 		  RECV_ATTR_STR(MAIL_ATTR_LABEL, scache_endp_label),
    343 		  ATTR_TYPE_END) != 4
    344 	|| ttl <= 0) {
    345 	msg_warn("%s: bad or missing request parameter", myname);
    346 	attr_print(client_stream, ATTR_FLAG_NONE,
    347 		   SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_BAD),
    348 		   ATTR_TYPE_END);
    349 	return;
    350     } else {
    351 	scache_save_dest(scache,
    352 			 ttl > var_scache_ttl_lim ? var_scache_ttl_lim : ttl,
    353 			 STR(scache_dest_label), STR(scache_dest_prop),
    354 			 STR(scache_endp_label));
    355 	attr_print(client_stream, ATTR_FLAG_NONE,
    356 		   SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_OK),
    357 		   ATTR_TYPE_END);
    358 	scache_size(scache, &size);
    359 	if (size.dest_count > scache_dest_count)
    360 	    scache_dest_count = size.dest_count;
    361 	if (size.endp_count > scache_endp_count)
    362 	    scache_endp_count = size.endp_count;
    363 	return;
    364     }
    365 }
    366 
    367 /* scache_find_dest_service - protocol to find connection for destination */
    368 
    369 static void scache_find_dest_service(VSTREAM *client_stream)
    370 {
    371     const char *myname = "scache_find_dest_service";
    372     int     fd;
    373 
    374     if (attr_scan(client_stream,
    375 		  ATTR_FLAG_STRICT,
    376 		  RECV_ATTR_STR(MAIL_ATTR_LABEL, scache_dest_label),
    377 		  ATTR_TYPE_END) != 1) {
    378 	msg_warn("%s: bad or missing request parameter", myname);
    379 	attr_print(client_stream, ATTR_FLAG_NONE,
    380 		   SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_BAD),
    381 		   SEND_ATTR_STR(MAIL_ATTR_PROP, ""),
    382 		   SEND_ATTR_STR(MAIL_ATTR_PROP, ""),
    383 		   ATTR_TYPE_END);
    384 	return;
    385     } else if ((fd = scache_find_dest(scache, STR(scache_dest_label),
    386 				      scache_dest_prop,
    387 				      scache_endp_prop)) < 0) {
    388 	attr_print(client_stream, ATTR_FLAG_NONE,
    389 		   SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_FAIL),
    390 		   SEND_ATTR_STR(MAIL_ATTR_PROP, ""),
    391 		   SEND_ATTR_STR(MAIL_ATTR_PROP, ""),
    392 		   ATTR_TYPE_END);
    393 	scache_dest_miss++;
    394 	return;
    395     } else {
    396 	attr_print(client_stream, ATTR_FLAG_NONE,
    397 		   SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_OK),
    398 		   SEND_ATTR_STR(MAIL_ATTR_PROP, STR(scache_dest_prop)),
    399 		   SEND_ATTR_STR(MAIL_ATTR_PROP, STR(scache_endp_prop)),
    400 		   ATTR_TYPE_END);
    401 	if (vstream_fflush(client_stream) != 0
    402 #ifdef CANT_WRITE_BEFORE_SENDING_FD
    403 	    || attr_scan(client_stream, ATTR_FLAG_STRICT,
    404 			 RECV_ATTR_STR(MAIL_ATTR_DUMMY, scache_dummy),
    405 			 ATTR_TYPE_END) != 1
    406 #endif
    407 	    || LOCAL_SEND_FD(vstream_fileno(client_stream), fd) < 0
    408 #ifdef MUST_READ_AFTER_SENDING_FD
    409 	    || attr_scan(client_stream, ATTR_FLAG_STRICT,
    410 			 RECV_ATTR_STR(MAIL_ATTR_DUMMY, scache_dummy),
    411 			 ATTR_TYPE_END) != 1
    412 #endif
    413 	    )
    414 	    msg_warn("%s: cannot send file descriptor: %m", myname);
    415 	if (close(fd) < 0)
    416 	    msg_warn("close(%d): %m", fd);
    417 	scache_dest_hits++;
    418 	return;
    419     }
    420 }
    421 
    422 /* scache_service - perform service for client */
    423 
    424 static void scache_service(VSTREAM *client_stream, char *unused_service,
    425 			           char **argv)
    426 {
    427 
    428     /*
    429      * Sanity check. This service takes no command-line arguments.
    430      */
    431     if (argv[0])
    432 	msg_fatal("unexpected command-line argument: %s", argv[0]);
    433 
    434     /*
    435      * This routine runs whenever a client connects to the UNIX-domain socket
    436      * dedicated to the scache service. All connection-management stuff is
    437      * handled by the common code in multi_server.c.
    438      *
    439      * XXX Workaround: with some requests, the client sends a dummy message
    440      * after the server replies (yes that's a botch). When the scache server
    441      * is slow, this dummy message may become concatenated with the next
    442      * request from the same client. The do-while loop below will repeat
    443      * instead of discarding the client request. We must process it now
    444      * because there will be no select() notification.
    445      */
    446     do {
    447 	if (attr_scan(client_stream,
    448 		      ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
    449 		      RECV_ATTR_STR(MAIL_ATTR_REQ, scache_request),
    450 		      ATTR_TYPE_END) == 1) {
    451 	    if (VSTREQ(scache_request, SCACHE_REQ_SAVE_DEST)) {
    452 		scache_save_dest_service(client_stream);
    453 	    } else if (VSTREQ(scache_request, SCACHE_REQ_FIND_DEST)) {
    454 		scache_find_dest_service(client_stream);
    455 	    } else if (VSTREQ(scache_request, SCACHE_REQ_SAVE_ENDP)) {
    456 		scache_save_endp_service(client_stream);
    457 	    } else if (VSTREQ(scache_request, SCACHE_REQ_FIND_ENDP)) {
    458 		scache_find_endp_service(client_stream);
    459 	    } else {
    460 		msg_warn("unrecognized request: \"%s\", ignored",
    461 			 STR(scache_request));
    462 		attr_print(client_stream, ATTR_FLAG_NONE,
    463 			   SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_BAD),
    464 			   ATTR_TYPE_END);
    465 	    }
    466 	}
    467     } while (vstream_peek(client_stream) > 0);
    468     vstream_fflush(client_stream);
    469 }
    470 
    471 /* scache_status_dump - log and reset cache statistics */
    472 
    473 static void scache_status_dump(char *unused_name, char **unused_argv)
    474 {
    475     if (scache_dest_hits || scache_dest_miss
    476 	|| scache_endp_hits || scache_endp_miss
    477 	|| scache_dest_count || scache_endp_count
    478 	|| scache_sess_count)
    479 	msg_info("statistics: start interval %.15s",
    480 		 ctime(&scache_start_time) + 4);
    481 
    482     if (scache_dest_hits || scache_dest_miss) {
    483 	msg_info("statistics: domain lookup hits=%d miss=%d success=%d%%",
    484 		 scache_dest_hits, scache_dest_miss,
    485 		 scache_dest_hits * 100
    486 		 / (scache_dest_hits + scache_dest_miss));
    487 	scache_dest_hits = scache_dest_miss = 0;
    488     }
    489     if (scache_endp_hits || scache_endp_miss) {
    490 	msg_info("statistics: address lookup hits=%d miss=%d success=%d%%",
    491 		 scache_endp_hits, scache_endp_miss,
    492 		 scache_endp_hits * 100
    493 		 / (scache_endp_hits + scache_endp_miss));
    494 	scache_endp_hits = scache_endp_miss = 0;
    495     }
    496     if (scache_dest_count || scache_endp_count || scache_sess_count) {
    497 	msg_info("statistics: max simultaneous domains=%d addresses=%d connection=%d",
    498 		 scache_dest_count, scache_endp_count, scache_sess_count);
    499 	scache_dest_count = 0;
    500 	scache_endp_count = 0;
    501 	scache_sess_count = 0;
    502     }
    503     scache_start_time = event_time();
    504 }
    505 
    506 /* scache_status_update - log and reset cache statistics periodically */
    507 
    508 static void scache_status_update(int unused_event, void *context)
    509 {
    510     scache_status_dump((char *) 0, (char **) 0);
    511     event_request_timer(scache_status_update, context, var_scache_stat_time);
    512 }
    513 
    514 /* post_jail_init - initialization after privilege drop */
    515 
    516 static void post_jail_init(char *unused_name, char **unused_argv)
    517 {
    518 
    519     /*
    520      * Pre-allocate the cache instance.
    521      */
    522     scache = scache_multi_create();
    523 
    524     /*
    525      * Pre-allocate buffers.
    526      */
    527     scache_request = vstring_alloc(10);
    528     scache_dest_label = vstring_alloc(10);
    529     scache_dest_prop = vstring_alloc(10);
    530     scache_endp_label = vstring_alloc(10);
    531     scache_endp_prop = vstring_alloc(10);
    532 #ifdef CANT_WRITE_BEFORE_SENDING_FD
    533     scache_dummy = vstring_alloc(10);
    534 #endif
    535 
    536     /*
    537      * Disable the max_use limit. We still terminate when no client is
    538      * connected for $idle_limit time units.
    539      */
    540     var_use_limit = 0;
    541 
    542     /*
    543      * Dump and reset cache statistics every so often.
    544      */
    545     event_request_timer(scache_status_update, (void *) 0, var_scache_stat_time);
    546     scache_start_time = event_time();
    547 }
    548 
    549 /* scache_post_accept - announce our protocol */
    550 
    551 static void scache_post_accept(VSTREAM *stream, char *unused_name,
    552 			           char **unused_argv, HTABLE *unused_table)
    553 {
    554 
    555     /*
    556      * Announce the protocol.
    557      */
    558     attr_print(stream, ATTR_FLAG_NONE,
    559 	       SEND_ATTR_STR(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_SCACHE),
    560 	       ATTR_TYPE_END);
    561     (void) vstream_fflush(stream);
    562 }
    563 
    564 MAIL_VERSION_STAMP_DECLARE;
    565 
    566 /* main - pass control to the multi-threaded skeleton */
    567 
    568 int     main(int argc, char **argv)
    569 {
    570     static const CONFIG_TIME_TABLE time_table[] = {
    571 	VAR_SCACHE_TTL_LIM, DEF_SCACHE_TTL_LIM, &var_scache_ttl_lim, 1, 0,
    572 	VAR_SCACHE_STAT_TIME, DEF_SCACHE_STAT_TIME, &var_scache_stat_time, 1, 0,
    573 	0,
    574     };
    575 
    576     /*
    577      * Fingerprint executables and core dumps.
    578      */
    579     MAIL_VERSION_STAMP_ALLOCATE;
    580 
    581     multi_server_main(argc, argv, scache_service,
    582 		      CA_MAIL_SERVER_TIME_TABLE(time_table),
    583 		      CA_MAIL_SERVER_POST_INIT(post_jail_init),
    584 		      CA_MAIL_SERVER_POST_ACCEPT(scache_post_accept),
    585 		      CA_MAIL_SERVER_EXIT(scache_status_dump),
    586 		      CA_MAIL_SERVER_SOLITARY,
    587 		      0);
    588 }
    589