xref: /illumos-gate/usr/src/cmd/sendmail/src/mci.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * Copyright (c) 1998-2005 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1995-1997 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1988, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13 
14 #pragma ident	"%Z%%M%	%I%	%E% SMI"
15 
16 #include <sendmail.h>
17 
18 SM_RCSID("@(#)$Id: mci.c,v 8.214 2005/02/04 22:01:45 ca Exp $")
19 
20 #if NETINET || NETINET6
21 # include <arpa/inet.h>
22 #endif /* NETINET || NETINET6 */
23 
24 #include <dirent.h>
25 
26 static int	mci_generate_persistent_path __P((const char *, char *,
27 						  int, bool));
28 static bool	mci_load_persistent __P((MCI *));
29 static void	mci_uncache __P((MCI **, bool));
30 static int	mci_lock_host_statfile __P((MCI *));
31 static int	mci_read_persistent __P((SM_FILE_T *, MCI *));
32 
33 /*
34 **  Mail Connection Information (MCI) Caching Module.
35 **
36 **	There are actually two separate things cached.  The first is
37 **	the set of all open connections -- these are stored in a
38 **	(small) list.  The second is stored in the symbol table; it
39 **	has the overall status for all hosts, whether or not there
40 **	is a connection open currently.
41 **
42 **	There should never be too many connections open (since this
43 **	could flood the socket table), nor should a connection be
44 **	allowed to sit idly for too long.
45 **
46 **	MaxMciCache is the maximum number of open connections that
47 **	will be supported.
48 **
49 **	MciCacheTimeout is the time (in seconds) that a connection
50 **	is permitted to survive without activity.
51 **
52 **	We actually try any cached connections by sending a NOOP
53 **	before we use them; if the NOOP fails we close down the
54 **	connection and reopen it.  Note that this means that a
55 **	server SMTP that doesn't support NOOP will hose the
56 **	algorithm -- but that doesn't seem too likely.
57 **
58 **	The persistent MCI code is donated by Mark Lovell and Paul
59 **	Vixie.  It is based on the long term host status code in KJS
60 **	written by Paul but has been adapted by Mark to fit into the
61 **	MCI structure.
62 */
63 
64 static MCI	**MciCache;		/* the open connection cache */
65 
66 /*
67 **  MCI_CACHE -- enter a connection structure into the open connection cache
68 **
69 **	This may cause something else to be flushed.
70 **
71 **	Parameters:
72 **		mci -- the connection to cache.
73 **
74 **	Returns:
75 **		none.
76 */
77 
78 void
79 mci_cache(mci)
80 	register MCI *mci;
81 {
82 	register MCI **mcislot;
83 
84 	/*
85 	**  Find the best slot.  This may cause expired connections
86 	**  to be closed.
87 	*/
88 
89 	mcislot = mci_scan(mci);
90 	if (mcislot == NULL)
91 	{
92 		/* we don't support caching */
93 		return;
94 	}
95 
96 	if (mci->mci_host == NULL)
97 		return;
98 
99 	/* if this is already cached, we are done */
100 	if (bitset(MCIF_CACHED, mci->mci_flags))
101 		return;
102 
103 	/* otherwise we may have to clear the slot */
104 	if (*mcislot != NULL)
105 		mci_uncache(mcislot, true);
106 
107 	if (tTd(42, 5))
108 		sm_dprintf("mci_cache: caching %p (%s) in slot %d\n",
109 			   mci, mci->mci_host, (int) (mcislot - MciCache));
110 	if (tTd(91, 100))
111 		sm_syslog(LOG_DEBUG, CurEnv->e_id,
112 			  "mci_cache: caching %lx (%.100s) in slot %d",
113 			  (unsigned long) mci, mci->mci_host,
114 			  (int) (mcislot - MciCache));
115 
116 	*mcislot = mci;
117 	mci->mci_flags |= MCIF_CACHED;
118 }
119 /*
120 **  MCI_SCAN -- scan the cache, flush junk, and return best slot
121 **
122 **	Parameters:
123 **		savemci -- never flush this one.  Can be null.
124 **
125 **	Returns:
126 **		The LRU (or empty) slot.
127 */
128 
129 MCI **
130 mci_scan(savemci)
131 	MCI *savemci;
132 {
133 	time_t now;
134 	register MCI **bestmci;
135 	register MCI *mci;
136 	register int i;
137 
138 	if (MaxMciCache <= 0)
139 	{
140 		/* we don't support caching */
141 		return NULL;
142 	}
143 
144 	if (MciCache == NULL)
145 	{
146 		/* first call */
147 		MciCache = (MCI **) sm_pmalloc_x(MaxMciCache * sizeof *MciCache);
148 		memset((char *) MciCache, '\0', MaxMciCache * sizeof *MciCache);
149 		return &MciCache[0];
150 	}
151 
152 	now = curtime();
153 	bestmci = &MciCache[0];
154 	for (i = 0; i < MaxMciCache; i++)
155 	{
156 		mci = MciCache[i];
157 		if (mci == NULL || mci->mci_state == MCIS_CLOSED)
158 		{
159 			bestmci = &MciCache[i];
160 			continue;
161 		}
162 		if ((mci->mci_lastuse + MciCacheTimeout <= now ||
163 		     (mci->mci_mailer != NULL &&
164 		      mci->mci_mailer->m_maxdeliveries > 0 &&
165 		      mci->mci_deliveries + 1 >= mci->mci_mailer->m_maxdeliveries))&&
166 		    mci != savemci)
167 		{
168 			/* connection idle too long or too many deliveries */
169 			bestmci = &MciCache[i];
170 
171 			/* close it */
172 			mci_uncache(bestmci, true);
173 			continue;
174 		}
175 		if (*bestmci == NULL)
176 			continue;
177 		if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
178 			bestmci = &MciCache[i];
179 	}
180 	return bestmci;
181 }
182 /*
183 **  MCI_UNCACHE -- remove a connection from a slot.
184 **
185 **	May close a connection.
186 **
187 **	Parameters:
188 **		mcislot -- the slot to empty.
189 **		doquit -- if true, send QUIT protocol on this connection.
190 **			  if false, we are assumed to be in a forked child;
191 **				all we want to do is close the file(s).
192 **
193 **	Returns:
194 **		none.
195 */
196 
197 static void
198 mci_uncache(mcislot, doquit)
199 	register MCI **mcislot;
200 	bool doquit;
201 {
202 	register MCI *mci;
203 	extern ENVELOPE BlankEnvelope;
204 
205 	mci = *mcislot;
206 	if (mci == NULL)
207 		return;
208 	*mcislot = NULL;
209 	if (mci->mci_host == NULL)
210 		return;
211 
212 	mci_unlock_host(mci);
213 
214 	if (tTd(42, 5))
215 		sm_dprintf("mci_uncache: uncaching %p (%s) from slot %d (%d)\n",
216 			   mci, mci->mci_host, (int) (mcislot - MciCache),
217 			   doquit);
218 	if (tTd(91, 100))
219 		sm_syslog(LOG_DEBUG, CurEnv->e_id,
220 			  "mci_uncache: uncaching %lx (%.100s) from slot %d (%d)",
221 			  (unsigned long) mci, mci->mci_host,
222 			  (int) (mcislot - MciCache), doquit);
223 
224 	mci->mci_deliveries = 0;
225 	if (doquit)
226 	{
227 		message("Closing connection to %s", mci->mci_host);
228 
229 		mci->mci_flags &= ~MCIF_CACHED;
230 
231 		/* only uses the envelope to flush the transcript file */
232 		if (mci->mci_state != MCIS_CLOSED)
233 			smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
234 #if XLA
235 		xla_host_end(mci->mci_host);
236 #endif /* XLA */
237 	}
238 	else
239 	{
240 		if (mci->mci_in != NULL)
241 			(void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
242 		if (mci->mci_out != NULL)
243 			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
244 		mci->mci_in = mci->mci_out = NULL;
245 		mci->mci_state = MCIS_CLOSED;
246 		mci->mci_exitstat = EX_OK;
247 		mci->mci_errno = 0;
248 		mci->mci_flags = 0;
249 
250 		mci->mci_retryrcpt = false;
251 		mci->mci_tolist = NULL;
252 #if PIPELINING
253 		mci->mci_okrcpts = 0;
254 #endif /* PIPELINING */
255 	}
256 
257 	SM_FREE_CLR(mci->mci_status);
258 	SM_FREE_CLR(mci->mci_rstatus);
259 	SM_FREE_CLR(mci->mci_heloname);
260 	if (mci->mci_rpool != NULL)
261 	{
262 		sm_rpool_free(mci->mci_rpool);
263 		mci->mci_macro.mac_rpool = NULL;
264 		mci->mci_rpool = NULL;
265 	}
266 }
267 /*
268 **  MCI_FLUSH -- flush the entire cache
269 **
270 **	Parameters:
271 **		doquit -- if true, send QUIT protocol.
272 **			  if false, just close the connection.
273 **		allbut -- but leave this one open.
274 **
275 **	Returns:
276 **		none.
277 */
278 
279 void
280 mci_flush(doquit, allbut)
281 	bool doquit;
282 	MCI *allbut;
283 {
284 	register int i;
285 
286 	if (MciCache == NULL)
287 		return;
288 
289 	for (i = 0; i < MaxMciCache; i++)
290 	{
291 		if (allbut != MciCache[i])
292 			mci_uncache(&MciCache[i], doquit);
293 	}
294 }
295 /*
296 **  MCI_GET -- get information about a particular host
297 **
298 **	Parameters:
299 **		host -- host to look for.
300 **		m -- mailer.
301 **
302 **	Returns:
303 **		mci for this host (might be new).
304 */
305 
306 MCI *
307 mci_get(host, m)
308 	char *host;
309 	MAILER *m;
310 {
311 	register MCI *mci;
312 	register STAB *s;
313 	extern SOCKADDR CurHostAddr;
314 
315 	/* clear CurHostAddr so we don't get a bogus address with this name */
316 	memset(&CurHostAddr, '\0', sizeof CurHostAddr);
317 
318 	/* clear out any expired connections */
319 	(void) mci_scan(NULL);
320 
321 	if (m->m_mno < 0)
322 		syserr("!negative mno %d (%s)", m->m_mno, m->m_name);
323 
324 	s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
325 	mci = &s->s_mci;
326 
327 	/* initialize per-message data */
328 	mci->mci_retryrcpt = false;
329 	mci->mci_tolist = NULL;
330 #if PIPELINING
331 	mci->mci_okrcpts = 0;
332 #endif /* PIPELINING */
333 
334 	if (mci->mci_rpool == NULL)
335 		mci->mci_rpool = sm_rpool_new_x(NULL);
336 
337 	if (mci->mci_macro.mac_rpool == NULL)
338 		mci->mci_macro.mac_rpool = mci->mci_rpool;
339 
340 	/*
341 	**  We don't need to load the persistent data if we have data
342 	**  already loaded in the cache.
343 	*/
344 
345 	if (mci->mci_host == NULL &&
346 	    (mci->mci_host = s->s_name) != NULL &&
347 	    !mci_load_persistent(mci))
348 	{
349 		if (tTd(42, 2))
350 			sm_dprintf("mci_get(%s %s): lock failed\n",
351 				host, m->m_name);
352 		mci->mci_exitstat = EX_TEMPFAIL;
353 		mci->mci_state = MCIS_CLOSED;
354 		mci->mci_statfile = NULL;
355 		return mci;
356 	}
357 
358 	if (tTd(42, 2))
359 	{
360 		sm_dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n",
361 			host, m->m_name, mci->mci_state, mci->mci_flags,
362 			mci->mci_exitstat, mci->mci_errno);
363 	}
364 
365 	if (mci->mci_state == MCIS_OPEN)
366 	{
367 		/* poke the connection to see if it's still alive */
368 		(void) smtpprobe(mci);
369 
370 		/* reset the stored state in the event of a timeout */
371 		if (mci->mci_state != MCIS_OPEN)
372 		{
373 			mci->mci_errno = 0;
374 			mci->mci_exitstat = EX_OK;
375 			mci->mci_state = MCIS_CLOSED;
376 		}
377 		else
378 		{
379 			/* get peer host address */
380 			/* (this should really be in the mci struct) */
381 			SOCKADDR_LEN_T socklen = sizeof CurHostAddr;
382 
383 			(void) getpeername(sm_io_getinfo(mci->mci_in,
384 							 SM_IO_WHAT_FD, NULL),
385 				(struct sockaddr *) &CurHostAddr, &socklen);
386 		}
387 	}
388 	if (mci->mci_state == MCIS_CLOSED)
389 	{
390 		time_t now = curtime();
391 
392 		/* if this info is stale, ignore it */
393 		if (mci->mci_lastuse + MciInfoTimeout <= now)
394 		{
395 			mci->mci_lastuse = now;
396 			mci->mci_errno = 0;
397 			mci->mci_exitstat = EX_OK;
398 		}
399 	}
400 
401 	return mci;
402 }
403 
404 /*
405 **  MCI_CLOSE -- (forcefully) close files used for a connection.
406 **	Note: this is a last resort, usually smtpquit() or endmailer()
407 **		should be used to close a connection.
408 **
409 **	Parameters:
410 **		mci -- the connection to close.
411 **		where -- where has this been called?
412 **
413 **	Returns:
414 **		none.
415 */
416 
417 void
418 mci_close(mci, where)
419 	MCI *mci;
420 	char *where;
421 {
422 	bool dumped;
423 
424 	if (mci == NULL)
425 		return;
426 	dumped = false;
427 	if (mci->mci_out != NULL)
428 	{
429 		if (tTd(56, 1))
430 		{
431 			sm_dprintf("mci_close: mci_out!=NULL, where=%s\n",
432 				where);
433 			mci_dump(sm_debug_file(), mci, false);
434 			dumped = true;
435 		}
436 		(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
437 		mci->mci_out = NULL;
438 	}
439 	if (mci->mci_in != NULL)
440 	{
441 		if (tTd(56, 1))
442 		{
443 			sm_dprintf("mci_close: mci_in!=NULL, where=%s\n",
444 				where);
445 			if (!dumped)
446 				mci_dump(sm_debug_file(), mci, false);
447 		}
448 		(void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
449 		mci->mci_in = NULL;
450 	}
451 	mci->mci_state = MCIS_CLOSED;
452 }
453 
454 /*
455 **  MCI_NEW -- allocate new MCI structure
456 **
457 **	Parameters:
458 **		rpool -- if non-NULL: allocate from that rpool.
459 **
460 **	Returns:
461 **		mci (new).
462 */
463 
464 MCI *
465 mci_new(rpool)
466 	SM_RPOOL_T *rpool;
467 {
468 	register MCI *mci;
469 
470 	if (rpool == NULL)
471 		mci = (MCI *) sm_malloc_x(sizeof *mci);
472 	else
473 		mci = (MCI *) sm_rpool_malloc_x(rpool, sizeof *mci);
474 	memset((char *) mci, '\0', sizeof *mci);
475 	mci->mci_rpool = sm_rpool_new_x(NULL);
476 	mci->mci_macro.mac_rpool = mci->mci_rpool;
477 	return mci;
478 }
479 /*
480 **  MCI_MATCH -- check connection cache for a particular host
481 **
482 **	Parameters:
483 **		host -- host to look for.
484 **		m -- mailer.
485 **
486 **	Returns:
487 **		true iff open connection exists.
488 */
489 
490 bool
491 mci_match(host, m)
492 	char *host;
493 	MAILER *m;
494 {
495 	register MCI *mci;
496 	register STAB *s;
497 
498 	if (m->m_mno < 0 || m->m_mno > MAXMAILERS)
499 		return false;
500 	s = stab(host, ST_MCI + m->m_mno, ST_FIND);
501 	if (s == NULL)
502 		return false;
503 
504 	mci = &s->s_mci;
505 	return mci->mci_state == MCIS_OPEN;
506 }
507 /*
508 **  MCI_SETSTAT -- set status codes in MCI structure.
509 **
510 **	Parameters:
511 **		mci -- the MCI structure to set.
512 **		xstat -- the exit status code.
513 **		dstat -- the DSN status code.
514 **		rstat -- the SMTP status code.
515 **
516 **	Returns:
517 **		none.
518 */
519 
520 void
521 mci_setstat(mci, xstat, dstat, rstat)
522 	MCI *mci;
523 	int xstat;
524 	char *dstat;
525 	char *rstat;
526 {
527 	/* protocol errors should never be interpreted as sticky */
528 	if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL)
529 		mci->mci_exitstat = xstat;
530 
531 	SM_FREE_CLR(mci->mci_status);
532 	if (dstat != NULL)
533 		mci->mci_status = sm_strdup_x(dstat);
534 
535 	SM_FREE_CLR(mci->mci_rstatus);
536 	if (rstat != NULL)
537 		mci->mci_rstatus = sm_strdup_x(rstat);
538 }
539 /*
540 **  MCI_DUMP -- dump the contents of an MCI structure.
541 **
542 **	Parameters:
543 **		fp -- output file pointer
544 **		mci -- the MCI structure to dump.
545 **
546 **	Returns:
547 **		none.
548 **
549 **	Side Effects:
550 **		none.
551 */
552 
553 struct mcifbits
554 {
555 	int	mcif_bit;	/* flag bit */
556 	char	*mcif_name;	/* flag name */
557 };
558 static struct mcifbits	MciFlags[] =
559 {
560 	{ MCIF_VALID,		"VALID"		},
561 	{ MCIF_CACHED,		"CACHED"	},
562 	{ MCIF_ESMTP,		"ESMTP"		},
563 	{ MCIF_EXPN,		"EXPN"		},
564 	{ MCIF_SIZE,		"SIZE"		},
565 	{ MCIF_8BITMIME,	"8BITMIME"	},
566 	{ MCIF_7BIT,		"7BIT"		},
567 	{ MCIF_INHEADER,	"INHEADER"	},
568 	{ MCIF_CVT8TO7,		"CVT8TO7"	},
569 	{ MCIF_DSN,		"DSN"		},
570 	{ MCIF_8BITOK,		"8BITOK"	},
571 	{ MCIF_CVT7TO8,		"CVT7TO8"	},
572 	{ MCIF_INMIME,		"INMIME"	},
573 	{ MCIF_AUTH,		"AUTH"		},
574 	{ MCIF_AUTHACT,		"AUTHACT"	},
575 	{ MCIF_ENHSTAT,		"ENHSTAT"	},
576 	{ MCIF_PIPELINED,	"PIPELINED"	},
577 #if STARTTLS
578 	{ MCIF_TLS,		"TLS"		},
579 	{ MCIF_TLSACT,		"TLSACT"	},
580 #endif /* STARTTLS */
581 	{ MCIF_DLVR_BY,		"DLVR_BY"	},
582 	{ 0,			NULL		}
583 };
584 
585 void
586 mci_dump(fp, mci, logit)
587 	SM_FILE_T *fp;
588 	register MCI *mci;
589 	bool logit;
590 {
591 	register char *p;
592 	char *sep;
593 	char buf[4000];
594 
595 	sep = logit ? " " : "\n\t";
596 	p = buf;
597 	(void) sm_snprintf(p, SPACELEFT(buf, p), "MCI@%p: ", mci);
598 	p += strlen(p);
599 	if (mci == NULL)
600 	{
601 		(void) sm_snprintf(p, SPACELEFT(buf, p), "NULL");
602 		goto printit;
603 	}
604 	(void) sm_snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags);
605 	p += strlen(p);
606 
607 	/*
608 	**  The following check is just for paranoia.  It protects the
609 	**  assignment in the if() clause. If there's not some minimum
610 	**  amount of space we can stop right now. The check will not
611 	**  trigger as long as sizeof(buf)=4000.
612 	*/
613 
614 	if (p >= buf + sizeof(buf) - 4)
615 		goto printit;
616 	if (mci->mci_flags != 0)
617 	{
618 		struct mcifbits *f;
619 
620 		*p++ = '<';	/* protected above */
621 		for (f = MciFlags; f->mcif_bit != 0; f++)
622 		{
623 			if (!bitset(f->mcif_bit, mci->mci_flags))
624 				continue;
625 			(void) sm_strlcpyn(p, SPACELEFT(buf, p), 2,
626 					   f->mcif_name, ",");
627 			p += strlen(p);
628 		}
629 		p[-1] = '>';
630 	}
631 
632 	/* Note: sm_snprintf() takes care of NULL arguments for %s */
633 	(void) sm_snprintf(p, SPACELEFT(buf, p),
634 		",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s",
635 		sep, mci->mci_errno, mci->mci_herrno,
636 		mci->mci_exitstat, mci->mci_state, (int) mci->mci_pid, sep);
637 	p += strlen(p);
638 	(void) sm_snprintf(p, SPACELEFT(buf, p),
639 		"maxsize=%ld, phase=%s, mailer=%s,%s",
640 		mci->mci_maxsize, mci->mci_phase,
641 		mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name,
642 		sep);
643 	p += strlen(p);
644 	(void) sm_snprintf(p, SPACELEFT(buf, p),
645 		"status=%s, rstatus=%s,%s",
646 		mci->mci_status, mci->mci_rstatus, sep);
647 	p += strlen(p);
648 	(void) sm_snprintf(p, SPACELEFT(buf, p),
649 		"host=%s, lastuse=%s",
650 		mci->mci_host, ctime(&mci->mci_lastuse));
651 printit:
652 	if (logit)
653 		sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf);
654 	else
655 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s\n", buf);
656 }
657 /*
658 **  MCI_DUMP_ALL -- print the entire MCI cache
659 **
660 **	Parameters:
661 **		fp -- output file pointer
662 **		logit -- if set, log the result instead of printing
663 **			to stdout.
664 **
665 **	Returns:
666 **		none.
667 */
668 
669 void
670 mci_dump_all(fp, logit)
671 	SM_FILE_T *fp;
672 	bool logit;
673 {
674 	register int i;
675 
676 	if (MciCache == NULL)
677 		return;
678 
679 	for (i = 0; i < MaxMciCache; i++)
680 		mci_dump(fp, MciCache[i], logit);
681 }
682 /*
683 **  MCI_LOCK_HOST -- Lock host while sending.
684 **
685 **	If we are contacting a host, we'll need to
686 **	update the status information in the host status
687 **	file, and if we want to do that, we ought to have
688 **	locked it. This has the (according to some)
689 **	desirable effect of serializing connectivity with
690 **	remote hosts -- i.e.: one connection to a given
691 **	host at a time.
692 **
693 **	Parameters:
694 **		mci -- containing the host we want to lock.
695 **
696 **	Returns:
697 **		EX_OK	    -- got the lock.
698 **		EX_TEMPFAIL -- didn't get the lock.
699 */
700 
701 int
702 mci_lock_host(mci)
703 	MCI *mci;
704 {
705 	if (mci == NULL)
706 	{
707 		if (tTd(56, 1))
708 			sm_dprintf("mci_lock_host: NULL mci\n");
709 		return EX_OK;
710 	}
711 
712 	if (!SingleThreadDelivery)
713 		return EX_OK;
714 
715 	return mci_lock_host_statfile(mci);
716 }
717 
718 static int
719 mci_lock_host_statfile(mci)
720 	MCI *mci;
721 {
722 	int save_errno = errno;
723 	int retVal = EX_OK;
724 	char fname[MAXPATHLEN];
725 
726 	if (HostStatDir == NULL || mci->mci_host == NULL)
727 		return EX_OK;
728 
729 	if (tTd(56, 2))
730 		sm_dprintf("mci_lock_host: attempting to lock %s\n",
731 			   mci->mci_host);
732 
733 	if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname,
734 					 true) < 0)
735 	{
736 		/* of course this should never happen */
737 		if (tTd(56, 2))
738 			sm_dprintf("mci_lock_host: Failed to generate host path for %s\n",
739 				   mci->mci_host);
740 
741 		retVal = EX_TEMPFAIL;
742 		goto cleanup;
743 	}
744 
745 	mci->mci_statfile = safefopen(fname, O_RDWR, FileMode,
746 				      SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT);
747 
748 	if (mci->mci_statfile == NULL)
749 	{
750 		syserr("mci_lock_host: cannot create host lock file %s", fname);
751 		goto cleanup;
752 	}
753 
754 	if (!lockfile(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL),
755 		      fname, "", LOCK_EX|LOCK_NB))
756 	{
757 		if (tTd(56, 2))
758 			sm_dprintf("mci_lock_host: couldn't get lock on %s\n",
759 				fname);
760 		(void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT);
761 		mci->mci_statfile = NULL;
762 		retVal = EX_TEMPFAIL;
763 		goto cleanup;
764 	}
765 
766 	if (tTd(56, 12) && mci->mci_statfile != NULL)
767 		sm_dprintf("mci_lock_host: Sanity check -- lock is good\n");
768 
769 cleanup:
770 	errno = save_errno;
771 	return retVal;
772 }
773 /*
774 **  MCI_UNLOCK_HOST -- unlock host
775 **
776 **	Clean up the lock on a host, close the file, let
777 **	someone else use it.
778 **
779 **	Parameters:
780 **		mci -- us.
781 **
782 **	Returns:
783 **		nothing.
784 */
785 
786 void
787 mci_unlock_host(mci)
788 	MCI *mci;
789 {
790 	int save_errno = errno;
791 
792 	if (mci == NULL)
793 	{
794 		if (tTd(56, 1))
795 			sm_dprintf("mci_unlock_host: NULL mci\n");
796 		return;
797 	}
798 
799 	if (HostStatDir == NULL || mci->mci_host == NULL)
800 		return;
801 
802 	if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL)
803 	{
804 		if (tTd(56, 1))
805 			sm_dprintf("mci_unlock_host: stat file already locked\n");
806 	}
807 	else
808 	{
809 		if (tTd(56, 2))
810 			sm_dprintf("mci_unlock_host: store prior to unlock\n");
811 		mci_store_persistent(mci);
812 	}
813 
814 	if (mci->mci_statfile != NULL)
815 	{
816 		(void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT);
817 		mci->mci_statfile = NULL;
818 	}
819 
820 	errno = save_errno;
821 }
822 /*
823 **  MCI_LOAD_PERSISTENT -- load persistent host info
824 **
825 **	Load information about host that is kept
826 **	in common for all running sendmails.
827 **
828 **	Parameters:
829 **		mci -- the host/connection to load persistent info for.
830 **
831 **	Returns:
832 **		true -- lock was successful
833 **		false -- lock failed
834 */
835 
836 static bool
837 mci_load_persistent(mci)
838 	MCI *mci;
839 {
840 	int save_errno = errno;
841 	bool locked = true;
842 	SM_FILE_T *fp;
843 	char fname[MAXPATHLEN];
844 
845 	if (mci == NULL)
846 	{
847 		if (tTd(56, 1))
848 			sm_dprintf("mci_load_persistent: NULL mci\n");
849 		return true;
850 	}
851 
852 	if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL)
853 		return true;
854 
855 	/* Already have the persistent information in memory */
856 	if (SingleThreadDelivery && mci->mci_statfile != NULL)
857 		return true;
858 
859 	if (tTd(56, 1))
860 		sm_dprintf("mci_load_persistent: Attempting to load persistent information for %s\n",
861 			   mci->mci_host);
862 
863 	if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname,
864 					 false) < 0)
865 	{
866 		/* Not much we can do if the file isn't there... */
867 		if (tTd(56, 1))
868 			sm_dprintf("mci_load_persistent: Couldn't generate host path\n");
869 		goto cleanup;
870 	}
871 
872 	fp = safefopen(fname, O_RDONLY, FileMode,
873 		       SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
874 	if (fp == NULL)
875 	{
876 		/* I can't think of any reason this should ever happen */
877 		if (tTd(56, 1))
878 			sm_dprintf("mci_load_persistent: open(%s): %s\n",
879 				fname, sm_errstring(errno));
880 		goto cleanup;
881 	}
882 
883 	FileName = fname;
884 	locked = lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, "",
885 			  LOCK_SH|LOCK_NB);
886 	if (locked)
887 	{
888 		(void) mci_read_persistent(fp, mci);
889 		(void) lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname,
890 				"", LOCK_UN);
891 	}
892 	FileName = NULL;
893 	(void) sm_io_close(fp, SM_TIME_DEFAULT);
894 
895 cleanup:
896 	errno = save_errno;
897 	return locked;
898 }
899 /*
900 **  MCI_READ_PERSISTENT -- read persistent host status file
901 **
902 **	Parameters:
903 **		fp -- the file pointer to read.
904 **		mci -- the pointer to fill in.
905 **
906 **	Returns:
907 **		-1 -- if the file was corrupt.
908 **		0 -- otherwise.
909 **
910 **	Warning:
911 **		This code makes the assumption that this data
912 **		will be read in an atomic fashion, and that the data
913 **		was written in an atomic fashion.  Any other functioning
914 **		may lead to some form of insanity.  This should be
915 **		perfectly safe due to underlying stdio buffering.
916 */
917 
918 static int
919 mci_read_persistent(fp, mci)
920 	SM_FILE_T *fp;
921 	register MCI *mci;
922 {
923 	int ver;
924 	register char *p;
925 	int saveLineNumber = LineNumber;
926 	char buf[MAXLINE];
927 
928 	if (fp == NULL)
929 		syserr("mci_read_persistent: NULL fp");
930 	if (mci == NULL)
931 		syserr("mci_read_persistent: NULL mci");
932 	if (tTd(56, 93))
933 	{
934 		sm_dprintf("mci_read_persistent: fp=%lx, mci=",
935 			   (unsigned long) fp);
936 	}
937 
938 	SM_FREE_CLR(mci->mci_status);
939 	SM_FREE_CLR(mci->mci_rstatus);
940 
941 	sm_io_rewind(fp, SM_TIME_DEFAULT);
942 	ver = -1;
943 	LineNumber = 0;
944 	while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
945 	{
946 		LineNumber++;
947 		p = strchr(buf, '\n');
948 		if (p != NULL)
949 			*p = '\0';
950 		switch (buf[0])
951 		{
952 		  case 'V':		/* version stamp */
953 			ver = atoi(&buf[1]);
954 			if (ver < 0 || ver > 0)
955 				syserr("Unknown host status version %d: %d max",
956 					ver, 0);
957 			break;
958 
959 		  case 'E':		/* UNIX error number */
960 			mci->mci_errno = atoi(&buf[1]);
961 			break;
962 
963 		  case 'H':		/* DNS error number */
964 			mci->mci_herrno = atoi(&buf[1]);
965 			break;
966 
967 		  case 'S':		/* UNIX exit status */
968 			mci->mci_exitstat = atoi(&buf[1]);
969 			break;
970 
971 		  case 'D':		/* DSN status */
972 			mci->mci_status = newstr(&buf[1]);
973 			break;
974 
975 		  case 'R':		/* SMTP status */
976 			mci->mci_rstatus = newstr(&buf[1]);
977 			break;
978 
979 		  case 'U':		/* last usage time */
980 			mci->mci_lastuse = atol(&buf[1]);
981 			break;
982 
983 		  case '.':		/* end of file */
984 			if (tTd(56, 93))
985 				mci_dump(sm_debug_file(), mci, false);
986 			return 0;
987 
988 		  default:
989 			sm_syslog(LOG_CRIT, NOQID,
990 				  "%s: line %d: Unknown host status line \"%s\"",
991 				  FileName == NULL ? mci->mci_host : FileName,
992 				  LineNumber, buf);
993 			LineNumber = saveLineNumber;
994 			return -1;
995 		}
996 	}
997 	LineNumber = saveLineNumber;
998 	if (tTd(56, 93))
999 		sm_dprintf("incomplete (missing dot for EOF)\n");
1000 	if (ver < 0)
1001 		return -1;
1002 	return 0;
1003 }
1004 /*
1005 **  MCI_STORE_PERSISTENT -- Store persistent MCI information
1006 **
1007 **	Store information about host that is kept
1008 **	in common for all running sendmails.
1009 **
1010 **	Parameters:
1011 **		mci -- the host/connection to store persistent info for.
1012 **
1013 **	Returns:
1014 **		none.
1015 */
1016 
1017 void
1018 mci_store_persistent(mci)
1019 	MCI *mci;
1020 {
1021 	int save_errno = errno;
1022 
1023 	if (mci == NULL)
1024 	{
1025 		if (tTd(56, 1))
1026 			sm_dprintf("mci_store_persistent: NULL mci\n");
1027 		return;
1028 	}
1029 
1030 	if (HostStatDir == NULL || mci->mci_host == NULL)
1031 		return;
1032 
1033 	if (tTd(56, 1))
1034 		sm_dprintf("mci_store_persistent: Storing information for %s\n",
1035 			   mci->mci_host);
1036 
1037 	if (mci->mci_statfile == NULL)
1038 	{
1039 		if (tTd(56, 1))
1040 			sm_dprintf("mci_store_persistent: no statfile\n");
1041 		return;
1042 	}
1043 
1044 	sm_io_rewind(mci->mci_statfile, SM_TIME_DEFAULT);
1045 #if !NOFTRUNCATE
1046 	(void) ftruncate(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL),
1047 			 (off_t) 0);
1048 #endif /* !NOFTRUNCATE */
1049 
1050 	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "V0\n");
1051 	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "E%d\n",
1052 			     mci->mci_errno);
1053 	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "H%d\n",
1054 			     mci->mci_herrno);
1055 	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "S%d\n",
1056 			     mci->mci_exitstat);
1057 	if (mci->mci_status != NULL)
1058 		(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT,
1059 				     "D%.80s\n",
1060 				     denlstring(mci->mci_status, true, false));
1061 	if (mci->mci_rstatus != NULL)
1062 		(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT,
1063 				     "R%.80s\n",
1064 				     denlstring(mci->mci_rstatus, true, false));
1065 	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "U%ld\n",
1066 			     (long)(mci->mci_lastuse));
1067 	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, ".\n");
1068 
1069 	(void) sm_io_flush(mci->mci_statfile, SM_TIME_DEFAULT);
1070 
1071 	errno = save_errno;
1072 	return;
1073 }
1074 /*
1075 **  MCI_TRAVERSE_PERSISTENT -- walk persistent status tree
1076 **
1077 **	Recursively find all the mci host files in `pathname'.  Default to
1078 **		main host status directory if no path is provided.
1079 **	Call (*action)(pathname, host) for each file found.
1080 **
1081 **	Note: all information is collected in a list before it is processed.
1082 **	This may not be the best way to do it, but it seems safest, since
1083 **	the file system would be touched while we are attempting to traverse
1084 **	the directory tree otherwise (during purges).
1085 **
1086 **	Parameters:
1087 **		action -- function to call on each node.  If returns < 0,
1088 **			return immediately.
1089 **		pathname -- root of tree.  If null, use main host status
1090 **			directory.
1091 **
1092 **	Returns:
1093 **		< 0 -- if any action routine returns a negative value, that
1094 **			value is returned.
1095 **		0 -- if we successfully went to completion.
1096 **		> 0 -- return status from action()
1097 */
1098 
1099 int
1100 mci_traverse_persistent(action, pathname)
1101 	int (*action)__P((char *, char *));
1102 	char *pathname;
1103 {
1104 	struct stat statbuf;
1105 	DIR *d;
1106 	int ret;
1107 
1108 	if (pathname == NULL)
1109 		pathname = HostStatDir;
1110 	if (pathname == NULL)
1111 		return -1;
1112 
1113 	if (tTd(56, 1))
1114 		sm_dprintf("mci_traverse: pathname is %s\n", pathname);
1115 
1116 	ret = stat(pathname, &statbuf);
1117 	if (ret < 0)
1118 	{
1119 		if (tTd(56, 2))
1120 			sm_dprintf("mci_traverse: Failed to stat %s: %s\n",
1121 				pathname, sm_errstring(errno));
1122 		return ret;
1123 	}
1124 	if (S_ISDIR(statbuf.st_mode))
1125 	{
1126 		bool leftone, removedone;
1127 		size_t len;
1128 		char *newptr;
1129 		struct dirent *e;
1130 		char newpath[MAXPATHLEN];
1131 
1132 		if ((d = opendir(pathname)) == NULL)
1133 		{
1134 			if (tTd(56, 2))
1135 				sm_dprintf("mci_traverse: opendir %s: %s\n",
1136 					pathname, sm_errstring(errno));
1137 			return -1;
1138 		}
1139 		len = sizeof(newpath) - MAXNAMLEN - 3;
1140 		if (sm_strlcpy(newpath, pathname, len) >= len)
1141 		{
1142 			if (tTd(56, 2))
1143 				sm_dprintf("mci_traverse: path \"%s\" too long",
1144 					pathname);
1145 			return -1;
1146 		}
1147 		newptr = newpath + strlen(newpath);
1148 		*newptr++ = '/';
1149 
1150 		/*
1151 		**  repeat until no file has been removed
1152 		**  this may become ugly when several files "expire"
1153 		**  during these loops, but it's better than doing
1154 		**  a rewinddir() inside the inner loop
1155 		*/
1156 
1157 		do
1158 		{
1159 			leftone = removedone = false;
1160 			while ((e = readdir(d)) != NULL)
1161 			{
1162 				if (e->d_name[0] == '.')
1163 					continue;
1164 
1165 				(void) sm_strlcpy(newptr, e->d_name,
1166 					       sizeof newpath -
1167 					       (newptr - newpath));
1168 
1169 				if (StopRequest)
1170 					stop_sendmail();
1171 				ret = mci_traverse_persistent(action, newpath);
1172 				if (ret < 0)
1173 					break;
1174 				if (ret == 1)
1175 					leftone = true;
1176 				if (!removedone && ret == 0 &&
1177 				    action == mci_purge_persistent)
1178 					removedone = true;
1179 			}
1180 			if (ret < 0)
1181 				break;
1182 
1183 			/*
1184 			**  The following appears to be
1185 			**  necessary during purges, since
1186 			**  we modify the directory structure
1187 			*/
1188 
1189 			if (removedone)
1190 				rewinddir(d);
1191 			if (tTd(56, 40))
1192 				sm_dprintf("mci_traverse: path %s: ret %d removed %d left %d\n",
1193 					pathname, ret, removedone, leftone);
1194 		} while (removedone);
1195 
1196 		/* purge (or whatever) the directory proper */
1197 		if (!leftone)
1198 		{
1199 			*--newptr = '\0';
1200 			ret = (*action)(newpath, NULL);
1201 		}
1202 		(void) closedir(d);
1203 	}
1204 	else if (S_ISREG(statbuf.st_mode))
1205 	{
1206 		char *end = pathname + strlen(pathname) - 1;
1207 		char *start;
1208 		char *scan;
1209 		char host[MAXHOSTNAMELEN];
1210 		char *hostptr = host;
1211 
1212 		/*
1213 		**  Reconstruct the host name from the path to the
1214 		**  persistent information.
1215 		*/
1216 
1217 		do
1218 		{
1219 			if (hostptr != host)
1220 				*(hostptr++) = '.';
1221 			start = end;
1222 			while (start > pathname && *(start - 1) != '/')
1223 				start--;
1224 
1225 			if (*end == '.')
1226 				end--;
1227 
1228 			for (scan = start; scan <= end; scan++)
1229 				*(hostptr++) = *scan;
1230 
1231 			end = start - 2;
1232 		} while (end > pathname && *end == '.');
1233 
1234 		*hostptr = '\0';
1235 
1236 		/*
1237 		**  Do something with the file containing the persistent
1238 		**  information.
1239 		*/
1240 
1241 		ret = (*action)(pathname, host);
1242 	}
1243 
1244 	return ret;
1245 }
1246 /*
1247 **  MCI_PRINT_PERSISTENT -- print persistent info
1248 **
1249 **	Dump the persistent information in the file 'pathname'
1250 **
1251 **	Parameters:
1252 **		pathname -- the pathname to the status file.
1253 **		hostname -- the corresponding host name.
1254 **
1255 **	Returns:
1256 **		0
1257 */
1258 
1259 int
1260 mci_print_persistent(pathname, hostname)
1261 	char *pathname;
1262 	char *hostname;
1263 {
1264 	static bool initflag = false;
1265 	SM_FILE_T *fp;
1266 	int width = Verbose ? 78 : 25;
1267 	bool locked;
1268 	MCI mcib;
1269 
1270 	/* skip directories */
1271 	if (hostname == NULL)
1272 		return 0;
1273 
1274 	if (StopRequest)
1275 		stop_sendmail();
1276 
1277 	if (!initflag)
1278 	{
1279 		initflag = true;
1280 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1281 				     " -------------- Hostname --------------- How long ago ---------Results---------\n");
1282 	}
1283 
1284 	fp = safefopen(pathname, O_RDONLY, FileMode,
1285 		       SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
1286 
1287 	if (fp == NULL)
1288 	{
1289 		if (tTd(56, 1))
1290 			sm_dprintf("mci_print_persistent: cannot open %s: %s\n",
1291 				pathname, sm_errstring(errno));
1292 		return 0;
1293 	}
1294 
1295 	FileName = pathname;
1296 	memset(&mcib, '\0', sizeof mcib);
1297 	if (mci_read_persistent(fp, &mcib) < 0)
1298 	{
1299 		syserr("%s: could not read status file", pathname);
1300 		(void) sm_io_close(fp, SM_TIME_DEFAULT);
1301 		FileName = NULL;
1302 		return 0;
1303 	}
1304 
1305 	locked = !lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), pathname,
1306 			   "", LOCK_SH|LOCK_NB);
1307 	(void) sm_io_close(fp, SM_TIME_DEFAULT);
1308 	FileName = NULL;
1309 
1310 	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%c%-39s %12s ",
1311 			     locked ? '*' : ' ', hostname,
1312 			     pintvl(curtime() - mcib.mci_lastuse, true));
1313 	if (mcib.mci_rstatus != NULL)
1314 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", width,
1315 				     mcib.mci_rstatus);
1316 	else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0)
1317 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1318 				     "Deferred: %.*s\n", width - 10,
1319 				     sm_errstring(mcib.mci_errno));
1320 	else if (mcib.mci_exitstat != 0)
1321 	{
1322 		char *exmsg = sm_sysexmsg(mcib.mci_exitstat);
1323 
1324 		if (exmsg == NULL)
1325 		{
1326 			char buf[80];
1327 
1328 			(void) sm_snprintf(buf, sizeof buf,
1329 				"Unknown mailer error %d",
1330 				mcib.mci_exitstat);
1331 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n",
1332 					     width, buf);
1333 		}
1334 		else
1335 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n",
1336 					     width, &exmsg[5]);
1337 	}
1338 	else if (mcib.mci_errno == 0)
1339 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK\n");
1340 	else
1341 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK: %.*s\n",
1342 				     width - 4, sm_errstring(mcib.mci_errno));
1343 
1344 	return 0;
1345 }
1346 /*
1347 **  MCI_PURGE_PERSISTENT -- Remove a persistence status file.
1348 **
1349 **	Parameters:
1350 **		pathname -- path to the status file.
1351 **		hostname -- name of host corresponding to that file.
1352 **			NULL if this is a directory (domain).
1353 **
1354 **	Returns:
1355 **		0 -- ok
1356 **		1 -- file not deleted (too young, incorrect format)
1357 **		< 0 -- some error occurred
1358 */
1359 
1360 int
1361 mci_purge_persistent(pathname, hostname)
1362 	char *pathname;
1363 	char *hostname;
1364 {
1365 	struct stat statbuf;
1366 	char *end = pathname + strlen(pathname) - 1;
1367 	int ret;
1368 
1369 	if (tTd(56, 1))
1370 		sm_dprintf("mci_purge_persistent: purging %s\n", pathname);
1371 
1372 	ret = stat(pathname, &statbuf);
1373 	if (ret < 0)
1374 	{
1375 		if (tTd(56, 2))
1376 			sm_dprintf("mci_purge_persistent: Failed to stat %s: %s\n",
1377 				pathname, sm_errstring(errno));
1378 		return ret;
1379 	}
1380 	if (curtime() - statbuf.st_mtime <= MciInfoTimeout)
1381 		return 1;
1382 	if (hostname != NULL)
1383 	{
1384 		/* remove the file */
1385 		ret = unlink(pathname);
1386 		if (ret < 0)
1387 		{
1388 			if (LogLevel > 8)
1389 				sm_syslog(LOG_ERR, NOQID,
1390 					  "mci_purge_persistent: failed to unlink %s: %s",
1391 					  pathname, sm_errstring(errno));
1392 			if (tTd(56, 2))
1393 				sm_dprintf("mci_purge_persistent: failed to unlink %s: %s\n",
1394 					pathname, sm_errstring(errno));
1395 			return ret;
1396 		}
1397 	}
1398 	else
1399 	{
1400 		/* remove the directory */
1401 		if (*end != '.')
1402 			return 1;
1403 
1404 		if (tTd(56, 1))
1405 			sm_dprintf("mci_purge_persistent: dpurge %s\n", pathname);
1406 
1407 		ret = rmdir(pathname);
1408 		if (ret < 0)
1409 		{
1410 			if (tTd(56, 2))
1411 				sm_dprintf("mci_purge_persistent: rmdir %s: %s\n",
1412 					pathname, sm_errstring(errno));
1413 			return ret;
1414 		}
1415 	}
1416 
1417 	return 0;
1418 }
1419 /*
1420 **  MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname
1421 **
1422 **	Given `host', convert from a.b.c to $HostStatDir/c./b./a,
1423 **	putting the result into `path'.  if `createflag' is set, intervening
1424 **	directories will be created as needed.
1425 **
1426 **	Parameters:
1427 **		host -- host name to convert from.
1428 **		path -- place to store result.
1429 **		pathlen -- length of path buffer.
1430 **		createflag -- if set, create intervening directories as
1431 **			needed.
1432 **
1433 **	Returns:
1434 **		0 -- success
1435 **		-1 -- failure
1436 */
1437 
1438 static int
1439 mci_generate_persistent_path(host, path, pathlen, createflag)
1440 	const char *host;
1441 	char *path;
1442 	int pathlen;
1443 	bool createflag;
1444 {
1445 	char *elem, *p, *x, ch;
1446 	int ret = 0;
1447 	int len;
1448 	char t_host[MAXHOSTNAMELEN];
1449 #if NETINET6
1450 	struct in6_addr in6_addr;
1451 #endif /* NETINET6 */
1452 
1453 	/*
1454 	**  Rationality check the arguments.
1455 	*/
1456 
1457 	if (host == NULL)
1458 	{
1459 		syserr("mci_generate_persistent_path: null host");
1460 		return -1;
1461 	}
1462 	if (path == NULL)
1463 	{
1464 		syserr("mci_generate_persistent_path: null path");
1465 		return -1;
1466 	}
1467 
1468 	if (tTd(56, 80))
1469 		sm_dprintf("mci_generate_persistent_path(%s): ", host);
1470 
1471 	if (*host == '\0' || *host == '.')
1472 		return -1;
1473 
1474 	/* make certain this is not a bracketed host number */
1475 	if (strlen(host) > sizeof t_host - 1)
1476 		return -1;
1477 	if (host[0] == '[')
1478 		(void) sm_strlcpy(t_host, host + 1, sizeof t_host);
1479 	else
1480 		(void) sm_strlcpy(t_host, host, sizeof t_host);
1481 
1482 	/*
1483 	**  Delete any trailing dots from the hostname.
1484 	**  Leave 'elem' pointing at the \0.
1485 	*/
1486 
1487 	elem = t_host + strlen(t_host);
1488 	while (elem > t_host &&
1489 	       (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']')))
1490 		*--elem = '\0';
1491 
1492 	/* check for bogus bracketed address */
1493 	if (host[0] == '[')
1494 	{
1495 		bool good = false;
1496 # if NETINET6
1497 		if (anynet_pton(AF_INET6, t_host, &in6_addr) == 1)
1498 			good = true;
1499 # endif /* NETINET6 */
1500 # if NETINET
1501 		if (inet_addr(t_host) != INADDR_NONE)
1502 			good = true;
1503 # endif /* NETINET */
1504 		if (!good)
1505 			return -1;
1506 	}
1507 
1508 	/* check for what will be the final length of the path */
1509 	len = strlen(HostStatDir) + 2;
1510 	for (p = (char *) t_host; *p != '\0'; p++)
1511 	{
1512 		if (*p == '.')
1513 			len++;
1514 		len++;
1515 		if (p[0] == '.' && p[1] == '.')
1516 			return -1;
1517 	}
1518 	if (len > pathlen || len < 1)
1519 		return -1;
1520 	(void) sm_strlcpy(path, HostStatDir, pathlen);
1521 	p = path + strlen(path);
1522 	while (elem > t_host)
1523 	{
1524 		if (!path_is_dir(path, createflag))
1525 		{
1526 			ret = -1;
1527 			break;
1528 		}
1529 		elem--;
1530 		while (elem >= t_host && *elem != '.')
1531 			elem--;
1532 		*p++ = '/';
1533 		x = elem + 1;
1534 		while ((ch = *x++) != '\0' && ch != '.')
1535 		{
1536 			if (isascii(ch) && isupper(ch))
1537 				ch = tolower(ch);
1538 			if (ch == '/')
1539 				ch = ':';	/* / -> : */
1540 			*p++ = ch;
1541 		}
1542 		if (elem >= t_host)
1543 			*p++ = '.';
1544 		*p = '\0';
1545 	}
1546 	if (tTd(56, 80))
1547 	{
1548 		if (ret < 0)
1549 			sm_dprintf("FAILURE %d\n", ret);
1550 		else
1551 			sm_dprintf("SUCCESS %s\n", path);
1552 	}
1553 	return ret;
1554 }
1555