queue.c revision 73188
138032Speter/*
273188Sgshapiro * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1438032Speter
1564562Sgshapiro#include <sendmail.h>
1664562Sgshapiro
1738032Speter#ifndef lint
1864562Sgshapiro# if QUEUE
1973188Sgshapirostatic char id[] = "@(#)$Id: queue.c,v 8.343.4.44 2001/02/22 00:55:35 ca Exp $ (with queueing)";
2064562Sgshapiro# else /* QUEUE */
2173188Sgshapirostatic char id[] = "@(#)$Id: queue.c,v 8.343.4.44 2001/02/22 00:55:35 ca Exp $ (without queueing)";
2264562Sgshapiro# endif /* QUEUE */
2364562Sgshapiro#endif /* ! lint */
2438032Speter
2538032Speter# include <dirent.h>
2638032Speter
2764562Sgshapiro#if QUEUE
2838032Speter
2964562Sgshapiro# if _FFR_QUEUEDELAY
3064562Sgshapiro#  define QF_VERSION	5	/* version number of this queue format */
3164562Sgshapirostatic time_t	queuedelay __P((ENVELOPE *));
3264562Sgshapiro# else /* _FFR_QUEUEDELAY */
3364562Sgshapiro#  define QF_VERSION	4	/* version number of this queue format */
3464562Sgshapiro#  define queuedelay(e)	MinQueueAge
3564562Sgshapiro# endif /* _FFR_QUEUEDELAY */
3664562Sgshapiro
3738032Speter/*
3838032Speter**  Work queue.
3938032Speter*/
4038032Speter
4138032Speterstruct work
4238032Speter{
4338032Speter	char		*w_name;	/* name of control file */
4438032Speter	char		*w_host;	/* name of recipient host */
4538032Speter	bool		w_lock;		/* is message locked? */
4638032Speter	bool		w_tooyoung;	/* is it too young to run? */
4738032Speter	long		w_pri;		/* priority of message, see below */
4838032Speter	time_t		w_ctime;	/* creation time of message */
4938032Speter	struct work	*w_next;	/* next in queue */
5038032Speter};
5138032Speter
5238032Spetertypedef struct work	WORK;
5338032Speter
5464562Sgshapirostatic WORK	*WorkQ;			/* queue of things to be done */
5538032Speter
5664562Sgshapirostatic void	grow_wlist __P((int));
5764562Sgshapirostatic int	orderq __P((int, bool));
5864562Sgshapirostatic void	printctladdr __P((ADDRESS *, FILE *));
5964562Sgshapirostatic int	print_single_queue __P((int));
6064562Sgshapirostatic bool	readqf __P((ENVELOPE *));
6164562Sgshapirostatic void	runqueueevent __P((void));
6264562Sgshapirostatic int	run_single_queue __P((int, bool, bool));
6364562Sgshapirostatic char	*strrev __P((char *));
6464562Sgshapirostatic ADDRESS	*setctluser __P((char *, int));
6564562Sgshapirostatic int	workcmpf0();
6664562Sgshapirostatic int	workcmpf1();
6764562Sgshapirostatic int	workcmpf2();
6864562Sgshapirostatic int	workcmpf3();
6964562Sgshapirostatic int	workcmpf4();
7038032Speter
7138032Speter/*
7238032Speter**  QUEUEUP -- queue a message up for future transmission.
7338032Speter**
7438032Speter**	Parameters:
7538032Speter**		e -- the envelope to queue up.
7638032Speter**		announce -- if TRUE, tell when you are queueing up.
7738032Speter**
7838032Speter**	Returns:
7938032Speter**		none.
8038032Speter**
8138032Speter**	Side Effects:
8238032Speter**		The current request are saved in a control file.
8338032Speter**		The queue file is left locked.
8438032Speter*/
8538032Speter
8664562Sgshapiro# define TEMPQF_LETTER 'T'
8771345Sgshapiro# define LOSEQF_LETTER 'Q'
8864562Sgshapiro
8938032Spetervoid
9038032Speterqueueup(e, announce)
9138032Speter	register ENVELOPE *e;
9238032Speter	bool announce;
9338032Speter{
9438032Speter	char *qf;
9538032Speter	register FILE *tfp;
9638032Speter	register HDR *h;
9738032Speter	register ADDRESS *q;
9864562Sgshapiro	int tfd = -1;
9938032Speter	int i;
10038032Speter	bool newid;
10138032Speter	register char *p;
10238032Speter	MAILER nullmailer;
10338032Speter	MCI mcibuf;
10464562Sgshapiro	char tf[MAXPATHLEN];
10538032Speter	char buf[MAXLINE];
10638032Speter
10738032Speter	/*
10838032Speter	**  Create control file.
10938032Speter	*/
11038032Speter
11138032Speter	newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);
11238032Speter
11338032Speter	/* if newid, queuename will create a locked qf file in e->lockfp */
11464562Sgshapiro	(void) strlcpy(tf, queuename(e, 't'), sizeof tf);
11538032Speter	tfp = e->e_lockfp;
11638032Speter	if (tfp == NULL)
11738032Speter		newid = FALSE;
11838032Speter
11938032Speter	/* if newid, just write the qf file directly (instead of tf file) */
12038032Speter	if (!newid)
12138032Speter	{
12264562Sgshapiro		int flags;
12364562Sgshapiro
12464562Sgshapiro		flags = O_CREAT|O_WRONLY|O_EXCL;
12564562Sgshapiro
12638032Speter		/* get a locked tf file */
12738032Speter		for (i = 0; i < 128; i++)
12838032Speter		{
12964562Sgshapiro			if (tfd < 0)
13038032Speter			{
13164562Sgshapiro#if _FFR_QUEUE_FILE_MODE
13264562Sgshapiro				MODE_T oldumask;
13364562Sgshapiro
13464562Sgshapiro				if (bitset(S_IWGRP, QueueFileMode))
13564562Sgshapiro					oldumask = umask(002);
13664562Sgshapiro				tfd = open(tf, flags, QueueFileMode);
13764562Sgshapiro				if (bitset(S_IWGRP, QueueFileMode))
13864562Sgshapiro					(void) umask(oldumask);
13964562Sgshapiro#else /* _FFR_QUEUE_FILE_MODE */
14064562Sgshapiro				tfd = open(tf, flags, FileMode);
14164562Sgshapiro#endif /* _FFR_QUEUE_FILE_MODE */
14264562Sgshapiro
14364562Sgshapiro				if (tfd < 0)
14464562Sgshapiro				{
14564562Sgshapiro					if (errno != EEXIST)
14664562Sgshapiro						break;
14764562Sgshapiro					if (LogLevel > 0 && (i % 32) == 0)
14864562Sgshapiro						sm_syslog(LOG_ALERT, e->e_id,
14964562Sgshapiro							  "queueup: cannot create %s, uid=%d: %s",
15064562Sgshapiro							  tf, geteuid(), errstring(errno));
15164562Sgshapiro				}
15238032Speter			}
15364562Sgshapiro			if (tfd >= 0)
15438032Speter			{
15564562Sgshapiro				if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB))
15638032Speter					break;
15738032Speter				else if (LogLevel > 0 && (i % 32) == 0)
15838032Speter					sm_syslog(LOG_ALERT, e->e_id,
15964562Sgshapiro						  "queueup: cannot lock %s: %s",
16064562Sgshapiro						  tf, errstring(errno));
16164562Sgshapiro				if ((i % 32) == 31)
16264562Sgshapiro				{
16364562Sgshapiro					(void) close(tfd);
16464562Sgshapiro					tfd = -1;
16564562Sgshapiro				}
16638032Speter			}
16738032Speter
16838032Speter			if ((i % 32) == 31)
16938032Speter			{
17038032Speter				/* save the old temp file away */
17164562Sgshapiro				(void) rename(tf, queuename(e, TEMPQF_LETTER));
17238032Speter			}
17338032Speter			else
17464562Sgshapiro				(void) sleep(i % 32);
17538032Speter		}
17664562Sgshapiro		if (tfd < 0 || (tfp = fdopen(tfd, "w")) == NULL)
17738032Speter		{
17864562Sgshapiro			int save_errno = errno;
17964562Sgshapiro
18038032Speter			printopenfds(TRUE);
18164562Sgshapiro			errno = save_errno;
18238032Speter			syserr("!queueup: cannot create queue temp file %s, uid=%d",
18338032Speter				tf, geteuid());
18438032Speter		}
18538032Speter	}
18638032Speter
18738032Speter	if (tTd(40, 1))
18864562Sgshapiro		dprintf("\n>>>>> queueing %s/qf%s%s >>>>>\n",
18964562Sgshapiro			qid_printqueue(e->e_queuedir), e->e_id,
19038032Speter			newid ? " (new id)" : "");
19138032Speter	if (tTd(40, 3))
19238032Speter	{
19364562Sgshapiro		dprintf("  e_flags=");
19438032Speter		printenvflags(e);
19538032Speter	}
19638032Speter	if (tTd(40, 32))
19738032Speter	{
19864562Sgshapiro		dprintf("  sendq=");
19938032Speter		printaddr(e->e_sendqueue, TRUE);
20038032Speter	}
20138032Speter	if (tTd(40, 9))
20238032Speter	{
20364562Sgshapiro		dprintf("  tfp=");
20438032Speter		dumpfd(fileno(tfp), TRUE, FALSE);
20564562Sgshapiro		dprintf("  lockfp=");
20638032Speter		if (e->e_lockfp == NULL)
20764562Sgshapiro			dprintf("NULL\n");
20838032Speter		else
20938032Speter			dumpfd(fileno(e->e_lockfp), TRUE, FALSE);
21038032Speter	}
21138032Speter
21238032Speter	/*
21338032Speter	**  If there is no data file yet, create one.
21438032Speter	*/
21538032Speter
21664562Sgshapiro	if (bitset(EF_HAS_DF, e->e_flags))
21738032Speter	{
21864562Sgshapiro		if (e->e_dfp != NULL && bfcommit(e->e_dfp) < 0)
21964562Sgshapiro			syserr("!queueup: cannot commit data file %s, uid=%d",
22064562Sgshapiro				queuename(e, 'd'), geteuid());
22164562Sgshapiro	}
22264562Sgshapiro	else
22364562Sgshapiro	{
22464562Sgshapiro		int dfd;
22538032Speter		register FILE *dfp = NULL;
22664562Sgshapiro		char dfname[MAXPATHLEN];
22738032Speter		struct stat stbuf;
22838032Speter
22964562Sgshapiro		if (e->e_dfp != NULL && bftest(e->e_dfp))
23064562Sgshapiro			syserr("committing over bf file");
23164562Sgshapiro
23264562Sgshapiro		(void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
23364562Sgshapiro#if _FFR_QUEUE_FILE_MODE
23464562Sgshapiro		{
23564562Sgshapiro			MODE_T oldumask;
23664562Sgshapiro
23764562Sgshapiro			if (bitset(S_IWGRP, QueueFileMode))
23864562Sgshapiro				oldumask = umask(002);
23964562Sgshapiro			dfd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC,
24064562Sgshapiro				   QueueFileMode);
24164562Sgshapiro			if (bitset(S_IWGRP, QueueFileMode))
24264562Sgshapiro				(void) umask(oldumask);
24364562Sgshapiro		}
24464562Sgshapiro#else /* _FFR_QUEUE_FILE_MODE */
24564562Sgshapiro		dfd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode);
24664562Sgshapiro#endif /* _FFR_QUEUE_FILE_MODE */
24764562Sgshapiro		if (dfd < 0 || (dfp = fdopen(dfd, "w")) == NULL)
24838032Speter			syserr("!queueup: cannot create data temp file %s, uid=%d",
24938032Speter				dfname, geteuid());
25064562Sgshapiro		if (fstat(dfd, &stbuf) < 0)
25138032Speter			e->e_dfino = -1;
25238032Speter		else
25338032Speter		{
25438032Speter			e->e_dfdev = stbuf.st_dev;
25538032Speter			e->e_dfino = stbuf.st_ino;
25638032Speter		}
25738032Speter		e->e_flags |= EF_HAS_DF;
25864562Sgshapiro		memset(&mcibuf, '\0', sizeof mcibuf);
25938032Speter		mcibuf.mci_out = dfp;
26038032Speter		mcibuf.mci_mailer = FileMailer;
26138032Speter		(*e->e_putbody)(&mcibuf, e, NULL);
26264562Sgshapiro		if (fclose(dfp) < 0)
26364562Sgshapiro			syserr("!queueup: cannot save data temp file %s, uid=%d",
26464562Sgshapiro				dfname, geteuid());
26538032Speter		e->e_putbody = putbody;
26638032Speter	}
26738032Speter
26838032Speter	/*
26938032Speter	**  Output future work requests.
27038032Speter	**	Priority and creation time should be first, since
27138032Speter	**	they are required by orderq.
27238032Speter	*/
27338032Speter
27438032Speter	/* output queue version number (must be first!) */
27538032Speter	fprintf(tfp, "V%d\n", QF_VERSION);
27638032Speter
27738032Speter	/* output creation time */
27838032Speter	fprintf(tfp, "T%ld\n", (long) e->e_ctime);
27938032Speter
28038032Speter	/* output last delivery time */
28164562Sgshapiro# if _FFR_QUEUEDELAY
28238032Speter	fprintf(tfp, "K%ld\n", (long) e->e_dtime);
28364562Sgshapiro	fprintf(tfp, "G%d\n", e->e_queuealg);
28464562Sgshapiro	fprintf(tfp, "Y%ld\n", (long) e->e_queuedelay);
28564562Sgshapiro	if (tTd(40, 64))
28664562Sgshapiro		sm_syslog(LOG_INFO, e->e_id,
28764562Sgshapiro			"queue alg: %d delay %ld next: %ld (now: %ld)\n",
28864562Sgshapiro			e->e_queuealg, e->e_queuedelay, e->e_dtime, curtime());
28964562Sgshapiro# else /* _FFR_QUEUEDELAY */
29064562Sgshapiro	fprintf(tfp, "K%ld\n", (long) e->e_dtime);
29164562Sgshapiro# endif /* _FFR_QUEUEDELAY */
29238032Speter
29338032Speter	/* output number of delivery attempts */
29438032Speter	fprintf(tfp, "N%d\n", e->e_ntries);
29538032Speter
29638032Speter	/* output message priority */
29738032Speter	fprintf(tfp, "P%ld\n", e->e_msgpriority);
29838032Speter
29938032Speter	/* output inode number of data file */
30038032Speter	/* XXX should probably include device major/minor too */
30138032Speter	if (e->e_dfino != -1)
30238032Speter	{
30364562Sgshapiro		/*CONSTCOND*/
30438032Speter		if (sizeof e->e_dfino > sizeof(long))
30564562Sgshapiro			fprintf(tfp, "I%ld/%ld/%s\n",
30664562Sgshapiro				(long) major(e->e_dfdev),
30764562Sgshapiro				(long) minor(e->e_dfdev),
30838032Speter				quad_to_string(e->e_dfino));
30938032Speter		else
31064562Sgshapiro			fprintf(tfp, "I%ld/%ld/%lu\n",
31164562Sgshapiro				(long) major(e->e_dfdev),
31264562Sgshapiro				(long) minor(e->e_dfdev),
31338032Speter				(unsigned long) e->e_dfino);
31438032Speter	}
31538032Speter
31638032Speter	/* output body type */
31738032Speter	if (e->e_bodytype != NULL)
31838032Speter		fprintf(tfp, "B%s\n", denlstring(e->e_bodytype, TRUE, FALSE));
31938032Speter
32064562Sgshapiro# if _FFR_SAVE_CHARSET
32138032Speter	if (e->e_charset != NULL)
32238032Speter		fprintf(tfp, "X%s\n", denlstring(e->e_charset, TRUE, FALSE));
32364562Sgshapiro# endif /* _FFR_SAVE_CHARSET */
32438032Speter
32538032Speter	/* message from envelope, if it exists */
32638032Speter	if (e->e_message != NULL)
32738032Speter		fprintf(tfp, "M%s\n", denlstring(e->e_message, TRUE, FALSE));
32838032Speter
32938032Speter	/* send various flag bits through */
33038032Speter	p = buf;
33138032Speter	if (bitset(EF_WARNING, e->e_flags))
33238032Speter		*p++ = 'w';
33338032Speter	if (bitset(EF_RESPONSE, e->e_flags))
33438032Speter		*p++ = 'r';
33538032Speter	if (bitset(EF_HAS8BIT, e->e_flags))
33638032Speter		*p++ = '8';
33738032Speter	if (bitset(EF_DELETE_BCC, e->e_flags))
33838032Speter		*p++ = 'b';
33938032Speter	if (bitset(EF_RET_PARAM, e->e_flags))
34038032Speter		*p++ = 'd';
34138032Speter	if (bitset(EF_NO_BODY_RETN, e->e_flags))
34238032Speter		*p++ = 'n';
34338032Speter	*p++ = '\0';
34438032Speter	if (buf[0] != '\0')
34538032Speter		fprintf(tfp, "F%s\n", buf);
34638032Speter
34764562Sgshapiro	/* save $={persistentMacros} macro values */
34864562Sgshapiro	queueup_macros(macid("{persistentMacros}", NULL), tfp, e);
34938032Speter
35038032Speter	/* output name of sender */
35138032Speter	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
35238032Speter		p = e->e_sender;
35338032Speter	else
35438032Speter		p = e->e_from.q_paddr;
35538032Speter	fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE));
35638032Speter
35738032Speter	/* output ESMTP-supplied "original" information */
35838032Speter	if (e->e_envid != NULL)
35938032Speter		fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE));
36038032Speter
36164562Sgshapiro	/* output AUTH= parameter */
36264562Sgshapiro	if (e->e_auth_param != NULL)
36364562Sgshapiro		fprintf(tfp, "A%s\n", denlstring(e->e_auth_param,
36464562Sgshapiro						 TRUE, FALSE));
36564562Sgshapiro
36638032Speter	/* output list of recipient addresses */
36738032Speter	printctladdr(NULL, NULL);
36838032Speter	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
36938032Speter	{
37064562Sgshapiro		if (!QS_IS_UNDELIVERED(q->q_state))
37138032Speter			continue;
37264562Sgshapiro
37338032Speter		printctladdr(q, tfp);
37438032Speter		if (q->q_orcpt != NULL)
37538032Speter			fprintf(tfp, "Q%s\n",
37638032Speter				denlstring(q->q_orcpt, TRUE, FALSE));
37764562Sgshapiro		(void) putc('R', tfp);
37838032Speter		if (bitset(QPRIMARY, q->q_flags))
37964562Sgshapiro			(void) putc('P', tfp);
38038032Speter		if (bitset(QHASNOTIFY, q->q_flags))
38164562Sgshapiro			(void) putc('N', tfp);
38238032Speter		if (bitset(QPINGONSUCCESS, q->q_flags))
38364562Sgshapiro			(void) putc('S', tfp);
38438032Speter		if (bitset(QPINGONFAILURE, q->q_flags))
38564562Sgshapiro			(void) putc('F', tfp);
38638032Speter		if (bitset(QPINGONDELAY, q->q_flags))
38764562Sgshapiro			(void) putc('D', tfp);
38871345Sgshapiro		if (q->q_alias != NULL &&
38971345Sgshapiro		    bitset(QALIAS, q->q_alias->q_flags))
39071345Sgshapiro			(void) putc('A', tfp);
39164562Sgshapiro		(void) putc(':', tfp);
39264562Sgshapiro		(void) fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE));
39338032Speter		if (announce)
39438032Speter		{
39538032Speter			e->e_to = q->q_paddr;
39638032Speter			message("queued");
39738032Speter			if (LogLevel > 8)
39864562Sgshapiro				logdelivery(q->q_mailer, NULL, q->q_status,
39964562Sgshapiro					    "queued", NULL, (time_t) 0, e);
40038032Speter			e->e_to = NULL;
40138032Speter		}
40238032Speter		if (tTd(40, 1))
40338032Speter		{
40464562Sgshapiro			dprintf("queueing ");
40538032Speter			printaddr(q, FALSE);
40638032Speter		}
40738032Speter	}
40838032Speter
40938032Speter	/*
41038032Speter	**  Output headers for this message.
41138032Speter	**	Expand macros completely here.  Queue run will deal with
41238032Speter	**	everything as absolute headers.
41338032Speter	**		All headers that must be relative to the recipient
41438032Speter	**		can be cracked later.
41538032Speter	**	We set up a "null mailer" -- i.e., a mailer that will have
41638032Speter	**	no effect on the addresses as they are output.
41738032Speter	*/
41838032Speter
41964562Sgshapiro	memset((char *) &nullmailer, '\0', sizeof nullmailer);
42038032Speter	nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
42138032Speter			nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1;
42238032Speter	nullmailer.m_eol = "\n";
42364562Sgshapiro	memset(&mcibuf, '\0', sizeof mcibuf);
42438032Speter	mcibuf.mci_mailer = &nullmailer;
42538032Speter	mcibuf.mci_out = tfp;
42638032Speter
42738032Speter	define('g', "\201f", e);
42838032Speter	for (h = e->e_header; h != NULL; h = h->h_link)
42938032Speter	{
43043730Speter		if (h->h_value == NULL)
43138032Speter			continue;
43238032Speter
43338032Speter		/* don't output resent headers on non-resent messages */
43464562Sgshapiro		if (bitset(H_RESENT, h->h_flags) &&
43564562Sgshapiro		    !bitset(EF_RESENT, e->e_flags))
43638032Speter			continue;
43738032Speter
43838032Speter		/* expand macros; if null, don't output header at all */
43938032Speter		if (bitset(H_DEFAULT, h->h_flags))
44038032Speter		{
44138032Speter			(void) expand(h->h_value, buf, sizeof buf, e);
44238032Speter			if (buf[0] == '\0')
44338032Speter				continue;
44438032Speter		}
44538032Speter
44638032Speter		/* output this header */
44764562Sgshapiro		fprintf(tfp, "H?");
44838032Speter
44964562Sgshapiro		/* output conditional macro if present */
45064562Sgshapiro		if (h->h_macro != '\0')
45138032Speter		{
45264562Sgshapiro			if (bitset(0200, h->h_macro))
45364562Sgshapiro				fprintf(tfp, "${%s}",
45471345Sgshapiro					macname(bitidx(h->h_macro)));
45564562Sgshapiro			else
45664562Sgshapiro				fprintf(tfp, "$%c", h->h_macro);
45764562Sgshapiro		}
45864562Sgshapiro		else if (!bitzerop(h->h_mflags) &&
45964562Sgshapiro			 bitset(H_CHECK|H_ACHECK, h->h_flags))
46064562Sgshapiro		{
46138032Speter			int j;
46238032Speter
46364562Sgshapiro			/* if conditional, output the set of conditions */
46438032Speter			for (j = '\0'; j <= '\177'; j++)
46538032Speter				if (bitnset(j, h->h_mflags))
46638032Speter					(void) putc(j, tfp);
46738032Speter		}
46864562Sgshapiro		(void) putc('?', tfp);
46938032Speter
47038032Speter		/* output the header: expand macros, convert addresses */
47164562Sgshapiro		if (bitset(H_DEFAULT, h->h_flags) &&
47264562Sgshapiro		    !bitset(H_BINDLATE, h->h_flags))
47338032Speter		{
47438032Speter			fprintf(tfp, "%s: %s\n",
47538032Speter				h->h_field,
47638032Speter				denlstring(buf, FALSE, TRUE));
47738032Speter		}
47864562Sgshapiro		else if (bitset(H_FROM|H_RCPT, h->h_flags) &&
47964562Sgshapiro			 !bitset(H_BINDLATE, h->h_flags))
48038032Speter		{
48138032Speter			bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
48238032Speter			FILE *savetrace = TrafficLogFile;
48338032Speter
48438032Speter			TrafficLogFile = NULL;
48538032Speter
48638032Speter			if (bitset(H_FROM, h->h_flags))
48738032Speter				oldstyle = FALSE;
48838032Speter
48938032Speter			commaize(h, h->h_value, oldstyle, &mcibuf, e);
49038032Speter
49138032Speter			TrafficLogFile = savetrace;
49238032Speter		}
49338032Speter		else
49438032Speter		{
49538032Speter			fprintf(tfp, "%s: %s\n",
49638032Speter				h->h_field,
49738032Speter				denlstring(h->h_value, FALSE, TRUE));
49838032Speter		}
49938032Speter	}
50038032Speter
50138032Speter	/*
50238032Speter	**  Clean up.
50338032Speter	**
50438032Speter	**	Write a terminator record -- this is to prevent
50538032Speter	**	scurrilous crackers from appending any data.
50638032Speter	*/
50738032Speter
50838032Speter	fprintf(tfp, ".\n");
50938032Speter
51038032Speter	if (fflush(tfp) < 0 ||
51138032Speter	    (SuperSafe && fsync(fileno(tfp)) < 0) ||
51238032Speter	    ferror(tfp))
51338032Speter	{
51438032Speter		if (newid)
51538032Speter			syserr("!552 Error writing control file %s", tf);
51638032Speter		else
51738032Speter			syserr("!452 Error writing control file %s", tf);
51838032Speter	}
51938032Speter
52038032Speter	if (!newid)
52138032Speter	{
52238032Speter		/* rename (locked) tf to be (locked) qf */
52338032Speter		qf = queuename(e, 'q');
52438032Speter		if (rename(tf, qf) < 0)
52538032Speter			syserr("cannot rename(%s, %s), uid=%d",
52638032Speter				tf, qf, geteuid());
52764562Sgshapiro		/*
52864562Sgshapiro		**  fsync() after renaming to make sure
52964562Sgshapiro		**  metadata is written to disk on
53064562Sgshapiro		**  filesystems in which renames are
53164562Sgshapiro		**  not guaranteed such as softupdates.
53264562Sgshapiro		*/
53364562Sgshapiro
53464562Sgshapiro		if (tfd >= 0 && SuperSafe && fsync(tfd) < 0)
53571345Sgshapiro			syserr("!queueup: cannot fsync queue temp file %s", tf);
53664562Sgshapiro
53738032Speter		/* close and unlock old (locked) qf */
53838032Speter		if (e->e_lockfp != NULL)
53964562Sgshapiro			(void) fclose(e->e_lockfp);
54038032Speter		e->e_lockfp = tfp;
54138032Speter	}
54238032Speter	else
54338032Speter		qf = tf;
54438032Speter	errno = 0;
54538032Speter	e->e_flags |= EF_INQUEUE;
54638032Speter
54738032Speter	/* save log info */
54838032Speter	if (LogLevel > 79)
54938032Speter		sm_syslog(LOG_DEBUG, e->e_id, "queueup, qf=%s", qf);
55038032Speter
55138032Speter	if (tTd(40, 1))
55264562Sgshapiro		dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
55338032Speter	return;
55438032Speter}
55538032Speter
55664562Sgshapirostatic void
55738032Speterprintctladdr(a, tfp)
55838032Speter	register ADDRESS *a;
55938032Speter	FILE *tfp;
56038032Speter{
56164562Sgshapiro	char *user;
56238032Speter	register ADDRESS *q;
56338032Speter	uid_t uid;
56438032Speter	gid_t gid;
56538032Speter	static ADDRESS *lastctladdr = NULL;
56638032Speter	static uid_t lastuid;
56738032Speter
56838032Speter	/* initialization */
56938032Speter	if (a == NULL || a->q_alias == NULL || tfp == NULL)
57038032Speter	{
57138032Speter		if (lastctladdr != NULL && tfp != NULL)
57238032Speter			fprintf(tfp, "C\n");
57338032Speter		lastctladdr = NULL;
57438032Speter		lastuid = 0;
57538032Speter		return;
57638032Speter	}
57738032Speter
57838032Speter	/* find the active uid */
57938032Speter	q = getctladdr(a);
58038032Speter	if (q == NULL)
58138032Speter	{
58264562Sgshapiro		user = NULL;
58338032Speter		uid = 0;
58438032Speter		gid = 0;
58538032Speter	}
58638032Speter	else
58738032Speter	{
58864562Sgshapiro		user = q->q_ruser != NULL ? q->q_ruser : q->q_user;
58938032Speter		uid = q->q_uid;
59038032Speter		gid = q->q_gid;
59138032Speter	}
59238032Speter	a = a->q_alias;
59338032Speter
59438032Speter	/* check to see if this is the same as last time */
59538032Speter	if (lastctladdr != NULL && uid == lastuid &&
59638032Speter	    strcmp(lastctladdr->q_paddr, a->q_paddr) == 0)
59738032Speter		return;
59838032Speter	lastuid = uid;
59938032Speter	lastctladdr = a;
60038032Speter
60164562Sgshapiro	if (uid == 0 || user == NULL || user[0] == '\0')
60238032Speter		fprintf(tfp, "C");
60338032Speter	else
60438032Speter		fprintf(tfp, "C%s:%ld:%ld",
60564562Sgshapiro			denlstring(user, TRUE, FALSE), (long) uid, (long) gid);
60638032Speter	fprintf(tfp, ":%s\n", denlstring(a->q_paddr, TRUE, FALSE));
60738032Speter}
60838032Speter/*
60938032Speter**  RUNQUEUE -- run the jobs in the queue.
61038032Speter**
61138032Speter**	Gets the stuff out of the queue in some presumably logical
61238032Speter**	order and processes them.
61338032Speter**
61438032Speter**	Parameters:
61538032Speter**		forkflag -- TRUE if the queue scanning should be done in
61638032Speter**			a child process.  We double-fork so it is not our
61738032Speter**			child and we don't have to clean up after it.
61864562Sgshapiro**			FALSE can be ignored if we have multiple queues.
61938032Speter**		verbose -- if TRUE, print out status information.
62038032Speter**
62138032Speter**	Returns:
62238032Speter**		TRUE if the queue run successfully began.
62338032Speter**
62438032Speter**	Side Effects:
62538032Speter**		runs things in the mail queue.
62638032Speter*/
62738032Speter
62864562Sgshapirostatic ENVELOPE	QueueEnvelope;		/* the queue run envelope */
62964562Sgshapiroint		NumQueues = 0;		/* number of queues */
63064562Sgshapirostatic time_t	LastQueueTime = 0;	/* last time a queue ID assigned */
63164562Sgshapirostatic pid_t	LastQueuePid = -1;	/* last PID which had a queue ID */
63238032Speter
63364562Sgshapirostruct qpaths_s
63464562Sgshapiro{
63564562Sgshapiro	char	*qp_name;	/* name of queue dir */
63664562Sgshapiro	short	qp_subdirs;	/* use subdirs? */
63764562Sgshapiro};
63864562Sgshapiro
63964562Sgshapirotypedef struct qpaths_s QPATHS;
64064562Sgshapiro
64164562Sgshapiro/* values for qp_supdirs */
64264562Sgshapiro#define QP_NOSUB	0x0000	/* No subdirectories */
64364562Sgshapiro#define QP_SUBDF	0x0001	/* "df" subdirectory */
64464562Sgshapiro#define QP_SUBQF	0x0002	/* "qf" subdirectory */
64564562Sgshapiro#define QP_SUBXF	0x0004	/* "xf" subdirectory */
64664562Sgshapiro
64764562Sgshapirostatic QPATHS	*QPaths = NULL;		/* list of queue directories */
64864562Sgshapiro
64938032Speterbool
65038032Speterrunqueue(forkflag, verbose)
65138032Speter	bool forkflag;
65238032Speter	bool verbose;
65338032Speter{
65464562Sgshapiro	int i;
65564562Sgshapiro	bool ret = TRUE;
65664562Sgshapiro	static int curnum = 0;
65764562Sgshapiro
65871345Sgshapiro	DoQueueRun = FALSE;
65971345Sgshapiro
66071345Sgshapiro
66164562Sgshapiro	if (!forkflag && NumQueues > 1 && !verbose)
66264562Sgshapiro		forkflag = TRUE;
66364562Sgshapiro
66464562Sgshapiro	for (i = 0; i < NumQueues; i++)
66564562Sgshapiro	{
66664562Sgshapiro		/*
66764562Sgshapiro		**  Pick up where we left off, in case we
66864562Sgshapiro		**  used up all the children last time
66964562Sgshapiro		**  without finishing.
67064562Sgshapiro		*/
67164562Sgshapiro
67264562Sgshapiro		ret = run_single_queue(curnum, forkflag, verbose);
67364562Sgshapiro
67464562Sgshapiro		/*
67564562Sgshapiro		**  Failure means a message was printed for ETRN
67664562Sgshapiro		**  and subsequent queues are likely to fail as well.
67764562Sgshapiro		*/
67864562Sgshapiro
67964562Sgshapiro		if (!ret)
68064562Sgshapiro			break;
68164562Sgshapiro
68264562Sgshapiro		if (++curnum >= NumQueues)
68364562Sgshapiro			curnum = 0;
68464562Sgshapiro	}
68564562Sgshapiro	if (QueueIntvl != 0)
68664562Sgshapiro		(void) setevent(QueueIntvl, runqueueevent, 0);
68764562Sgshapiro	return ret;
68864562Sgshapiro}
68964562Sgshapiro/*
69064562Sgshapiro**  RUN_SINGLE_QUEUE -- run the jobs in a single queue.
69164562Sgshapiro**
69264562Sgshapiro**	Gets the stuff out of the queue in some presumably logical
69364562Sgshapiro**	order and processes them.
69464562Sgshapiro**
69564562Sgshapiro**	Parameters:
69664562Sgshapiro**		queuedir -- queue to process
69764562Sgshapiro**		forkflag -- TRUE if the queue scanning should be done in
69864562Sgshapiro**			a child process.  We double-fork so it is not our
69964562Sgshapiro**			child and we don't have to clean up after it.
70064562Sgshapiro**		verbose -- if TRUE, print out status information.
70164562Sgshapiro**
70264562Sgshapiro**	Returns:
70364562Sgshapiro**		TRUE if the queue run successfully began.
70464562Sgshapiro**
70564562Sgshapiro**	Side Effects:
70664562Sgshapiro**		runs things in the mail queue.
70764562Sgshapiro*/
70864562Sgshapiro
70964562Sgshapirostatic bool
71064562Sgshapirorun_single_queue(queuedir, forkflag, verbose)
71164562Sgshapiro	int queuedir;
71264562Sgshapiro	bool forkflag;
71364562Sgshapiro	bool verbose;
71464562Sgshapiro{
71538032Speter	register ENVELOPE *e;
71638032Speter	int njobs;
71738032Speter	int sequenceno = 0;
71871345Sgshapiro	time_t current_la_time, now;
71938032Speter	extern ENVELOPE BlankEnvelope;
72038032Speter
72138032Speter	/*
72238032Speter	**  If no work will ever be selected, don't even bother reading
72338032Speter	**  the queue.
72438032Speter	*/
72538032Speter
72664562Sgshapiro	CurrentLA = sm_getla(NULL);	/* get load average */
72738032Speter	current_la_time = curtime();
72838032Speter
72938032Speter	if (shouldqueue(WkRecipFact, current_la_time))
73038032Speter	{
73138032Speter		char *msg = "Skipping queue run -- load average too high";
73238032Speter
73338032Speter		if (verbose)
73438032Speter			message("458 %s\n", msg);
73538032Speter		if (LogLevel > 8)
73638032Speter			sm_syslog(LOG_INFO, NOQID,
73764562Sgshapiro				  "runqueue: %s",
73864562Sgshapiro				  msg);
73938032Speter		return FALSE;
74038032Speter	}
74138032Speter
74238032Speter	/*
74338032Speter	**  See if we already have too many children.
74438032Speter	*/
74538032Speter
74638032Speter	if (forkflag && QueueIntvl != 0 &&
74738032Speter	    MaxChildren > 0 && CurChildren >= MaxChildren)
74838032Speter	{
74964562Sgshapiro		char *msg = "Skipping queue run -- too many children";
75064562Sgshapiro
75164562Sgshapiro		if (verbose)
75264562Sgshapiro			message("458 %s (%d)\n", msg, CurChildren);
75364562Sgshapiro		if (LogLevel > 8)
75464562Sgshapiro			sm_syslog(LOG_INFO, NOQID,
75564562Sgshapiro				  "runqueue: %s (%d)",
75664562Sgshapiro				  msg, CurChildren);
75738032Speter		return FALSE;
75838032Speter	}
75938032Speter
76038032Speter	/*
76138032Speter	**  See if we want to go off and do other useful work.
76238032Speter	*/
76338032Speter
76438032Speter	if (forkflag)
76538032Speter	{
76638032Speter		pid_t pid;
76738032Speter
76864562Sgshapiro		(void) blocksignal(SIGCHLD);
76938032Speter		(void) setsignal(SIGCHLD, reapchild);
77038032Speter
77138032Speter		pid = dofork();
77238032Speter		if (pid == -1)
77338032Speter		{
77438032Speter			const char *msg = "Skipping queue run -- fork() failed";
77538032Speter			const char *err = errstring(errno);
77638032Speter
77738032Speter			if (verbose)
77838032Speter				message("458 %s: %s\n", msg, err);
77938032Speter			if (LogLevel > 8)
78038032Speter				sm_syslog(LOG_INFO, NOQID,
78164562Sgshapiro					  "runqueue: %s: %s",
78264562Sgshapiro					  msg, err);
78338032Speter			(void) releasesignal(SIGCHLD);
78438032Speter			return FALSE;
78538032Speter		}
78638032Speter		if (pid != 0)
78738032Speter		{
78838032Speter			/* parent -- pick up intermediate zombie */
78938032Speter			(void) blocksignal(SIGALRM);
79064562Sgshapiro			proc_list_add(pid, "Queue runner", PROC_QUEUE);
79138032Speter			(void) releasesignal(SIGALRM);
79264562Sgshapiro			(void) releasesignal(SIGCHLD);
79338032Speter			return TRUE;
79438032Speter		}
79564562Sgshapiro		/* child -- clean up signals */
79642575Speter		clrcontrol();
79738032Speter		proc_list_clear();
79842575Speter
79942575Speter		/* Add parent process as first child item */
80064562Sgshapiro		proc_list_add(getpid(), "Queue runner child process",
80164562Sgshapiro			      PROC_QUEUE_CHILD);
80264562Sgshapiro		(void) releasesignal(SIGCHLD);
80338032Speter		(void) setsignal(SIGCHLD, SIG_DFL);
80438032Speter		(void) setsignal(SIGHUP, intsig);
80564562Sgshapiro
80638032Speter	}
80738032Speter
80864562Sgshapiro	sm_setproctitle(TRUE, CurEnv, "running queue: %s",
80964562Sgshapiro			qid_printqueue(queuedir));
81038032Speter
81164562Sgshapiro	if (LogLevel > 69 || tTd(63, 99))
81238032Speter		sm_syslog(LOG_DEBUG, NOQID,
81364562Sgshapiro			  "runqueue %s, pid=%d, forkflag=%d",
81464562Sgshapiro			  qid_printqueue(queuedir), getpid(), forkflag);
81538032Speter
81638032Speter	/*
81738032Speter	**  Release any resources used by the daemon code.
81838032Speter	*/
81938032Speter
82038032Speter# if DAEMON
82138032Speter	clrdaemon();
82238032Speter# endif /* DAEMON */
82338032Speter
82438032Speter	/* force it to run expensive jobs */
82538032Speter	NoConnect = FALSE;
82638032Speter
82738032Speter	/* drop privileges */
82838032Speter	if (geteuid() == (uid_t) 0)
82938032Speter		(void) drop_privileges(FALSE);
83038032Speter
83138032Speter	/*
83238032Speter	**  Create ourselves an envelope
83338032Speter	*/
83438032Speter
83538032Speter	CurEnv = &QueueEnvelope;
83638032Speter	e = newenvelope(&QueueEnvelope, CurEnv);
83738032Speter	e->e_flags = BlankEnvelope.e_flags;
83873188Sgshapiro	e->e_parent = NULL;
83938032Speter
84038032Speter	/* make sure we have disconnected from parent */
84138032Speter	if (forkflag)
84238032Speter	{
84338032Speter		disconnect(1, e);
84438032Speter		QuickAbort = FALSE;
84538032Speter	}
84638032Speter
84738032Speter	/*
84838032Speter	**  If we are running part of the queue, always ignore stored
84938032Speter	**  host status.
85038032Speter	*/
85138032Speter
85238032Speter	if (QueueLimitId != NULL || QueueLimitSender != NULL ||
85338032Speter	    QueueLimitRecipient != NULL)
85438032Speter	{
85538032Speter		IgnoreHostStatus = TRUE;
85638032Speter		MinQueueAge = 0;
85738032Speter	}
85838032Speter
85938032Speter	/*
86038032Speter	**  Start making passes through the queue.
86138032Speter	**	First, read and sort the entire queue.
86238032Speter	**	Then, process the work in that order.
86338032Speter	**		But if you take too long, start over.
86438032Speter	*/
86538032Speter
86638032Speter	/* order the existing work requests */
86764562Sgshapiro	njobs = orderq(queuedir, FALSE);
86838032Speter
86964562Sgshapiro
87038032Speter	/* process them once at a time */
87138032Speter	while (WorkQ != NULL)
87238032Speter	{
87338032Speter		WORK *w = WorkQ;
87438032Speter
87538032Speter		WorkQ = WorkQ->w_next;
87638032Speter		e->e_to = NULL;
87738032Speter
87838032Speter		/*
87938032Speter		**  Ignore jobs that are too expensive for the moment.
88038032Speter		**
88138032Speter		**	Get new load average every 30 seconds.
88238032Speter		*/
88338032Speter
88471345Sgshapiro		now = curtime();
88571345Sgshapiro		if (current_la_time < now - 30)
88638032Speter		{
88764562Sgshapiro			CurrentLA = sm_getla(e);
88871345Sgshapiro			current_la_time = now;
88938032Speter		}
89038032Speter		if (shouldqueue(WkRecipFact, current_la_time))
89138032Speter		{
89238032Speter			char *msg = "Aborting queue run: load average too high";
89338032Speter
89438032Speter			if (Verbose)
89538032Speter				message("%s", msg);
89638032Speter			if (LogLevel > 8)
89738032Speter				sm_syslog(LOG_INFO, NOQID,
89864562Sgshapiro					  "runqueue: %s",
89964562Sgshapiro					  msg);
90038032Speter			break;
90138032Speter		}
90238032Speter		sequenceno++;
90338032Speter		if (shouldqueue(w->w_pri, w->w_ctime))
90438032Speter		{
90538032Speter			if (Verbose)
90638032Speter				message("");
90764562Sgshapiro			if (QueueSortOrder == QSO_BYPRIORITY)
90838032Speter			{
90938032Speter				if (Verbose)
91064562Sgshapiro					message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue",
91164562Sgshapiro						qid_printqueue(queuedir),
91238032Speter						w->w_name + 2,
91338032Speter						sequenceno,
91438032Speter						njobs);
91538032Speter				if (LogLevel > 8)
91638032Speter					sm_syslog(LOG_INFO, NOQID,
91764562Sgshapiro						  "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)",
91864562Sgshapiro						  qid_printqueue(queuedir),
91964562Sgshapiro						  w->w_name + 2,
92064562Sgshapiro						  w->w_pri,
92164562Sgshapiro						  CurrentLA,
92264562Sgshapiro						  sequenceno,
92364562Sgshapiro						  njobs);
92438032Speter				break;
92538032Speter			}
92638032Speter			else if (Verbose)
92764562Sgshapiro				message("Skipping %s/%s (sequence %d of %d)",
92864562Sgshapiro					qid_printqueue(queuedir),
92964562Sgshapiro					w->w_name + 2,
93064562Sgshapiro					sequenceno, njobs);
93138032Speter		}
93238032Speter		else
93338032Speter		{
93438032Speter			pid_t pid;
93538032Speter
93638032Speter			if (Verbose)
93738032Speter			{
93838032Speter				message("");
93964562Sgshapiro				message("Running %s/%s (sequence %d of %d)",
94064562Sgshapiro					qid_printqueue(queuedir),
94164562Sgshapiro					w->w_name + 2,
94264562Sgshapiro					sequenceno, njobs);
94338032Speter			}
94464562Sgshapiro			if (tTd(63, 100))
94564562Sgshapiro				sm_syslog(LOG_DEBUG, NOQID,
94664562Sgshapiro					  "runqueue %s dowork(%s)",
94764562Sgshapiro					  qid_printqueue(queuedir),
94864562Sgshapiro					  w->w_name + 2);
94964562Sgshapiro
95064562Sgshapiro			pid = dowork(queuedir, w->w_name + 2,
95164562Sgshapiro				     ForkQueueRuns, FALSE, e);
95238032Speter			errno = 0;
95338032Speter			if (pid != 0)
95438032Speter				(void) waitfor(pid);
95538032Speter		}
95638032Speter		free(w->w_name);
95738032Speter		if (w->w_host)
95838032Speter			free(w->w_host);
95938032Speter		free((char *) w);
96038032Speter	}
96138032Speter
96238032Speter	/* exit without the usual cleanup */
96338032Speter	e->e_id = NULL;
96464562Sgshapiro	if (forkflag)
96564562Sgshapiro		finis(TRUE, ExitStat);
96664562Sgshapiro	/* NOTREACHED */
96738032Speter	return TRUE;
96838032Speter}
96938032Speter
97038032Speter/*
97138032Speter**  RUNQUEUEEVENT -- stub for use in setevent
97238032Speter*/
97338032Speter
97464562Sgshapirostatic void
97538032Speterrunqueueevent()
97638032Speter{
97738032Speter	DoQueueRun = TRUE;
97838032Speter}
97938032Speter/*
98038032Speter**  ORDERQ -- order the work queue.
98138032Speter**
98238032Speter**	Parameters:
98364562Sgshapiro**		queuedir -- the index of the queue directory.
98438032Speter**		doall -- if set, include everything in the queue (even
98538032Speter**			the jobs that cannot be run because the load
98638032Speter**			average is too high).  Otherwise, exclude those
98738032Speter**			jobs.
98838032Speter**
98938032Speter**	Returns:
99038032Speter**		The number of request in the queue (not necessarily
99138032Speter**		the number of requests in WorkQ however).
99238032Speter**
99338032Speter**	Side Effects:
99438032Speter**		Sets WorkQ to the queue of available work, in order.
99538032Speter*/
99638032Speter
99738032Speter# define NEED_P		001
99838032Speter# define NEED_T		002
99938032Speter# define NEED_R		004
100038032Speter# define NEED_S		010
100171345Sgshapiro# define NEED_H		020
100238032Speter
100338032Speterstatic WORK	*WorkList = NULL;
100438032Speterstatic int	WorkListSize = 0;
100538032Speter
100664562Sgshapirostatic int
100764562Sgshapiroorderq(queuedir, doall)
100864562Sgshapiro	int queuedir;
100938032Speter	bool doall;
101038032Speter{
101138032Speter	register struct dirent *d;
101238032Speter	register WORK *w;
101338032Speter	register char *p;
101438032Speter	DIR *f;
101538032Speter	register int i;
101638032Speter	int wn = -1;
101738032Speter	int wc;
101838032Speter	QUEUE_CHAR *check;
101964562Sgshapiro	char qd[MAXPATHLEN];
102064562Sgshapiro	char qf[MAXPATHLEN];
102164562Sgshapiro
102264562Sgshapiro	if (queuedir == NOQDIR)
102364562Sgshapiro		(void) strlcpy(qd, ".", sizeof qd);
102464562Sgshapiro	else
102564562Sgshapiro		(void) snprintf(qd, sizeof qd, "%s%s",
102664562Sgshapiro				QPaths[queuedir].qp_name,
102764562Sgshapiro				(bitset(QP_SUBQF, QPaths[queuedir].qp_subdirs) ? "/qf" : ""));
102864562Sgshapiro
102938032Speter	if (tTd(41, 1))
103038032Speter	{
103164562Sgshapiro		dprintf("orderq:\n");
103238032Speter
103338032Speter		check = QueueLimitId;
103438032Speter		while (check != NULL)
103538032Speter		{
103664562Sgshapiro			dprintf("\tQueueLimitId = %s\n",
103764562Sgshapiro				check->queue_match);
103838032Speter			check = check->queue_next;
103938032Speter		}
104038032Speter
104138032Speter		check = QueueLimitSender;
104238032Speter		while (check != NULL)
104338032Speter		{
104464562Sgshapiro			dprintf("\tQueueLimitSender = %s\n",
104564562Sgshapiro				check->queue_match);
104638032Speter			check = check->queue_next;
104738032Speter		}
104838032Speter
104938032Speter		check = QueueLimitRecipient;
105038032Speter		while (check != NULL)
105138032Speter		{
105264562Sgshapiro			dprintf("\tQueueLimitRecipient = %s\n",
105364562Sgshapiro				check->queue_match);
105438032Speter			check = check->queue_next;
105538032Speter		}
105638032Speter	}
105738032Speter
105838032Speter	/* clear out old WorkQ */
105938032Speter	for (w = WorkQ; w != NULL; )
106038032Speter	{
106138032Speter		register WORK *nw = w->w_next;
106238032Speter
106338032Speter		WorkQ = nw;
106438032Speter		free(w->w_name);
106566494Sgshapiro		if (w->w_host != NULL)
106638032Speter			free(w->w_host);
106738032Speter		free((char *) w);
106838032Speter		w = nw;
106938032Speter	}
107038032Speter
107138032Speter	/* open the queue directory */
107264562Sgshapiro	f = opendir(qd);
107338032Speter	if (f == NULL)
107438032Speter	{
107564562Sgshapiro		syserr("orderq: cannot open \"%s\"", qid_printqueue(queuedir));
107664562Sgshapiro		return 0;
107738032Speter	}
107838032Speter
107938032Speter	/*
108038032Speter	**  Read the work directory.
108138032Speter	*/
108238032Speter
108338032Speter	while ((d = readdir(f)) != NULL)
108438032Speter	{
108538032Speter		FILE *cf;
108638032Speter		int qfver = 0;
108738032Speter		char lbuf[MAXNAME + 1];
108864562Sgshapiro		struct stat sbuf;
108938032Speter
109038032Speter		if (tTd(41, 50))
109164562Sgshapiro			dprintf("orderq: checking %s\n", d->d_name);
109238032Speter
109338032Speter		/* is this an interesting entry? */
109438032Speter		if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
109538032Speter			continue;
109638032Speter
109764562Sgshapiro		if (strlen(d->d_name) >= MAXQFNAME)
109842575Speter		{
109942575Speter			if (Verbose)
110042575Speter				printf("orderq: %s too long, %d max characters\n",
110142575Speter					d->d_name, MAXQFNAME);
110242575Speter			if (LogLevel > 0)
110342575Speter				sm_syslog(LOG_ALERT, NOQID,
110464562Sgshapiro					  "orderq: %s too long, %d max characters",
110564562Sgshapiro					  d->d_name, MAXQFNAME);
110638032Speter			continue;
110742575Speter		}
110838032Speter
110938032Speter		check = QueueLimitId;
111038032Speter		while (check != NULL)
111138032Speter		{
111238032Speter			if (strcontainedin(check->queue_match, d->d_name))
111338032Speter				break;
111438032Speter			else
111538032Speter				check = check->queue_next;
111638032Speter		}
111738032Speter		if (QueueLimitId != NULL && check == NULL)
111838032Speter			continue;
111938032Speter
112064562Sgshapiro		/* grow work list if necessary */
112138032Speter		if (++wn >= MaxQueueRun && MaxQueueRun > 0)
112238032Speter		{
112338032Speter			if (wn == MaxQueueRun && LogLevel > 0)
112464562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
112564562Sgshapiro					  "WorkList for %s maxed out at %d",
112664562Sgshapiro					  qid_printqueue(queuedir),
112764562Sgshapiro					  MaxQueueRun);
112838032Speter			continue;
112938032Speter		}
113038032Speter		if (wn >= WorkListSize)
113138032Speter		{
113264562Sgshapiro			grow_wlist(queuedir);
113338032Speter			if (wn >= WorkListSize)
113438032Speter				continue;
113538032Speter		}
113664562Sgshapiro		w = &WorkList[wn];
113738032Speter
113864562Sgshapiro		(void) snprintf(qf, sizeof qf, "%s/%s", qd, d->d_name);
113964562Sgshapiro		if (stat(qf, &sbuf) < 0)
114064562Sgshapiro		{
114164562Sgshapiro			if (errno != ENOENT)
114264562Sgshapiro				sm_syslog(LOG_INFO, NOQID,
114364562Sgshapiro					  "orderq: can't stat %s/%s",
114464562Sgshapiro					  qid_printqueue(queuedir), d->d_name);
114564562Sgshapiro			wn--;
114664562Sgshapiro			continue;
114764562Sgshapiro		}
114864562Sgshapiro		if (!bitset(S_IFREG, sbuf.st_mode))
114964562Sgshapiro		{
115064562Sgshapiro			/* Yikes!  Skip it or we will hang on open! */
115164562Sgshapiro			syserr("orderq: %s/%s is not a regular file",
115264562Sgshapiro			       qid_printqueue(queuedir), d->d_name);
115364562Sgshapiro			wn--;
115464562Sgshapiro			continue;
115564562Sgshapiro		}
115664562Sgshapiro
115764562Sgshapiro		/* avoid work if possible */
115866494Sgshapiro		if (QueueSortOrder == QSO_BYFILENAME &&
115966494Sgshapiro		    QueueLimitSender == NULL &&
116066494Sgshapiro		    QueueLimitRecipient == NULL)
116164562Sgshapiro		{
116264562Sgshapiro			w->w_name = newstr(d->d_name);
116364562Sgshapiro			w->w_host = NULL;
116464562Sgshapiro			w->w_lock = w->w_tooyoung = FALSE;
116564562Sgshapiro			w->w_pri = 0;
116664562Sgshapiro			w->w_ctime = 0;
116764562Sgshapiro			continue;
116864562Sgshapiro		}
116964562Sgshapiro
117064562Sgshapiro		/* open control file */
117164562Sgshapiro		cf = fopen(qf, "r");
117271345Sgshapiro
117338032Speter		if (cf == NULL)
117438032Speter		{
117538032Speter			/* this may be some random person sending hir msgs */
117638032Speter			/* syserr("orderq: cannot open %s", cbuf); */
117738032Speter			if (tTd(41, 2))
117864562Sgshapiro				dprintf("orderq: cannot open %s: %s\n",
117938032Speter					d->d_name, errstring(errno));
118038032Speter			errno = 0;
118138032Speter			wn--;
118238032Speter			continue;
118338032Speter		}
118438032Speter		w->w_name = newstr(d->d_name);
118538032Speter		w->w_host = NULL;
118638032Speter		w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB);
118738032Speter		w->w_tooyoung = FALSE;
118838032Speter
118938032Speter		/* make sure jobs in creation don't clog queue */
119038032Speter		w->w_pri = 0x7fffffff;
119138032Speter		w->w_ctime = 0;
119238032Speter
119338032Speter		/* extract useful information */
119438032Speter		i = NEED_P | NEED_T;
119571345Sgshapiro		if (QueueSortOrder == QSO_BYHOST)
119671345Sgshapiro		{
119771345Sgshapiro			/* need w_host set for host sort order */
119871345Sgshapiro			i |= NEED_H;
119971345Sgshapiro		}
120038032Speter		if (QueueLimitSender != NULL)
120138032Speter			i |= NEED_S;
120264562Sgshapiro		if (QueueLimitRecipient != NULL)
120338032Speter			i |= NEED_R;
120438032Speter		while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
120538032Speter		{
120638032Speter			int c;
120738032Speter			time_t age;
120838032Speter
120938032Speter			p = strchr(lbuf, '\n');
121038032Speter			if (p != NULL)
121138032Speter				*p = '\0';
121238032Speter			else
121338032Speter			{
121438032Speter				/* flush rest of overly long line */
121538032Speter				while ((c = getc(cf)) != EOF && c != '\n')
121638032Speter					continue;
121738032Speter			}
121838032Speter
121938032Speter			switch (lbuf[0])
122038032Speter			{
122138032Speter			  case 'V':
122238032Speter				qfver = atoi(&lbuf[1]);
122338032Speter				break;
122438032Speter
122538032Speter			  case 'P':
122638032Speter				w->w_pri = atol(&lbuf[1]);
122738032Speter				i &= ~NEED_P;
122838032Speter				break;
122938032Speter
123038032Speter			  case 'T':
123138032Speter				w->w_ctime = atol(&lbuf[1]);
123238032Speter				i &= ~NEED_T;
123338032Speter				break;
123438032Speter
123538032Speter			  case 'R':
123638032Speter				if (w->w_host == NULL &&
123738032Speter				    (p = strrchr(&lbuf[1], '@')) != NULL)
123864562Sgshapiro				{
123964562Sgshapiro					w->w_host = strrev(&p[1]);
124064562Sgshapiro					makelower(w->w_host);
124171345Sgshapiro					i &= ~NEED_H;
124264562Sgshapiro				}
124338032Speter				if (QueueLimitRecipient == NULL)
124438032Speter				{
124538032Speter					i &= ~NEED_R;
124638032Speter					break;
124738032Speter				}
124838032Speter				if (qfver > 0)
124938032Speter				{
125038032Speter					p = strchr(&lbuf[1], ':');
125138032Speter					if (p == NULL)
125238032Speter						p = &lbuf[1];
125338032Speter				}
125438032Speter				else
125538032Speter					p = &lbuf[1];
125638032Speter				check = QueueLimitRecipient;
125738032Speter				while (check != NULL)
125838032Speter				{
125938032Speter					if (strcontainedin(check->queue_match,
126038032Speter							   p))
126138032Speter						break;
126238032Speter					else
126338032Speter						check = check->queue_next;
126438032Speter				}
126538032Speter				if (check != NULL)
126638032Speter					i &= ~NEED_R;
126738032Speter				break;
126838032Speter
126938032Speter			  case 'S':
127064562Sgshapiro				check = QueueLimitSender;
127164562Sgshapiro				while (check != NULL)
127264562Sgshapiro				{
127364562Sgshapiro					if (strcontainedin(check->queue_match,
127464562Sgshapiro							   &lbuf[1]))
127564562Sgshapiro						break;
127664562Sgshapiro					else
127764562Sgshapiro						check = check->queue_next;
127864562Sgshapiro				}
127964562Sgshapiro				if (check != NULL)
128064562Sgshapiro					i &= ~NEED_S;
128138032Speter				break;
128238032Speter
128338032Speter			  case 'K':
128438032Speter				age = curtime() - (time_t) atol(&lbuf[1]);
128538032Speter				if (age >= 0 && MinQueueAge > 0 &&
128638032Speter				    age < MinQueueAge)
128738032Speter					w->w_tooyoung = TRUE;
128838032Speter				break;
128938032Speter
129038032Speter			  case 'N':
129138032Speter				if (atol(&lbuf[1]) == 0)
129238032Speter					w->w_tooyoung = FALSE;
129338032Speter				break;
129464562Sgshapiro
129564562Sgshapiro# if _FFR_QUEUEDELAY
129664562Sgshapiro/*
129764562Sgshapiro			  case 'G':
129864562Sgshapiro				queuealg = atoi(lbuf[1]);
129964562Sgshapiro				break;
130064562Sgshapiro			  case 'Y':
130164562Sgshapiro				queuedelay = (time_t) atol(&lbuf[1]);
130264562Sgshapiro				break;
130364562Sgshapiro*/
130464562Sgshapiro# endif /* _FFR_QUEUEDELAY */
130538032Speter			}
130638032Speter		}
130738032Speter		(void) fclose(cf);
130838032Speter
130938032Speter		if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) ||
131038032Speter		    bitset(NEED_R|NEED_S, i))
131138032Speter		{
131238032Speter			/* don't even bother sorting this job in */
131338032Speter			if (tTd(41, 49))
131464562Sgshapiro				dprintf("skipping %s (%x)\n", w->w_name, i);
131538032Speter			free(w->w_name);
131638032Speter			if (w->w_host)
131738032Speter				free(w->w_host);
131838032Speter			wn--;
131938032Speter		}
132038032Speter	}
132138032Speter	(void) closedir(f);
132238032Speter	wn++;
132338032Speter
132464562Sgshapiro	WorkQ = NULL;
132564562Sgshapiro	if (WorkList == NULL)
132664562Sgshapiro		return 0;
132738032Speter	wc = min(wn, WorkListSize);
132838032Speter	if (wc > MaxQueueRun && MaxQueueRun > 0)
132938032Speter		wc = MaxQueueRun;
133038032Speter
133164562Sgshapiro	if (QueueSortOrder == QSO_BYHOST)
133238032Speter	{
133338032Speter		/*
133438032Speter		**  Sort the work directory for the first time,
133538032Speter		**  based on host name, lock status, and priority.
133638032Speter		*/
133738032Speter
133838032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1);
133938032Speter
134038032Speter		/*
134138032Speter		**  If one message to host is locked, "lock" all messages
134238032Speter		**  to that host.
134338032Speter		*/
134438032Speter
134538032Speter		i = 0;
134638032Speter		while (i < wc)
134738032Speter		{
134838032Speter			if (!WorkList[i].w_lock)
134938032Speter			{
135038032Speter				i++;
135138032Speter				continue;
135238032Speter			}
135338032Speter			w = &WorkList[i];
135438032Speter			while (++i < wc)
135538032Speter			{
135638032Speter				if (WorkList[i].w_host == NULL &&
135738032Speter				    w->w_host == NULL)
135838032Speter					WorkList[i].w_lock = TRUE;
135938032Speter				else if (WorkList[i].w_host != NULL &&
136038032Speter					 w->w_host != NULL &&
136138032Speter					 sm_strcasecmp(WorkList[i].w_host, w->w_host) == 0)
136238032Speter					WorkList[i].w_lock = TRUE;
136338032Speter				else
136438032Speter					break;
136538032Speter			}
136638032Speter		}
136738032Speter
136838032Speter		/*
136938032Speter		**  Sort the work directory for the second time,
137038032Speter		**  based on lock status, host name, and priority.
137138032Speter		*/
137238032Speter
137338032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2);
137438032Speter	}
137564562Sgshapiro	else if (QueueSortOrder == QSO_BYTIME)
137638032Speter	{
137738032Speter		/*
137838032Speter		**  Simple sort based on submission time only.
137938032Speter		*/
138038032Speter
138138032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3);
138238032Speter	}
138364562Sgshapiro	else if (QueueSortOrder == QSO_BYFILENAME)
138464562Sgshapiro	{
138564562Sgshapiro		/*
138664562Sgshapiro		**  Sort based on qf filename.
138764562Sgshapiro		*/
138864562Sgshapiro
138964562Sgshapiro		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf4);
139064562Sgshapiro	}
139138032Speter	else
139238032Speter	{
139338032Speter		/*
139438032Speter		**  Simple sort based on queue priority only.
139538032Speter		*/
139638032Speter
139738032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0);
139838032Speter	}
139938032Speter
140038032Speter	/*
140138032Speter	**  Convert the work list into canonical form.
140238032Speter	**	Should be turning it into a list of envelopes here perhaps.
140338032Speter	*/
140438032Speter
140538032Speter	for (i = wc; --i >= 0; )
140638032Speter	{
140738032Speter		w = (WORK *) xalloc(sizeof *w);
140838032Speter		w->w_name = WorkList[i].w_name;
140938032Speter		w->w_host = WorkList[i].w_host;
141038032Speter		w->w_lock = WorkList[i].w_lock;
141138032Speter		w->w_tooyoung = WorkList[i].w_tooyoung;
141238032Speter		w->w_pri = WorkList[i].w_pri;
141338032Speter		w->w_ctime = WorkList[i].w_ctime;
141438032Speter		w->w_next = WorkQ;
141538032Speter		WorkQ = w;
141638032Speter	}
141738032Speter	if (WorkList != NULL)
141838032Speter		free(WorkList);
141938032Speter	WorkList = NULL;
142038032Speter	WorkListSize = 0;
142138032Speter
142238032Speter	if (tTd(40, 1))
142338032Speter	{
142438032Speter		for (w = WorkQ; w != NULL; w = w->w_next)
142564562Sgshapiro		{
142664562Sgshapiro			if (w->w_host != NULL)
142764562Sgshapiro				dprintf("%22s: pri=%ld %s\n",
142864562Sgshapiro					w->w_name, w->w_pri, w->w_host);
142964562Sgshapiro			else
143064562Sgshapiro				dprintf("%32s: pri=%ld\n",
143164562Sgshapiro					w->w_name, w->w_pri);
143264562Sgshapiro		}
143338032Speter	}
143438032Speter
143564562Sgshapiro	return wn;
143638032Speter}
143738032Speter/*
143838032Speter**  GROW_WLIST -- make the work list larger
143938032Speter**
144038032Speter**	Parameters:
144164562Sgshapiro**		queuedir -- the index for the queue directory.
144238032Speter**
144338032Speter**	Returns:
144438032Speter**		none.
144538032Speter**
144638032Speter**	Side Effects:
144738032Speter**		Adds another QUEUESEGSIZE entries to WorkList if possible.
144838032Speter**		It can fail if there isn't enough memory, so WorkListSize
144938032Speter**		should be checked again upon return.
145038032Speter*/
145138032Speter
145264562Sgshapirostatic void
145364562Sgshapirogrow_wlist(queuedir)
145464562Sgshapiro	int queuedir;
145538032Speter{
145638032Speter	if (tTd(41, 1))
145764562Sgshapiro		dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize);
145838032Speter	if (WorkList == NULL)
145938032Speter	{
146064562Sgshapiro		WorkList = (WORK *) xalloc((sizeof *WorkList) *
146164562Sgshapiro					   (QUEUESEGSIZE + 1));
146238032Speter		WorkListSize = QUEUESEGSIZE;
146338032Speter	}
146438032Speter	else
146538032Speter	{
146638032Speter		int newsize = WorkListSize + QUEUESEGSIZE;
146738032Speter		WORK *newlist = (WORK *) realloc((char *)WorkList,
146838032Speter					  (unsigned)sizeof(WORK) * (newsize + 1));
146938032Speter
147038032Speter		if (newlist != NULL)
147138032Speter		{
147238032Speter			WorkListSize = newsize;
147338032Speter			WorkList = newlist;
147438032Speter			if (LogLevel > 1)
147538032Speter			{
147664562Sgshapiro				sm_syslog(LOG_INFO, NOQID,
147764562Sgshapiro					  "grew WorkList for %s to %d",
147864562Sgshapiro					  qid_printqueue(queuedir),
147964562Sgshapiro					  WorkListSize);
148038032Speter			}
148138032Speter		}
148238032Speter		else if (LogLevel > 0)
148338032Speter		{
148438032Speter			sm_syslog(LOG_ALERT, NOQID,
148564562Sgshapiro				  "FAILED to grow WorkList for %s to %d",
148664562Sgshapiro				  qid_printqueue(queuedir), newsize);
148738032Speter		}
148838032Speter	}
148938032Speter	if (tTd(41, 1))
149064562Sgshapiro		dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize);
149138032Speter}
149238032Speter/*
149338032Speter**  WORKCMPF0 -- simple priority-only compare function.
149438032Speter**
149538032Speter**	Parameters:
149638032Speter**		a -- the first argument.
149738032Speter**		b -- the second argument.
149838032Speter**
149938032Speter**	Returns:
150038032Speter**		-1 if a < b
150138032Speter**		 0 if a == b
150238032Speter**		+1 if a > b
150338032Speter**
150438032Speter**	Side Effects:
150538032Speter**		none.
150638032Speter*/
150738032Speter
150864562Sgshapirostatic int
150938032Speterworkcmpf0(a, b)
151038032Speter	register WORK *a;
151138032Speter	register WORK *b;
151238032Speter{
151338032Speter	long pa = a->w_pri;
151438032Speter	long pb = b->w_pri;
151538032Speter
151638032Speter	if (pa == pb)
151738032Speter		return 0;
151838032Speter	else if (pa > pb)
151938032Speter		return 1;
152038032Speter	else
152138032Speter		return -1;
152238032Speter}
152338032Speter/*
152438032Speter**  WORKCMPF1 -- first compare function for ordering work based on host name.
152538032Speter**
152638032Speter**	Sorts on host name, lock status, and priority in that order.
152738032Speter**
152838032Speter**	Parameters:
152938032Speter**		a -- the first argument.
153038032Speter**		b -- the second argument.
153138032Speter**
153238032Speter**	Returns:
153338032Speter**		<0 if a < b
153438032Speter**		 0 if a == b
153538032Speter**		>0 if a > b
153638032Speter**
153738032Speter**	Side Effects:
153838032Speter**		none.
153938032Speter*/
154038032Speter
154164562Sgshapirostatic int
154238032Speterworkcmpf1(a, b)
154338032Speter	register WORK *a;
154438032Speter	register WORK *b;
154538032Speter{
154638032Speter	int i;
154738032Speter
154838032Speter	/* host name */
154938032Speter	if (a->w_host != NULL && b->w_host == NULL)
155038032Speter		return 1;
155138032Speter	else if (a->w_host == NULL && b->w_host != NULL)
155238032Speter		return -1;
155338032Speter	if (a->w_host != NULL && b->w_host != NULL &&
155438032Speter	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
155538032Speter		return i;
155638032Speter
155738032Speter	/* lock status */
155838032Speter	if (a->w_lock != b->w_lock)
155938032Speter		return b->w_lock - a->w_lock;
156038032Speter
156138032Speter	/* job priority */
156273188Sgshapiro	return workcmpf0(a, b);
156338032Speter}
156438032Speter/*
156538032Speter**  WORKCMPF2 -- second compare function for ordering work based on host name.
156638032Speter**
156738032Speter**	Sorts on lock status, host name, and priority in that order.
156838032Speter**
156938032Speter**	Parameters:
157038032Speter**		a -- the first argument.
157138032Speter**		b -- the second argument.
157238032Speter**
157338032Speter**	Returns:
157438032Speter**		<0 if a < b
157538032Speter**		 0 if a == b
157638032Speter**		>0 if a > b
157738032Speter**
157838032Speter**	Side Effects:
157938032Speter**		none.
158038032Speter*/
158138032Speter
158264562Sgshapirostatic int
158338032Speterworkcmpf2(a, b)
158438032Speter	register WORK *a;
158538032Speter	register WORK *b;
158638032Speter{
158738032Speter	int i;
158838032Speter
158938032Speter	/* lock status */
159038032Speter	if (a->w_lock != b->w_lock)
159138032Speter		return a->w_lock - b->w_lock;
159238032Speter
159338032Speter	/* host name */
159438032Speter	if (a->w_host != NULL && b->w_host == NULL)
159538032Speter		return 1;
159638032Speter	else if (a->w_host == NULL && b->w_host != NULL)
159738032Speter		return -1;
159838032Speter	if (a->w_host != NULL && b->w_host != NULL &&
159938032Speter	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
160038032Speter		return i;
160138032Speter
160238032Speter	/* job priority */
160373188Sgshapiro	return workcmpf0(a, b);
160438032Speter}
160538032Speter/*
160638032Speter**  WORKCMPF3 -- simple submission-time-only compare function.
160738032Speter**
160838032Speter**	Parameters:
160938032Speter**		a -- the first argument.
161038032Speter**		b -- the second argument.
161138032Speter**
161238032Speter**	Returns:
161338032Speter**		-1 if a < b
161438032Speter**		 0 if a == b
161538032Speter**		+1 if a > b
161638032Speter**
161738032Speter**	Side Effects:
161838032Speter**		none.
161938032Speter*/
162038032Speter
162164562Sgshapirostatic int
162238032Speterworkcmpf3(a, b)
162338032Speter	register WORK *a;
162438032Speter	register WORK *b;
162538032Speter{
162638032Speter	if (a->w_ctime > b->w_ctime)
162738032Speter		return 1;
162838032Speter	else if (a->w_ctime < b->w_ctime)
162938032Speter		return -1;
163038032Speter	else
163138032Speter		return 0;
163238032Speter}
163338032Speter/*
163464562Sgshapiro**  WORKCMPF4 -- compare based on file name
163564562Sgshapiro**
163664562Sgshapiro**	Parameters:
163764562Sgshapiro**		a -- the first argument.
163864562Sgshapiro**		b -- the second argument.
163964562Sgshapiro**
164064562Sgshapiro**	Returns:
164164562Sgshapiro**		-1 if a < b
164264562Sgshapiro**		 0 if a == b
164364562Sgshapiro**		+1 if a > b
164464562Sgshapiro**
164564562Sgshapiro**	Side Effects:
164664562Sgshapiro**		none.
164764562Sgshapiro*/
164864562Sgshapiro
164964562Sgshapirostatic int
165064562Sgshapiroworkcmpf4(a, b)
165164562Sgshapiro	register WORK *a;
165264562Sgshapiro	register WORK *b;
165364562Sgshapiro{
165464562Sgshapiro	return strcmp(a->w_name, b->w_name);
165564562Sgshapiro}
165664562Sgshapiro/*
165764562Sgshapiro**  STRREV -- reverse string
165864562Sgshapiro**
165964562Sgshapiro**	Returns a pointer to a new string that is the reverse of
166064562Sgshapiro**	the string pointed to by fwd.  The space for the new
166164562Sgshapiro**	string is obtained using xalloc().
166264562Sgshapiro**
166364562Sgshapiro**	Parameters:
166464562Sgshapiro**		fwd -- the string to reverse.
166564562Sgshapiro**
166664562Sgshapiro**	Returns:
166764562Sgshapiro**		the reversed string.
166864562Sgshapiro*/
166964562Sgshapiro
167064562Sgshapirostatic char *
167164562Sgshapirostrrev(fwd)
167264562Sgshapiro	char *fwd;
167364562Sgshapiro{
167464562Sgshapiro	char *rev = NULL;
167564562Sgshapiro	int len, cnt;
167664562Sgshapiro
167764562Sgshapiro	len = strlen(fwd);
167864562Sgshapiro	rev = xalloc(len + 1);
167964562Sgshapiro	for (cnt = 0; cnt < len; ++cnt)
168064562Sgshapiro		rev[cnt] = fwd[len - cnt - 1];
168164562Sgshapiro	rev[len] = '\0';
168264562Sgshapiro	return rev;
168364562Sgshapiro}
168464562Sgshapiro/*
168538032Speter**  DOWORK -- do a work request.
168638032Speter**
168738032Speter**	Parameters:
168864562Sgshapiro**		queuedir -- the index of the queue directory for the job.
168938032Speter**		id -- the ID of the job to run.
169038032Speter**		forkflag -- if set, run this in background.
169138032Speter**		requeueflag -- if set, reinstantiate the queue quickly.
169238032Speter**			This is used when expanding aliases in the queue.
169338032Speter**			If forkflag is also set, it doesn't wait for the
169438032Speter**			child.
169538032Speter**		e - the envelope in which to run it.
169638032Speter**
169738032Speter**	Returns:
169838032Speter**		process id of process that is running the queue job.
169938032Speter**
170038032Speter**	Side Effects:
170138032Speter**		The work request is satisfied if possible.
170238032Speter*/
170338032Speter
170438032Speterpid_t
170564562Sgshapirodowork(queuedir, id, forkflag, requeueflag, e)
170664562Sgshapiro	int queuedir;
170738032Speter	char *id;
170838032Speter	bool forkflag;
170938032Speter	bool requeueflag;
171038032Speter	register ENVELOPE *e;
171138032Speter{
171238032Speter	register pid_t pid;
171338032Speter
171438032Speter	if (tTd(40, 1))
171564562Sgshapiro		dprintf("dowork(%s/%s)\n", qid_printqueue(queuedir), id);
171638032Speter
171738032Speter	/*
171838032Speter	**  Fork for work.
171938032Speter	*/
172038032Speter
172138032Speter	if (forkflag)
172238032Speter	{
172364562Sgshapiro		/*
172464562Sgshapiro		**  Since the delivery may happen in a child and the
172564562Sgshapiro		**  parent does not wait, the parent may close the
172664562Sgshapiro		**  maps thereby removing any shared memory used by
172764562Sgshapiro		**  the map.  Therefore, close the maps now so the
172864562Sgshapiro		**  child will dynamically open them if necessary.
172964562Sgshapiro		*/
173064562Sgshapiro
173164562Sgshapiro		closemaps();
173264562Sgshapiro
173338032Speter		pid = fork();
173438032Speter		if (pid < 0)
173538032Speter		{
173638032Speter			syserr("dowork: cannot fork");
173738032Speter			return 0;
173838032Speter		}
173938032Speter		else if (pid > 0)
174038032Speter		{
174138032Speter			/* parent -- clean out connection cache */
174238032Speter			mci_flush(FALSE, NULL);
174338032Speter		}
174438032Speter		else
174538032Speter		{
174638032Speter			/* child -- error messages to the transcript */
174738032Speter			QuickAbort = OnlyOneError = FALSE;
174838032Speter		}
174938032Speter	}
175038032Speter	else
175138032Speter	{
175238032Speter		pid = 0;
175338032Speter	}
175438032Speter
175538032Speter	if (pid == 0)
175638032Speter	{
175738032Speter		/*
175838032Speter		**  CHILD
175938032Speter		**	Lock the control file to avoid duplicate deliveries.
176038032Speter		**		Then run the file as though we had just read it.
176138032Speter		**	We save an idea of the temporary name so we
176238032Speter		**		can recover on interrupt.
176338032Speter		*/
176438032Speter
176538032Speter		/* set basic modes, etc. */
176638032Speter		(void) alarm(0);
176764562Sgshapiro		clearstats();
176838032Speter		clearenvelope(e, FALSE);
176938032Speter		e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
177064562Sgshapiro		set_delivery_mode(SM_DELIVER, e);
177138032Speter		e->e_errormode = EM_MAIL;
177238032Speter		e->e_id = id;
177364562Sgshapiro		e->e_queuedir = queuedir;
177438032Speter		GrabTo = UseErrorsTo = FALSE;
177538032Speter		ExitStat = EX_OK;
177638032Speter		if (forkflag)
177738032Speter		{
177838032Speter			disconnect(1, e);
177964562Sgshapiro			OpMode = MD_QUEUERUN;
178038032Speter		}
178164562Sgshapiro		sm_setproctitle(TRUE, e, "%s: from queue", qid_printname(e));
178238032Speter		if (LogLevel > 76)
178338032Speter			sm_syslog(LOG_DEBUG, e->e_id,
178464562Sgshapiro				  "dowork, pid=%d",
178564562Sgshapiro				  getpid());
178638032Speter
178738032Speter		/* don't use the headers from sendmail.cf... */
178838032Speter		e->e_header = NULL;
178938032Speter
179038032Speter		/* read the queue control file -- return if locked */
179138032Speter		if (!readqf(e))
179238032Speter		{
179338032Speter			if (tTd(40, 4) && e->e_id != NULL)
179464562Sgshapiro				dprintf("readqf(%s) failed\n",
179564562Sgshapiro					qid_printname(e));
179638032Speter			e->e_id = NULL;
179738032Speter			if (forkflag)
179842575Speter				finis(FALSE, EX_OK);
179938032Speter			else
180038032Speter				return 0;
180138032Speter		}
180238032Speter
180338032Speter		e->e_flags |= EF_INQUEUE;
180438032Speter		eatheader(e, requeueflag);
180538032Speter
180638032Speter		if (requeueflag)
180738032Speter			queueup(e, FALSE);
180838032Speter
180938032Speter		/* do the delivery */
181038032Speter		sendall(e, SM_DELIVER);
181138032Speter
181238032Speter		/* finish up and exit */
181338032Speter		if (forkflag)
181442575Speter			finis(TRUE, ExitStat);
181538032Speter		else
181638032Speter			dropenvelope(e, TRUE);
181738032Speter	}
181838032Speter	e->e_id = NULL;
181938032Speter	return pid;
182038032Speter}
182138032Speter/*
182238032Speter**  READQF -- read queue file and set up environment.
182338032Speter**
182438032Speter**	Parameters:
182538032Speter**		e -- the envelope of the job to run.
182638032Speter**
182738032Speter**	Returns:
182838032Speter**		TRUE if it successfully read the queue file.
182938032Speter**		FALSE otherwise.
183038032Speter**
183138032Speter**	Side Effects:
183238032Speter**		The queue file is returned locked.
183338032Speter*/
183438032Speter
183564562Sgshapirostatic bool
183638032Speterreadqf(e)
183738032Speter	register ENVELOPE *e;
183838032Speter{
183938032Speter	register FILE *qfp;
184038032Speter	ADDRESS *ctladdr;
184138032Speter	struct stat st;
184238032Speter	char *bp;
184338032Speter	int qfver = 0;
184438032Speter	long hdrsize = 0;
184538032Speter	register char *p;
184638032Speter	char *orcpt = NULL;
184738032Speter	bool nomore = FALSE;
184864562Sgshapiro	MODE_T qsafe;
184964562Sgshapiro	char qf[MAXPATHLEN];
185038032Speter	char buf[MAXLINE];
185138032Speter
185238032Speter	/*
185338032Speter	**  Read and process the file.
185438032Speter	*/
185538032Speter
185664562Sgshapiro	(void) strlcpy(qf, queuename(e, 'q'), sizeof qf);
185738032Speter	qfp = fopen(qf, "r+");
185838032Speter	if (qfp == NULL)
185938032Speter	{
186064562Sgshapiro		int save_errno = errno;
186164562Sgshapiro
186238032Speter		if (tTd(40, 8))
186364562Sgshapiro			dprintf("readqf(%s): fopen failure (%s)\n",
186438032Speter				qf, errstring(errno));
186564562Sgshapiro		errno = save_errno;
186664562Sgshapiro		if (errno != ENOENT
186764562Sgshapiro		    )
186838032Speter			syserr("readqf: no control file %s", qf);
186938032Speter		return FALSE;
187038032Speter	}
187138032Speter
187238032Speter	if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB))
187338032Speter	{
187438032Speter		/* being processed by another queuer */
187564562Sgshapiro		if (Verbose)
187638032Speter			printf("%s: locked\n", e->e_id);
187764562Sgshapiro		if (tTd(40, 8))
187864562Sgshapiro			dprintf("%s: locked\n", e->e_id);
187938032Speter		if (LogLevel > 19)
188038032Speter			sm_syslog(LOG_DEBUG, e->e_id, "locked");
188138032Speter		(void) fclose(qfp);
188238032Speter		return FALSE;
188338032Speter	}
188438032Speter
188538032Speter	/*
188638032Speter	**  Check the queue file for plausibility to avoid attacks.
188738032Speter	*/
188838032Speter
188938032Speter	if (fstat(fileno(qfp), &st) < 0)
189038032Speter	{
189138032Speter		/* must have been being processed by someone else */
189238032Speter		if (tTd(40, 8))
189364562Sgshapiro			dprintf("readqf(%s): fstat failure (%s)\n",
189438032Speter				qf, errstring(errno));
189564562Sgshapiro		(void) fclose(qfp);
189638032Speter		return FALSE;
189738032Speter	}
189838032Speter
189964562Sgshapiro	qsafe = S_IWOTH|S_IWGRP;
190064562Sgshapiro#if _FFR_QUEUE_FILE_MODE
190164562Sgshapiro	if (bitset(S_IWGRP, QueueFileMode))
190264562Sgshapiro		qsafe &= ~S_IWGRP;
190364562Sgshapiro#endif /* _FFR_QUEUE_FILE_MODE */
190464562Sgshapiro
190564562Sgshapiro	if ((st.st_uid != geteuid() &&
190664562Sgshapiro	     st.st_uid != TrustedUid &&
190764562Sgshapiro	     geteuid() != RealUid) ||
190864562Sgshapiro	    bitset(qsafe, st.st_mode))
190938032Speter	{
191038032Speter		if (LogLevel > 0)
191138032Speter		{
191238032Speter			sm_syslog(LOG_ALERT, e->e_id,
191364562Sgshapiro				  "bogus queue file, uid=%d, mode=%o",
191464562Sgshapiro				  st.st_uid, st.st_mode);
191538032Speter		}
191638032Speter		if (tTd(40, 8))
191764562Sgshapiro			dprintf("readqf(%s): bogus file\n", qf);
191838032Speter		loseqfile(e, "bogus file uid in mqueue");
191964562Sgshapiro		(void) fclose(qfp);
192038032Speter		return FALSE;
192138032Speter	}
192238032Speter
192338032Speter	if (st.st_size == 0)
192438032Speter	{
192538032Speter		/* must be a bogus file -- if also old, just remove it */
192638032Speter		if (st.st_ctime + 10 * 60 < curtime())
192738032Speter		{
192864562Sgshapiro			(void) xunlink(queuename(e, 'd'));
192964562Sgshapiro			(void) xunlink(queuename(e, 'q'));
193038032Speter		}
193164562Sgshapiro		(void) fclose(qfp);
193238032Speter		return FALSE;
193338032Speter	}
193438032Speter
193538032Speter	if (st.st_nlink == 0)
193638032Speter	{
193738032Speter		/*
193838032Speter		**  Race condition -- we got a file just as it was being
193938032Speter		**  unlinked.  Just assume it is zero length.
194038032Speter		*/
194138032Speter
194264562Sgshapiro		(void) fclose(qfp);
194338032Speter		return FALSE;
194438032Speter	}
194538032Speter
194638032Speter	/* good file -- save this lock */
194738032Speter	e->e_lockfp = qfp;
194838032Speter
194938032Speter	/* do basic system initialization */
195038032Speter	initsys(e);
195138032Speter	define('i', e->e_id, e);
195238032Speter
195338032Speter	LineNumber = 0;
195438032Speter	e->e_flags |= EF_GLOBALERRS;
195564562Sgshapiro	OpMode = MD_QUEUERUN;
195638032Speter	ctladdr = NULL;
195738032Speter	e->e_dfino = -1;
195838032Speter	e->e_msgsize = -1;
195964562Sgshapiro# if _FFR_QUEUEDELAY
196064562Sgshapiro	e->e_queuealg = QD_LINEAR;
196164562Sgshapiro	e->e_queuedelay = (time_t) 0;
196264562Sgshapiro# endif /* _FFR_QUEUEDELAY */
196338032Speter	while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL)
196438032Speter	{
196538032Speter		u_long qflags;
196638032Speter		ADDRESS *q;
196738032Speter		int mid;
196871345Sgshapiro		time_t now;
196938032Speter		auto char *ep;
197038032Speter
197138032Speter		if (tTd(40, 4))
197264562Sgshapiro			dprintf("+++++ %s\n", bp);
197338032Speter		if (nomore)
197438032Speter		{
197538032Speter			/* hack attack */
197638032Speter			syserr("SECURITY ALERT: extra data in qf: %s", bp);
197764562Sgshapiro			(void) fclose(qfp);
197838032Speter			loseqfile(e, "bogus queue line");
197938032Speter			return FALSE;
198038032Speter		}
198138032Speter		switch (bp[0])
198238032Speter		{
198338032Speter		  case 'V':		/* queue file version number */
198438032Speter			qfver = atoi(&bp[1]);
198538032Speter			if (qfver <= QF_VERSION)
198638032Speter				break;
198738032Speter			syserr("Version number in qf (%d) greater than max (%d)",
198838032Speter				qfver, QF_VERSION);
198964562Sgshapiro			(void) fclose(qfp);
199038032Speter			loseqfile(e, "unsupported qf file version");
199138032Speter			return FALSE;
199238032Speter
199338032Speter		  case 'C':		/* specify controlling user */
199438032Speter			ctladdr = setctluser(&bp[1], qfver);
199538032Speter			break;
199638032Speter
199738032Speter		  case 'Q':		/* original recipient */
199838032Speter			orcpt = newstr(&bp[1]);
199938032Speter			break;
200038032Speter
200138032Speter		  case 'R':		/* specify recipient */
200238032Speter			p = bp;
200338032Speter			qflags = 0;
200438032Speter			if (qfver >= 1)
200538032Speter			{
200638032Speter				/* get flag bits */
200738032Speter				while (*++p != '\0' && *p != ':')
200838032Speter				{
200938032Speter					switch (*p)
201038032Speter					{
201138032Speter					  case 'N':
201238032Speter						qflags |= QHASNOTIFY;
201338032Speter						break;
201438032Speter
201538032Speter					  case 'S':
201638032Speter						qflags |= QPINGONSUCCESS;
201738032Speter						break;
201838032Speter
201938032Speter					  case 'F':
202038032Speter						qflags |= QPINGONFAILURE;
202138032Speter						break;
202238032Speter
202338032Speter					  case 'D':
202438032Speter						qflags |= QPINGONDELAY;
202538032Speter						break;
202638032Speter
202738032Speter					  case 'P':
202838032Speter						qflags |= QPRIMARY;
202938032Speter						break;
203071345Sgshapiro
203171345Sgshapiro					  case 'A':
203271345Sgshapiro						if (ctladdr != NULL)
203371345Sgshapiro							ctladdr->q_flags |= QALIAS;
203471345Sgshapiro						break;
203538032Speter					}
203638032Speter				}
203738032Speter			}
203838032Speter			else
203938032Speter				qflags |= QPRIMARY;
204038032Speter			q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e);
204138032Speter			if (q != NULL)
204238032Speter			{
204338032Speter				q->q_alias = ctladdr;
204438032Speter				if (qfver >= 1)
204538032Speter					q->q_flags &= ~Q_PINGFLAGS;
204638032Speter				q->q_flags |= qflags;
204738032Speter				q->q_orcpt = orcpt;
204838032Speter				(void) recipient(q, &e->e_sendqueue, 0, e);
204938032Speter			}
205038032Speter			orcpt = NULL;
205138032Speter			break;
205238032Speter
205338032Speter		  case 'E':		/* specify error recipient */
205438032Speter			/* no longer used */
205538032Speter			break;
205638032Speter
205738032Speter		  case 'H':		/* header */
205866494Sgshapiro			(void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e);
205938032Speter			hdrsize += strlen(&bp[1]);
206038032Speter			break;
206138032Speter
206238032Speter		  case 'L':		/* Solaris Content-Length: */
206338032Speter		  case 'M':		/* message */
206438032Speter			/* ignore this; we want a new message next time */
206538032Speter			break;
206638032Speter
206738032Speter		  case 'S':		/* sender */
206838032Speter			setsender(newstr(&bp[1]), e, NULL, '\0', TRUE);
206938032Speter			break;
207038032Speter
207138032Speter		  case 'B':		/* body type */
207238032Speter			e->e_bodytype = newstr(&bp[1]);
207338032Speter			break;
207438032Speter
207564562Sgshapiro# if _FFR_SAVE_CHARSET
207638032Speter		  case 'X':		/* character set */
207738032Speter			e->e_charset = newstr(&bp[1]);
207838032Speter			break;
207964562Sgshapiro# endif /* _FFR_SAVE_CHARSET */
208038032Speter
208138032Speter		  case 'D':		/* data file name */
208238032Speter			/* obsolete -- ignore */
208338032Speter			break;
208438032Speter
208538032Speter		  case 'T':		/* init time */
208638032Speter			e->e_ctime = atol(&bp[1]);
208738032Speter			break;
208838032Speter
208938032Speter		  case 'I':		/* data file's inode number */
209038032Speter			/* regenerated below */
209138032Speter			break;
209238032Speter
209364562Sgshapiro		  case 'K':	/* time of last delivery attempt */
209438032Speter			e->e_dtime = atol(&buf[1]);
209538032Speter			break;
209638032Speter
209764562Sgshapiro# if _FFR_QUEUEDELAY
209864562Sgshapiro		  case 'G':	/* queue delay algorithm */
209964562Sgshapiro			e->e_queuealg = atoi(&buf[1]);
210064562Sgshapiro			break;
210164562Sgshapiro		  case 'Y':	/* current delay */
210264562Sgshapiro			e->e_queuedelay = (time_t) atol(&buf[1]);
210364562Sgshapiro			break;
210464562Sgshapiro# endif /* _FFR_QUEUEDELAY */
210564562Sgshapiro
210638032Speter		  case 'N':		/* number of delivery attempts */
210738032Speter			e->e_ntries = atoi(&buf[1]);
210838032Speter
210938032Speter			/* if this has been tried recently, let it be */
211071345Sgshapiro			now = curtime();
211171345Sgshapiro			if (e->e_ntries > 0 && e->e_dtime <= now &&
211271345Sgshapiro			    now < e->e_dtime + queuedelay(e))
211338032Speter			{
211464562Sgshapiro				char *howlong;
211538032Speter
211671345Sgshapiro				howlong = pintvl(now - e->e_dtime, TRUE);
211764562Sgshapiro				if (Verbose)
211838032Speter					printf("%s: too young (%s)\n",
211964562Sgshapiro					       e->e_id, howlong);
212064562Sgshapiro				if (tTd(40, 8))
212164562Sgshapiro					dprintf("%s: too young (%s)\n",
212238032Speter						e->e_id, howlong);
212338032Speter				if (LogLevel > 19)
212438032Speter					sm_syslog(LOG_DEBUG, e->e_id,
212564562Sgshapiro						  "too young (%s)",
212664562Sgshapiro						  howlong);
212738032Speter				e->e_id = NULL;
212838032Speter				unlockqueue(e);
212938032Speter				return FALSE;
213038032Speter			}
213164562Sgshapiro			define(macid("{ntries}", NULL), newstr(&buf[1]), e);
213264562Sgshapiro
213364562Sgshapiro# if NAMED_BIND
213464562Sgshapiro			/* adjust BIND parameters immediately */
213564562Sgshapiro			if (e->e_ntries == 0)
213664562Sgshapiro			{
213764562Sgshapiro				_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
213864562Sgshapiro				_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
213964562Sgshapiro			}
214064562Sgshapiro			else
214164562Sgshapiro			{
214264562Sgshapiro				_res.retry = TimeOuts.res_retry[RES_TO_NORMAL];
214364562Sgshapiro				_res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL];
214464562Sgshapiro			}
214564562Sgshapiro# endif /* NAMED_BIND */
214638032Speter			break;
214738032Speter
214838032Speter		  case 'P':		/* message priority */
214938032Speter			e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
215038032Speter			break;
215138032Speter
215238032Speter		  case 'F':		/* flag bits */
215338032Speter			if (strncmp(bp, "From ", 5) == 0)
215438032Speter			{
215538032Speter				/* we are being spoofed! */
215638032Speter				syserr("SECURITY ALERT: bogus qf line %s", bp);
215764562Sgshapiro				(void) fclose(qfp);
215838032Speter				loseqfile(e, "bogus queue line");
215938032Speter				return FALSE;
216038032Speter			}
216138032Speter			for (p = &bp[1]; *p != '\0'; p++)
216238032Speter			{
216338032Speter				switch (*p)
216438032Speter				{
216538032Speter				  case 'w':	/* warning sent */
216638032Speter					e->e_flags |= EF_WARNING;
216738032Speter					break;
216838032Speter
216938032Speter				  case 'r':	/* response */
217038032Speter					e->e_flags |= EF_RESPONSE;
217138032Speter					break;
217238032Speter
217338032Speter				  case '8':	/* has 8 bit data */
217438032Speter					e->e_flags |= EF_HAS8BIT;
217538032Speter					break;
217638032Speter
217738032Speter				  case 'b':	/* delete Bcc: header */
217838032Speter					e->e_flags |= EF_DELETE_BCC;
217938032Speter					break;
218038032Speter
218138032Speter				  case 'd':	/* envelope has DSN RET= */
218238032Speter					e->e_flags |= EF_RET_PARAM;
218338032Speter					break;
218438032Speter
218538032Speter				  case 'n':	/* don't return body */
218638032Speter					e->e_flags |= EF_NO_BODY_RETN;
218738032Speter					break;
218838032Speter				}
218938032Speter			}
219038032Speter			break;
219138032Speter
219238032Speter		  case 'Z':		/* original envelope id from ESMTP */
219338032Speter			e->e_envid = newstr(&bp[1]);
219464562Sgshapiro			define(macid("{dsn_envid}", NULL), newstr(&bp[1]), e);
219538032Speter			break;
219638032Speter
219764562Sgshapiro		  case 'A':		/* AUTH= parameter */
219864562Sgshapiro			e->e_auth_param = newstr(&bp[1]);
219964562Sgshapiro			break;
220064562Sgshapiro
220138032Speter		  case '$':		/* define macro */
220264562Sgshapiro			{
220364562Sgshapiro				char *p;
220464562Sgshapiro
220564562Sgshapiro				mid = macid(&bp[1], &ep);
220671345Sgshapiro				if (mid == 0)
220771345Sgshapiro					break;
220871345Sgshapiro
220964562Sgshapiro				p = newstr(ep);
221064562Sgshapiro				define(mid, p, e);
221164562Sgshapiro
221264562Sgshapiro				/*
221364562Sgshapiro				**  HACK ALERT: Unfortunately, 8.10 and
221464562Sgshapiro				**  8.11 reused the ${if_addr} and
221564562Sgshapiro				**  ${if_family} macros for both the incoming
221664562Sgshapiro				**  interface address/family (getrequests())
221764562Sgshapiro				**  and the outgoing interface address/family
221864562Sgshapiro				**  (makeconnection()).  In order for D_BINDIF
221964562Sgshapiro				**  to work properly, have to preserve the
222064562Sgshapiro				**  incoming information in the queue file for
222164562Sgshapiro				**  later delivery attempts.  The original
222264562Sgshapiro				**  information is stored in the envelope
222364562Sgshapiro				**  in readqf() so it can be stored in
222464562Sgshapiro				**  queueup_macros().  This should be fixed
222564562Sgshapiro				**  in 8.12.
222664562Sgshapiro				*/
222764562Sgshapiro
222864562Sgshapiro				if (strcmp(macname(mid), "if_addr") == 0)
222964562Sgshapiro					e->e_if_macros[EIF_ADDR] = p;
223064562Sgshapiro			}
223138032Speter			break;
223238032Speter
223338032Speter		  case '.':		/* terminate file */
223438032Speter			nomore = TRUE;
223538032Speter			break;
223638032Speter
223738032Speter		  default:
223838032Speter			syserr("readqf: %s: line %d: bad line \"%s\"",
223938032Speter				qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
224064562Sgshapiro			(void) fclose(qfp);
224138032Speter			loseqfile(e, "unrecognized line");
224238032Speter			return FALSE;
224338032Speter		}
224438032Speter
224538032Speter		if (bp != buf)
224638032Speter			free(bp);
224738032Speter	}
224838032Speter
224938032Speter	/*
225038032Speter	**  If we haven't read any lines, this queue file is empty.
225138032Speter	**  Arrange to remove it without referencing any null pointers.
225238032Speter	*/
225338032Speter
225438032Speter	if (LineNumber == 0)
225538032Speter	{
225638032Speter		errno = 0;
225738032Speter		e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
225838032Speter		return TRUE;
225938032Speter	}
226038032Speter
226164562Sgshapiro	/* possibly set ${dsn_ret} macro */
226264562Sgshapiro	if (bitset(EF_RET_PARAM, e->e_flags))
226364562Sgshapiro	{
226464562Sgshapiro		if (bitset(EF_NO_BODY_RETN, e->e_flags))
226564562Sgshapiro			define(macid("{dsn_ret}", NULL), "hdrs", e);
226664562Sgshapiro		else
226764562Sgshapiro			define(macid("{dsn_ret}", NULL), "full", e);
226864562Sgshapiro	}
226964562Sgshapiro
227038032Speter	/*
227138032Speter	**  Arrange to read the data file.
227238032Speter	*/
227338032Speter
227438032Speter	p = queuename(e, 'd');
227538032Speter	e->e_dfp = fopen(p, "r");
227638032Speter	if (e->e_dfp == NULL)
227738032Speter	{
227838032Speter		syserr("readqf: cannot open %s", p);
227938032Speter	}
228038032Speter	else
228138032Speter	{
228238032Speter		e->e_flags |= EF_HAS_DF;
228338032Speter		if (fstat(fileno(e->e_dfp), &st) >= 0)
228438032Speter		{
228538032Speter			e->e_msgsize = st.st_size + hdrsize;
228638032Speter			e->e_dfdev = st.st_dev;
228738032Speter			e->e_dfino = st.st_ino;
228838032Speter		}
228938032Speter	}
229038032Speter
229138032Speter	return TRUE;
229238032Speter}
229338032Speter/*
229464562Sgshapiro**  PRTSTR -- print a string, "unprintable" characters are shown as \oct
229564562Sgshapiro**
229664562Sgshapiro**	Parameters:
229764562Sgshapiro**		s -- string to print
229864562Sgshapiro**		ml -- maximum length of output
229964562Sgshapiro**
230064562Sgshapiro**	Returns:
230164562Sgshapiro**		none.
230264562Sgshapiro**
230364562Sgshapiro**	Side Effects:
230464562Sgshapiro**		Prints a string on stdout.
230564562Sgshapiro*/
230664562Sgshapiro
230764562Sgshapirostatic void
230864562Sgshapiroprtstr(s, ml)
230964562Sgshapiro	char *s;
231064562Sgshapiro	int ml;
231164562Sgshapiro{
231264562Sgshapiro	char c;
231364562Sgshapiro
231464562Sgshapiro	if (s == NULL)
231564562Sgshapiro		return;
231664562Sgshapiro	while (ml-- > 0 && ((c = *s++) != '\0'))
231764562Sgshapiro	{
231864562Sgshapiro		if (c == '\\')
231964562Sgshapiro		{
232064562Sgshapiro			if (ml-- > 0)
232164562Sgshapiro			{
232264562Sgshapiro				putchar(c);
232364562Sgshapiro				putchar(c);
232464562Sgshapiro			}
232564562Sgshapiro		}
232664562Sgshapiro		else if (isascii(c) && isprint(c))
232764562Sgshapiro			putchar(c);
232864562Sgshapiro		else
232964562Sgshapiro		{
233064562Sgshapiro			if ((ml -= 3) > 0)
233164562Sgshapiro				printf("\\%03o", c);
233264562Sgshapiro		}
233364562Sgshapiro	}
233464562Sgshapiro}
233564562Sgshapiro/*
233638032Speter**  PRINTQUEUE -- print out a representation of the mail queue
233738032Speter**
233838032Speter**	Parameters:
233938032Speter**		none.
234038032Speter**
234138032Speter**	Returns:
234238032Speter**		none.
234338032Speter**
234438032Speter**	Side Effects:
234538032Speter**		Prints a listing of the mail queue on the standard output.
234638032Speter*/
234738032Speter
234838032Spetervoid
234938032Speterprintqueue()
235038032Speter{
235164562Sgshapiro	int i, nrequests = 0;
235264562Sgshapiro
235364562Sgshapiro	for (i = 0; i < NumQueues; i++)
235464562Sgshapiro		nrequests += print_single_queue(i);
235564562Sgshapiro	if (NumQueues > 1)
235664562Sgshapiro		printf("\t\tTotal Requests: %d\n", nrequests);
235764562Sgshapiro}
235864562Sgshapiro/*
235964562Sgshapiro**  PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue
236064562Sgshapiro**
236164562Sgshapiro**	Parameters:
236264562Sgshapiro**		queuedir -- queue directory
236364562Sgshapiro**
236464562Sgshapiro**	Returns:
236571345Sgshapiro**		number of entries
236664562Sgshapiro**
236764562Sgshapiro**	Side Effects:
236864562Sgshapiro**		Prints a listing of the mail queue on the standard output.
236964562Sgshapiro*/
237064562Sgshapiro
237164562Sgshapirostatic int
237264562Sgshapiroprint_single_queue(queuedir)
237364562Sgshapiro	int queuedir;
237464562Sgshapiro{
237538032Speter	register WORK *w;
237638032Speter	FILE *f;
237738032Speter	int nrequests;
237864562Sgshapiro	char qd[MAXPATHLEN];
237964562Sgshapiro	char qddf[MAXPATHLEN];
238038032Speter	char buf[MAXLINE];
238138032Speter
238264562Sgshapiro	if (queuedir == NOQDIR)
238364562Sgshapiro	{
238464562Sgshapiro		(void) strlcpy(qd, ".", sizeof qd);
238564562Sgshapiro		(void) strlcpy(qddf, ".", sizeof qddf);
238664562Sgshapiro	}
238764562Sgshapiro	else
238864562Sgshapiro	{
238964562Sgshapiro		(void) snprintf(qd, sizeof qd, "%s%s",
239064562Sgshapiro				QPaths[queuedir].qp_name,
239164562Sgshapiro				(bitset(QP_SUBQF, QPaths[queuedir].qp_subdirs) ? "/qf" : ""));
239264562Sgshapiro		(void) snprintf(qddf, sizeof qddf, "%s%s",
239364562Sgshapiro				QPaths[queuedir].qp_name,
239464562Sgshapiro				(bitset(QP_SUBDF, QPaths[queuedir].qp_subdirs) ? "/df" : ""));
239564562Sgshapiro	}
239664562Sgshapiro
239738032Speter	/*
239838032Speter	**  Check for permission to print the queue
239938032Speter	*/
240038032Speter
240138032Speter	if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
240238032Speter	{
240338032Speter		struct stat st;
240438032Speter# ifdef NGROUPS_MAX
240538032Speter		int n;
240638032Speter		extern GIDSET_T InitialGidSet[NGROUPS_MAX];
240764562Sgshapiro# endif /* NGROUPS_MAX */
240838032Speter
240964562Sgshapiro		if (stat(qd, &st) < 0)
241038032Speter		{
241164562Sgshapiro			syserr("Cannot stat %s", qid_printqueue(queuedir));
241264562Sgshapiro			return 0;
241338032Speter		}
241438032Speter# ifdef NGROUPS_MAX
241538032Speter		n = NGROUPS_MAX;
241638032Speter		while (--n >= 0)
241738032Speter		{
241838032Speter			if (InitialGidSet[n] == st.st_gid)
241938032Speter				break;
242038032Speter		}
242138032Speter		if (n < 0 && RealGid != st.st_gid)
242264562Sgshapiro# else /* NGROUPS_MAX */
242338032Speter		if (RealGid != st.st_gid)
242464562Sgshapiro# endif /* NGROUPS_MAX */
242538032Speter		{
242638032Speter			usrerr("510 You are not permitted to see the queue");
242738032Speter			setstat(EX_NOPERM);
242864562Sgshapiro			return 0;
242938032Speter		}
243038032Speter	}
243138032Speter
243238032Speter	/*
243338032Speter	**  Read and order the queue.
243438032Speter	*/
243538032Speter
243664562Sgshapiro	nrequests = orderq(queuedir, TRUE);
243738032Speter
243838032Speter	/*
243938032Speter	**  Print the work list that we have read.
244038032Speter	*/
244138032Speter
244238032Speter	/* first see if there is anything */
244338032Speter	if (nrequests <= 0)
244438032Speter	{
244564562Sgshapiro		printf("%s is empty\n", qid_printqueue(queuedir));
244664562Sgshapiro		return 0;
244738032Speter	}
244838032Speter
244964562Sgshapiro	CurrentLA = sm_getla(NULL);	/* get load average */
245038032Speter
245164562Sgshapiro	printf("\t\t%s (%d request%s", qid_printqueue(queuedir), nrequests,
245264562Sgshapiro	       nrequests == 1 ? "" : "s");
245338032Speter	if (MaxQueueRun > 0 && nrequests > MaxQueueRun)
245438032Speter		printf(", only %d printed", MaxQueueRun);
245538032Speter	if (Verbose)
245664562Sgshapiro		printf(")\n----Q-ID---- --Size-- -Priority- ---Q-Time--- ---------Sender/Recipient--------\n");
245738032Speter	else
245864562Sgshapiro		printf(")\n----Q-ID---- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
245938032Speter	for (w = WorkQ; w != NULL; w = w->w_next)
246038032Speter	{
246138032Speter		struct stat st;
246238032Speter		auto time_t submittime = 0;
246338032Speter		long dfsize;
246438032Speter		int flags = 0;
246538032Speter		int qfver;
246638032Speter		char statmsg[MAXLINE];
246738032Speter		char bodytype[MAXNAME + 1];
246864562Sgshapiro		char qf[MAXPATHLEN];
246938032Speter
247064562Sgshapiro		printf("%12s", w->w_name + 2);
247164562Sgshapiro		(void) snprintf(qf, sizeof qf, "%s/%s", qd, w->w_name);
247264562Sgshapiro		f = fopen(qf, "r");
247338032Speter		if (f == NULL)
247438032Speter		{
247538032Speter			printf(" (job completed)\n");
247638032Speter			errno = 0;
247738032Speter			continue;
247838032Speter		}
247938032Speter		w->w_name[0] = 'd';
248064562Sgshapiro		(void) snprintf(qf, sizeof qf, "%s/%s", qddf, w->w_name);
248164562Sgshapiro		if (stat(qf, &st) >= 0)
248238032Speter			dfsize = st.st_size;
248338032Speter		else
248438032Speter			dfsize = -1;
248538032Speter		if (w->w_lock)
248638032Speter			printf("*");
248738032Speter		else if (w->w_tooyoung)
248838032Speter			printf("-");
248938032Speter		else if (shouldqueue(w->w_pri, w->w_ctime))
249038032Speter			printf("X");
249138032Speter		else
249238032Speter			printf(" ");
249338032Speter		errno = 0;
249438032Speter
249538032Speter		statmsg[0] = bodytype[0] = '\0';
249638032Speter		qfver = 0;
249738032Speter		while (fgets(buf, sizeof buf, f) != NULL)
249838032Speter		{
249938032Speter			register int i;
250038032Speter			register char *p;
250138032Speter
250238032Speter			fixcrlf(buf, TRUE);
250338032Speter			switch (buf[0])
250438032Speter			{
250538032Speter			  case 'V':	/* queue file version */
250638032Speter				qfver = atoi(&buf[1]);
250738032Speter				break;
250838032Speter
250938032Speter			  case 'M':	/* error message */
251038032Speter				if ((i = strlen(&buf[1])) >= sizeof statmsg)
251138032Speter					i = sizeof statmsg - 1;
251264562Sgshapiro				memmove(statmsg, &buf[1], i);
251338032Speter				statmsg[i] = '\0';
251438032Speter				break;
251538032Speter
251638032Speter			  case 'B':	/* body type */
251738032Speter				if ((i = strlen(&buf[1])) >= sizeof bodytype)
251838032Speter					i = sizeof bodytype - 1;
251964562Sgshapiro				memmove(bodytype, &buf[1], i);
252038032Speter				bodytype[i] = '\0';
252138032Speter				break;
252238032Speter
252338032Speter			  case 'S':	/* sender name */
252438032Speter				if (Verbose)
252564562Sgshapiro				{
252664562Sgshapiro					printf("%8ld %10ld%c%.12s ",
252764562Sgshapiro					       dfsize,
252864562Sgshapiro					       w->w_pri,
252964562Sgshapiro					       bitset(EF_WARNING, flags) ? '+' : ' ',
253064562Sgshapiro					       ctime(&submittime) + 4);
253164562Sgshapiro					prtstr(&buf[1], 78);
253264562Sgshapiro				}
253338032Speter				else
253464562Sgshapiro				{
253564562Sgshapiro					printf("%8ld %.16s ", dfsize,
253664562Sgshapiro					    ctime(&submittime));
253764562Sgshapiro					prtstr(&buf[1], 40);
253864562Sgshapiro				}
253938032Speter				if (statmsg[0] != '\0' || bodytype[0] != '\0')
254038032Speter				{
254138032Speter					printf("\n    %10.10s", bodytype);
254238032Speter					if (statmsg[0] != '\0')
254338032Speter						printf("   (%.*s)",
254464562Sgshapiro						       Verbose ? 100 : 60,
254564562Sgshapiro						       statmsg);
254638032Speter				}
254738032Speter				break;
254838032Speter
254938032Speter			  case 'C':	/* controlling user */
255038032Speter				if (Verbose)
255138032Speter					printf("\n\t\t\t\t      (---%.74s---)",
255264562Sgshapiro					       &buf[1]);
255338032Speter				break;
255438032Speter
255538032Speter			  case 'R':	/* recipient name */
255638032Speter				p = &buf[1];
255738032Speter				if (qfver >= 1)
255838032Speter				{
255938032Speter					p = strchr(p, ':');
256038032Speter					if (p == NULL)
256138032Speter						break;
256238032Speter					p++;
256338032Speter				}
256438032Speter				if (Verbose)
256564562Sgshapiro				{
256664562Sgshapiro					printf("\n\t\t\t\t\t      ");
256764562Sgshapiro					prtstr(p, 73);
256864562Sgshapiro				}
256938032Speter				else
257064562Sgshapiro				{
257164562Sgshapiro					printf("\n\t\t\t\t       ");
257264562Sgshapiro					prtstr(p, 40);
257364562Sgshapiro				}
257438032Speter				break;
257538032Speter
257638032Speter			  case 'T':	/* creation time */
257738032Speter				submittime = atol(&buf[1]);
257838032Speter				break;
257938032Speter
258038032Speter			  case 'F':	/* flag bits */
258138032Speter				for (p = &buf[1]; *p != '\0'; p++)
258238032Speter				{
258338032Speter					switch (*p)
258438032Speter					{
258538032Speter					  case 'w':
258638032Speter						flags |= EF_WARNING;
258738032Speter						break;
258838032Speter					}
258938032Speter				}
259038032Speter			}
259138032Speter		}
259238032Speter		if (submittime == (time_t) 0)
259338032Speter			printf(" (no control file)");
259438032Speter		printf("\n");
259538032Speter		(void) fclose(f);
259638032Speter	}
259764562Sgshapiro	return nrequests;
259838032Speter}
259938032Speter/*
260038032Speter**  QUEUENAME -- build a file name in the queue directory for this envelope.
260138032Speter**
260238032Speter**	Parameters:
260338032Speter**		e -- envelope to build it in/from.
260438032Speter**		type -- the file type, used as the first character
260538032Speter**			of the file name.
260638032Speter**
260738032Speter**	Returns:
260864562Sgshapiro**		a pointer to the queue name (in a static buffer).
260938032Speter**
261038032Speter**	Side Effects:
261164562Sgshapiro**		If no id code is already assigned, queuename() will
261264562Sgshapiro**		assign an id code with assign_queueid().  If no queue
261364562Sgshapiro**		directory is assigned, one will be set with setnewqueue().
261438032Speter*/
261538032Speter
261638032Speterchar *
261738032Speterqueuename(e, type)
261838032Speter	register ENVELOPE *e;
261938032Speter	int type;
262038032Speter{
262164562Sgshapiro	char *sub = "";
262264562Sgshapiro	static char buf[MAXPATHLEN];
262338032Speter
262464562Sgshapiro	/* Assign an ID if needed */
262538032Speter	if (e->e_id == NULL)
262664562Sgshapiro		assign_queueid(e);
262764562Sgshapiro
262864562Sgshapiro	/* Assign a queue directory if needed */
262964562Sgshapiro	if (e->e_queuedir == NOQDIR)
263064562Sgshapiro		setnewqueue(e);
263164562Sgshapiro
263264562Sgshapiro	if (e->e_queuedir == NOQDIR)
263364562Sgshapiro		(void) snprintf(buf, sizeof buf, "%cf%s",
263464562Sgshapiro				type, e->e_id);
263564562Sgshapiro	else
263638032Speter	{
263764562Sgshapiro		switch (type)
263864562Sgshapiro		{
263964562Sgshapiro		  case 'd':
264064562Sgshapiro			if (bitset(QP_SUBDF, QPaths[e->e_queuedir].qp_subdirs))
264164562Sgshapiro				sub = "/df";
264264562Sgshapiro			break;
264338032Speter
264471345Sgshapiro		  case TEMPQF_LETTER:
264564562Sgshapiro		  case 't':
264671345Sgshapiro		  case LOSEQF_LETTER:
264764562Sgshapiro		  case 'q':
264864562Sgshapiro			if (bitset(QP_SUBQF, QPaths[e->e_queuedir].qp_subdirs))
264964562Sgshapiro				sub = "/qf";
265064562Sgshapiro			break;
265164562Sgshapiro
265264562Sgshapiro		  case 'x':
265364562Sgshapiro			if (bitset(QP_SUBXF, QPaths[e->e_queuedir].qp_subdirs))
265464562Sgshapiro				sub = "/xf";
265564562Sgshapiro			break;
265638032Speter		}
265738032Speter
265864562Sgshapiro		(void) snprintf(buf, sizeof buf, "%s%s/%cf%s",
265964562Sgshapiro				QPaths[e->e_queuedir].qp_name,
266064562Sgshapiro				sub, type, e->e_id);
266164562Sgshapiro	}
266238032Speter
266364562Sgshapiro	if (tTd(7, 2))
266464562Sgshapiro		dprintf("queuename: %s\n", buf);
266564562Sgshapiro	return buf;
266664562Sgshapiro}
266764562Sgshapiro/*
266864562Sgshapiro**  ASSIGN_QUEUEID -- assign a queue ID for this envelope.
266964562Sgshapiro**
267064562Sgshapiro**	Assigns an id code if one does not already exist.
267164562Sgshapiro**	This code assumes that nothing will remain in the queue for
267264562Sgshapiro**	longer than 60 years.  It is critical that files with the given
267364562Sgshapiro**	name not already exist in the queue.
267464562Sgshapiro**	Also initializes e_queuedir to NOQDIR.
267564562Sgshapiro**
267664562Sgshapiro**	Parameters:
267764562Sgshapiro**		e -- envelope to set it in.
267864562Sgshapiro**
267964562Sgshapiro**	Returns:
268064562Sgshapiro**		none.
268164562Sgshapiro*/
268238032Speter
268364562Sgshapirostatic char	Base60Code[] =	"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx";
268438032Speter
268564562Sgshapirovoid
268664562Sgshapiroassign_queueid(e)
268764562Sgshapiro	register ENVELOPE *e;
268864562Sgshapiro{
268964562Sgshapiro	pid_t pid = getpid();
269064562Sgshapiro	static char cX = 0;
269164562Sgshapiro	static long random_offset;
269264562Sgshapiro	struct tm *tm;
269364562Sgshapiro	char idbuf[MAXQFNAME - 2];
269438032Speter
269564562Sgshapiro	if (e->e_id != NULL)
269664562Sgshapiro		return;
269738032Speter
269864562Sgshapiro	/* see if we need to get a new base time/pid */
269964562Sgshapiro	if (cX >= 60 || LastQueueTime == 0 || LastQueuePid != pid)
270064562Sgshapiro	{
270164562Sgshapiro		time_t then = LastQueueTime;
270264562Sgshapiro
270364562Sgshapiro		/* if the first time through, pick a random offset */
270464562Sgshapiro		if (LastQueueTime == 0)
270564562Sgshapiro			random_offset = get_random();
270664562Sgshapiro
270764562Sgshapiro		while ((LastQueueTime = curtime()) == then &&
270864562Sgshapiro		       LastQueuePid == pid)
270938032Speter		{
271064562Sgshapiro			(void) sleep(1);
271138032Speter		}
271264562Sgshapiro		LastQueuePid = getpid();
271364562Sgshapiro		cX = 0;
271438032Speter	}
271564562Sgshapiro	if (tTd(7, 50))
271664562Sgshapiro		dprintf("assign_queueid: random_offset = %ld (%d)\n",
271764562Sgshapiro			random_offset, (int)(cX + random_offset) % 60);
271838032Speter
271964562Sgshapiro	tm = gmtime(&LastQueueTime);
272064562Sgshapiro	idbuf[0] = Base60Code[tm->tm_year % 60];
272164562Sgshapiro	idbuf[1] = Base60Code[tm->tm_mon];
272264562Sgshapiro	idbuf[2] = Base60Code[tm->tm_mday];
272364562Sgshapiro	idbuf[3] = Base60Code[tm->tm_hour];
272464562Sgshapiro	idbuf[4] = Base60Code[tm->tm_min];
272564562Sgshapiro	idbuf[5] = Base60Code[tm->tm_sec];
272664562Sgshapiro	idbuf[6] = Base60Code[((int)cX++ + random_offset) % 60];
272764562Sgshapiro	(void) snprintf(&idbuf[7], sizeof idbuf - 7, "%05d",
272864562Sgshapiro			(int) LastQueuePid);
272964562Sgshapiro	e->e_id = newstr(idbuf);
273064562Sgshapiro	define('i', e->e_id, e);
273164562Sgshapiro	e->e_queuedir = NOQDIR;
273264562Sgshapiro	if (tTd(7, 1))
273364562Sgshapiro		dprintf("assign_queueid: assigned id %s, e=%lx\n",
273464562Sgshapiro			e->e_id, (u_long) e);
273564562Sgshapiro	if (LogLevel > 93)
273664562Sgshapiro		sm_syslog(LOG_DEBUG, e->e_id, "assigned id");
273738032Speter}
273838032Speter/*
273964562Sgshapiro**  SYNC_QUEUE_TIME -- Assure exclusive PID in any given second
274064562Sgshapiro**
274164562Sgshapiro**	Make sure one PID can't be used by two processes in any one second.
274264562Sgshapiro**
274364562Sgshapiro**		If the system rotates PIDs fast enough, may get the
274464562Sgshapiro**		same pid in the same second for two distinct processes.
274564562Sgshapiro**		This will interfere with the queue file naming system.
274664562Sgshapiro**
274764562Sgshapiro**	Parameters:
274864562Sgshapiro**		none
274964562Sgshapiro**
275064562Sgshapiro**	Returns:
275164562Sgshapiro**		none
275264562Sgshapiro*/
275364562Sgshapirovoid
275464562Sgshapirosync_queue_time()
275564562Sgshapiro{
275664562Sgshapiro# if FAST_PID_RECYCLE
275764562Sgshapiro	if (OpMode != MD_TEST &&
275864562Sgshapiro	    OpMode != MD_VERIFY &&
275964562Sgshapiro	    LastQueueTime > 0 &&
276064562Sgshapiro	    LastQueuePid == getpid() &&
276164562Sgshapiro	    curtime() == LastQueueTime)
276264562Sgshapiro		(void) sleep(1);
276364562Sgshapiro# endif /* FAST_PID_RECYCLE */
276464562Sgshapiro}
276564562Sgshapiro/*
276638032Speter**  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
276738032Speter**
276838032Speter**	Parameters:
276938032Speter**		e -- the envelope to unlock.
277038032Speter**
277138032Speter**	Returns:
277238032Speter**		none
277338032Speter**
277438032Speter**	Side Effects:
277538032Speter**		unlocks the queue for `e'.
277638032Speter*/
277738032Speter
277838032Spetervoid
277938032Speterunlockqueue(e)
278038032Speter	ENVELOPE *e;
278138032Speter{
278238032Speter	if (tTd(51, 4))
278364562Sgshapiro		dprintf("unlockqueue(%s)\n",
278438032Speter			e->e_id == NULL ? "NOQUEUE" : e->e_id);
278538032Speter
278664562Sgshapiro
278738032Speter	/* if there is a lock file in the envelope, close it */
278838032Speter	if (e->e_lockfp != NULL)
278964562Sgshapiro		(void) fclose(e->e_lockfp);
279038032Speter	e->e_lockfp = NULL;
279138032Speter
279238032Speter	/* don't create a queue id if we don't already have one */
279338032Speter	if (e->e_id == NULL)
279438032Speter		return;
279538032Speter
279638032Speter	/* remove the transcript */
279738032Speter	if (LogLevel > 87)
279838032Speter		sm_syslog(LOG_DEBUG, e->e_id, "unlock");
279938032Speter	if (!tTd(51, 104))
280038032Speter		xunlink(queuename(e, 'x'));
280138032Speter
280238032Speter}
280338032Speter/*
280438032Speter**  SETCTLUSER -- create a controlling address
280538032Speter**
280638032Speter**	Create a fake "address" given only a local login name; this is
280738032Speter**	used as a "controlling user" for future recipient addresses.
280838032Speter**
280938032Speter**	Parameters:
281038032Speter**		user -- the user name of the controlling user.
281138032Speter**		qfver -- the version stamp of this qf file.
281238032Speter**
281338032Speter**	Returns:
281438032Speter**		An address descriptor for the controlling user.
281538032Speter**
281638032Speter**	Side Effects:
281738032Speter**		none.
281838032Speter*/
281938032Speter
282064562Sgshapirostatic ADDRESS *
282138032Spetersetctluser(user, qfver)
282238032Speter	char *user;
282338032Speter	int qfver;
282438032Speter{
282538032Speter	register ADDRESS *a;
282638032Speter	struct passwd *pw;
282738032Speter	char *p;
282838032Speter
282938032Speter	/*
283038032Speter	**  See if this clears our concept of controlling user.
283138032Speter	*/
283238032Speter
283338032Speter	if (user == NULL || *user == '\0')
283438032Speter		return NULL;
283538032Speter
283638032Speter	/*
283738032Speter	**  Set up addr fields for controlling user.
283838032Speter	*/
283938032Speter
284038032Speter	a = (ADDRESS *) xalloc(sizeof *a);
284164562Sgshapiro	memset((char *) a, '\0', sizeof *a);
284238032Speter
284338032Speter	if (*user == '\0')
284438032Speter	{
284538032Speter		p = NULL;
284638032Speter		a->q_user = newstr(DefUser);
284738032Speter	}
284838032Speter	else if (*user == ':')
284938032Speter	{
285038032Speter		p = &user[1];
285138032Speter		a->q_user = newstr(p);
285238032Speter	}
285338032Speter	else
285438032Speter	{
285538032Speter		p = strtok(user, ":");
285638032Speter		a->q_user = newstr(user);
285738032Speter		if (qfver >= 2)
285838032Speter		{
285938032Speter			if ((p = strtok(NULL, ":")) != NULL)
286038032Speter				a->q_uid = atoi(p);
286138032Speter			if ((p = strtok(NULL, ":")) != NULL)
286238032Speter				a->q_gid = atoi(p);
286338032Speter			if ((p = strtok(NULL, ":")) != NULL)
286438032Speter				a->q_flags |= QGOODUID;
286538032Speter		}
286638032Speter		else if ((pw = sm_getpwnam(user)) != NULL)
286738032Speter		{
286866494Sgshapiro			if (*pw->pw_dir == '\0')
286966494Sgshapiro				a->q_home = NULL;
287066494Sgshapiro			else if (strcmp(pw->pw_dir, "/") == 0)
287138032Speter				a->q_home = "";
287238032Speter			else
287338032Speter				a->q_home = newstr(pw->pw_dir);
287438032Speter			a->q_uid = pw->pw_uid;
287538032Speter			a->q_gid = pw->pw_gid;
287638032Speter			a->q_flags |= QGOODUID;
287738032Speter		}
287838032Speter	}
287938032Speter
288064562Sgshapiro	a->q_flags |= QPRIMARY;		/* flag as a "ctladdr" */
288138032Speter	a->q_mailer = LocalMailer;
288238032Speter	if (p == NULL)
288364562Sgshapiro		a->q_paddr = newstr(a->q_user);
288438032Speter	else
288538032Speter		a->q_paddr = newstr(p);
288638032Speter	return a;
288738032Speter}
288838032Speter/*
288938032Speter**  LOSEQFILE -- save the qf as Qf and try to let someone know
289038032Speter**
289138032Speter**	Parameters:
289238032Speter**		e -- the envelope (e->e_id will be used).
289338032Speter**		why -- reported to whomever can hear.
289438032Speter**
289538032Speter**	Returns:
289638032Speter**		none.
289738032Speter*/
289838032Speter
289938032Spetervoid
290038032Speterloseqfile(e, why)
290138032Speter	register ENVELOPE *e;
290238032Speter	char *why;
290338032Speter{
290438032Speter	char *p;
290564562Sgshapiro	char buf[MAXPATHLEN];
290638032Speter
290738032Speter	if (e == NULL || e->e_id == NULL)
290838032Speter		return;
290938032Speter	p = queuename(e, 'q');
291064562Sgshapiro	if (strlen(p) >= (SIZE_T) sizeof buf)
291138032Speter		return;
291264562Sgshapiro	(void) strlcpy(buf, p, sizeof buf);
291364562Sgshapiro	p = queuename(e, LOSEQF_LETTER);
291438032Speter	if (rename(buf, p) < 0)
291538032Speter		syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid());
291638032Speter	else if (LogLevel > 0)
291738032Speter		sm_syslog(LOG_ALERT, e->e_id,
291864562Sgshapiro			  "Losing %s: %s", buf, why);
291938032Speter}
292064562Sgshapiro/*
292164562Sgshapiro**  QID_PRINTNAME -- create externally printable version of queue id
292264562Sgshapiro**
292364562Sgshapiro**	Parameters:
292464562Sgshapiro**		e -- the envelope.
292564562Sgshapiro**
292664562Sgshapiro**	Returns:
292764562Sgshapiro**		a printable version
292864562Sgshapiro*/
292964562Sgshapiro
293064562Sgshapirochar *
293164562Sgshapiroqid_printname(e)
293264562Sgshapiro	ENVELOPE *e;
293364562Sgshapiro{
293464562Sgshapiro	char *id;
293564562Sgshapiro	static char idbuf[MAXQFNAME + 34];
293664562Sgshapiro
293764562Sgshapiro	if (e == NULL)
293864562Sgshapiro		return "";
293964562Sgshapiro
294064562Sgshapiro	if (e->e_id == NULL)
294164562Sgshapiro		id = "";
294264562Sgshapiro	else
294364562Sgshapiro		id = e->e_id;
294464562Sgshapiro
294564562Sgshapiro	if (e->e_queuedir == NOQDIR)
294664562Sgshapiro		return id;
294764562Sgshapiro
294864562Sgshapiro	(void) snprintf(idbuf, sizeof idbuf, "%.32s/%s",
294964562Sgshapiro			QPaths[e->e_queuedir].qp_name, id);
295064562Sgshapiro	return idbuf;
295164562Sgshapiro}
295264562Sgshapiro/*
295364562Sgshapiro**  QID_PRINTQUEUE -- create full version of queue directory for df files
295464562Sgshapiro**
295564562Sgshapiro**	Parameters:
295664562Sgshapiro**		queuedir -- the short version of the queue directory
295764562Sgshapiro**
295864562Sgshapiro**	Returns:
295964562Sgshapiro**		the full pathname to the queue (static)
296064562Sgshapiro*/
296164562Sgshapiro
296264562Sgshapirochar *
296364562Sgshapiroqid_printqueue(queuedir)
296464562Sgshapiro	int queuedir;
296564562Sgshapiro{
296664562Sgshapiro	char *subdir;
296764562Sgshapiro	static char dir[MAXPATHLEN];
296864562Sgshapiro
296964562Sgshapiro	if (queuedir == NOQDIR)
297064562Sgshapiro		return QueueDir;
297164562Sgshapiro
297264562Sgshapiro	if (strcmp(QPaths[queuedir].qp_name, ".") == 0)
297364562Sgshapiro		subdir = NULL;
297464562Sgshapiro	else
297564562Sgshapiro		subdir = QPaths[queuedir].qp_name;
297664562Sgshapiro
297764562Sgshapiro	(void) snprintf(dir, sizeof dir, "%s%s%s%s", QueueDir,
297864562Sgshapiro			subdir == NULL ? "" : "/",
297964562Sgshapiro			subdir == NULL ? "" : subdir,
298064562Sgshapiro			(bitset(QP_SUBDF, QPaths[queuedir].qp_subdirs) ? "/df" : ""));
298164562Sgshapiro	return dir;
298264562Sgshapiro}
298364562Sgshapiro/*
298464562Sgshapiro**  SETNEWQUEUE -- Sets a new queue directory
298564562Sgshapiro**
298664562Sgshapiro**	Assign a queue directory to an envelope and store the directory
298764562Sgshapiro**	in e->e_queuedir.  The queue is chosen at random.
298864562Sgshapiro**
298964562Sgshapiro**	This routine may be improved in the future to allow for more
299064562Sgshapiro**	elaborate queueing schemes.  Suggestions and code contributions
299164562Sgshapiro**	are welcome.
299264562Sgshapiro**
299364562Sgshapiro**	Parameters:
299464562Sgshapiro**		e -- envelope to assign a queue for.
299564562Sgshapiro**
299664562Sgshapiro**	Returns:
299764562Sgshapiro**		none.
299864562Sgshapiro*/
299964562Sgshapiro
300064562Sgshapirovoid
300164562Sgshapirosetnewqueue(e)
300264562Sgshapiro	ENVELOPE *e;
300364562Sgshapiro{
300464562Sgshapiro	int idx;
300564562Sgshapiro
300664562Sgshapiro	if (tTd(41, 20))
300764562Sgshapiro		dprintf("setnewqueue: called\n");
300864562Sgshapiro
300964562Sgshapiro	if (e->e_queuedir != NOQDIR)
301064562Sgshapiro	{
301164562Sgshapiro		if (tTd(41, 20))
301264562Sgshapiro			dprintf("setnewqueue: e_queuedir already assigned (%s)\n",
301364562Sgshapiro				qid_printqueue(e->e_queuedir));
301464562Sgshapiro		return;
301564562Sgshapiro	}
301664562Sgshapiro
301764562Sgshapiro	if (NumQueues == 1)
301864562Sgshapiro		idx = 0;
301964562Sgshapiro	else
302064562Sgshapiro	{
302164562Sgshapiro#if RANDOMSHIFT
302264562Sgshapiro		/* lower bits are not random "enough", select others */
302364562Sgshapiro		idx = (get_random() >> RANDOMSHIFT) % NumQueues;
302464562Sgshapiro#else /* RANDOMSHIFT */
302564562Sgshapiro		idx = get_random() % NumQueues;
302664562Sgshapiro#endif /* RANDOMSHIFT */
302764562Sgshapiro		if (tTd(41, 15))
302864562Sgshapiro			dprintf("setnewqueue: get_random() %% %d = %d\n",
302964562Sgshapiro				NumQueues, idx);
303064562Sgshapiro	}
303164562Sgshapiro
303264562Sgshapiro	e->e_queuedir = idx;
303364562Sgshapiro	if (tTd(41, 3))
303464562Sgshapiro		dprintf("setnewqueue: Assigned queue directory %s\n",
303564562Sgshapiro			qid_printqueue(e->e_queuedir));
303664562Sgshapiro}
303764562Sgshapiro
303864562Sgshapiro/*
303964562Sgshapiro**  CHKQDIR -- check a queue directory
304064562Sgshapiro**
304164562Sgshapiro**	Parameters:
304264562Sgshapiro**		name -- name of queue directory
304364562Sgshapiro**		sff -- flags for safefile()
304464562Sgshapiro**
304564562Sgshapiro**	Returns:
304664562Sgshapiro**		is it a queue directory?
304764562Sgshapiro*/
304864562Sgshapiro
304964562Sgshapirostatic bool
305064562Sgshapirochkqdir(name, sff)
305164562Sgshapiro	char *name;
305264562Sgshapiro	long sff;
305364562Sgshapiro{
305464562Sgshapiro	struct stat statb;
305564562Sgshapiro	int i;
305664562Sgshapiro
305766494Sgshapiro	/* skip over . and .. directories */
305866494Sgshapiro	if (name[0] == '.' &&
305966494Sgshapiro	    (name[1] == '\0' || (name[2] == '.' && name[3] == '\0')))
306066494Sgshapiro		return FALSE;
306164562Sgshapiro# if HASLSTAT
306264562Sgshapiro	if (lstat(name, &statb) < 0)
306364562Sgshapiro# else /* HASLSTAT */
306464562Sgshapiro	if (stat(name, &statb) < 0)
306564562Sgshapiro# endif /* HASLSTAT */
306664562Sgshapiro	{
306764562Sgshapiro		if (tTd(41, 2))
306864562Sgshapiro			dprintf("multiqueue_cache: stat(\"%s\"): %s\n",
306964562Sgshapiro				name, errstring(errno));
307064562Sgshapiro		return FALSE;
307164562Sgshapiro	}
307264562Sgshapiro# if HASLSTAT
307364562Sgshapiro	if (S_ISLNK(statb.st_mode))
307464562Sgshapiro	{
307564562Sgshapiro		/*
307664562Sgshapiro		**  For a symlink we need to make sure the
307764562Sgshapiro		**  target is a directory
307864562Sgshapiro		*/
307964562Sgshapiro		if (stat(name, &statb) < 0)
308064562Sgshapiro		{
308164562Sgshapiro			if (tTd(41, 2))
308264562Sgshapiro				dprintf("multiqueue_cache: stat(\"%s\"): %s\n",
308364562Sgshapiro					name, errstring(errno));
308464562Sgshapiro			return FALSE;
308564562Sgshapiro		}
308664562Sgshapiro	}
308764562Sgshapiro# endif /* HASLSTAT */
308864562Sgshapiro
308964562Sgshapiro	if (!S_ISDIR(statb.st_mode))
309064562Sgshapiro	{
309164562Sgshapiro		if (tTd(41, 2))
309264562Sgshapiro			dprintf("multiqueue_cache: \"%s\": Not a directory\n",
309364562Sgshapiro				name);
309464562Sgshapiro		return FALSE;
309564562Sgshapiro	}
309664562Sgshapiro
309764562Sgshapiro	/* Print a warning if unsafe (but still use it) */
309864562Sgshapiro	i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0);
309964562Sgshapiro	if (i != 0 && tTd(41, 2))
310064562Sgshapiro		dprintf("multiqueue_cache: \"%s\": Not safe: %s\n",
310164562Sgshapiro			name, errstring(i));
310264562Sgshapiro	return TRUE;
310364562Sgshapiro}
310464562Sgshapiro
310564562Sgshapiro/*
310664562Sgshapiro**  MULTIQUEUE_CACHE -- cache a list of paths to queues.
310764562Sgshapiro**
310864562Sgshapiro**	Each potential queue is checked as the cache is built.
310964562Sgshapiro**	Thereafter, each is blindly trusted.
311064562Sgshapiro**	Note that we can be called again after a timeout to rebuild
311164562Sgshapiro**	(although code for that is not ready yet).
311264562Sgshapiro**
311364562Sgshapiro**	Parameters:
311464562Sgshapiro**		none
311564562Sgshapiro**
311664562Sgshapiro**	Returns:
311764562Sgshapiro**		none
311864562Sgshapiro*/
311964562Sgshapiro
312064562Sgshapirovoid
312164562Sgshapiromultiqueue_cache()
312264562Sgshapiro{
312364562Sgshapiro	register DIR *dp;
312464562Sgshapiro	register struct dirent *d;
312564562Sgshapiro	char *cp;
312664562Sgshapiro	int i, len;
312764562Sgshapiro	int slotsleft = 0;
312864562Sgshapiro	long sff = SFF_ANYFILE;
312964562Sgshapiro	char qpath[MAXPATHLEN];
313064562Sgshapiro	char subdir[MAXPATHLEN];
313164562Sgshapiro
313264562Sgshapiro	if (tTd(41, 20))
313364562Sgshapiro		dprintf("multiqueue_cache: called\n");
313464562Sgshapiro
313564562Sgshapiro	if (NumQueues != 0 && QPaths != NULL)
313664562Sgshapiro	{
313764562Sgshapiro		for (i = 0; i < NumQueues; i++)
313864562Sgshapiro		{
313964562Sgshapiro			if (QPaths[i].qp_name != NULL)
314064562Sgshapiro				(void) free(QPaths[i].qp_name);
314164562Sgshapiro		}
314264562Sgshapiro		(void) free((char *)QPaths);
314364562Sgshapiro		QPaths = NULL;
314464562Sgshapiro		NumQueues = 0;
314564562Sgshapiro	}
314664562Sgshapiro
314764562Sgshapiro	/* If running as root, allow safedirpath() checks to use privs */
314864562Sgshapiro	if (RunAsUid == 0)
314964562Sgshapiro		sff |= SFF_ROOTOK;
315064562Sgshapiro
315164562Sgshapiro	(void) snprintf(qpath, sizeof qpath, "%s", QueueDir);
315264562Sgshapiro	len = strlen(qpath) - 1;
315364562Sgshapiro	cp = &qpath[len];
315464562Sgshapiro	if (*cp == '*')
315564562Sgshapiro	{
315664562Sgshapiro		*cp = '\0';
315764562Sgshapiro		if ((cp = strrchr(qpath, '/')) == NULL)
315864562Sgshapiro		{
315964562Sgshapiro			syserr("QueueDirectory: can not wildcard relative path");
316064562Sgshapiro			if (tTd(41, 2))
316164562Sgshapiro				dprintf("multiqueue_cache: \"%s\": Can not wildcard relative path.\n",
316271345Sgshapiro					qpath);
316364562Sgshapiro			ExitStat = EX_CONFIG;
316464562Sgshapiro			return;
316564562Sgshapiro		}
316664562Sgshapiro		if (cp == qpath)
316764562Sgshapiro		{
316864562Sgshapiro			/*
316964562Sgshapiro			**  Special case of top level wildcard, like /foo*
317064562Sgshapiro			*/
317164562Sgshapiro
317264562Sgshapiro			(void) snprintf(qpath + 1, sizeof qpath - 1,
317364562Sgshapiro					"%s", qpath);
317464562Sgshapiro			++cp;
317564562Sgshapiro		}
317664562Sgshapiro		*(cp++) = '\0';
317764562Sgshapiro		len = strlen(cp);
317864562Sgshapiro
317964562Sgshapiro		if (tTd(41, 2))
318064562Sgshapiro			dprintf("multiqueue_cache: prefix=\"%s\"\n", cp);
318164562Sgshapiro
318264562Sgshapiro		QueueDir = newstr(qpath);
318364562Sgshapiro
318464562Sgshapiro		/*
318564562Sgshapiro		**  XXX Should probably wrap this whole loop in a timeout
318664562Sgshapiro		**  in case some wag decides to NFS mount the queues.
318764562Sgshapiro		*/
318864562Sgshapiro
318964562Sgshapiro		/* test path to get warning messages */
319064562Sgshapiro		i= safedirpath(QueueDir, RunAsUid, RunAsGid, NULL, sff, 0, 0);
319164562Sgshapiro		if (i != 0 && tTd(41, 2))
319264562Sgshapiro			dprintf("multiqueue_cache: \"%s\": Not safe: %s\n",
319364562Sgshapiro				QueueDir, errstring(i));
319464562Sgshapiro
319564562Sgshapiro		if (chdir(QueueDir) < 0)
319664562Sgshapiro		{
319764562Sgshapiro			syserr("can not chdir(%s)", QueueDir);
319864562Sgshapiro			if (tTd(41, 2))
319964562Sgshapiro				dprintf("multiqueue_cache: \"%s\": %s\n",
320064562Sgshapiro					qpath, errstring(errno));
320164562Sgshapiro			ExitStat = EX_CONFIG;
320264562Sgshapiro			return;
320364562Sgshapiro		}
320464562Sgshapiro
320564562Sgshapiro		if ((dp = opendir(".")) == NULL)
320664562Sgshapiro		{
320764562Sgshapiro			syserr("can not opendir(%s)", QueueDir);
320864562Sgshapiro			if (tTd(41, 2))
320964562Sgshapiro				dprintf("multiqueue_cache: opendir(\"%s\"): %s\n",
321064562Sgshapiro					QueueDir, errstring(errno));
321164562Sgshapiro			ExitStat = EX_CONFIG;
321264562Sgshapiro			return;
321364562Sgshapiro		}
321464562Sgshapiro		while ((d = readdir(dp)) != NULL)
321564562Sgshapiro		{
321664562Sgshapiro			if (strncmp(d->d_name, cp, len) != 0)
321764562Sgshapiro			{
321864562Sgshapiro				if (tTd(41, 5))
321964562Sgshapiro					dprintf("multiqueue_cache: \"%s\", skipped\n",
322064562Sgshapiro						d->d_name);
322164562Sgshapiro				continue;
322264562Sgshapiro			}
322364562Sgshapiro			if (!chkqdir(d->d_name, sff))
322464562Sgshapiro				continue;
322564562Sgshapiro
322664562Sgshapiro			if (QPaths == NULL)
322764562Sgshapiro			{
322864562Sgshapiro				slotsleft = 20;
322964562Sgshapiro				QPaths = (QPATHS *)xalloc((sizeof *QPaths) *
323064562Sgshapiro							  slotsleft);
323164562Sgshapiro				NumQueues = 0;
323264562Sgshapiro			}
323364562Sgshapiro			else if (slotsleft < 1)
323464562Sgshapiro			{
323564562Sgshapiro				QPaths = (QPATHS *)realloc((char *)QPaths,
323664562Sgshapiro							  (sizeof *QPaths) *
323764562Sgshapiro							  (NumQueues + 10));
323864562Sgshapiro				if (QPaths == NULL)
323964562Sgshapiro				{
324064562Sgshapiro					(void) closedir(dp);
324164562Sgshapiro					return;
324264562Sgshapiro				}
324364562Sgshapiro				slotsleft += 10;
324464562Sgshapiro			}
324564562Sgshapiro
324664562Sgshapiro			/* check subdirs */
324764562Sgshapiro			QPaths[NumQueues].qp_subdirs = QP_NOSUB;
324864562Sgshapiro			(void) snprintf(subdir, sizeof subdir, "%s/%s/%s",
324964562Sgshapiro					qpath, d->d_name, "qf");
325064562Sgshapiro			if (chkqdir(subdir, sff))
325164562Sgshapiro				QPaths[NumQueues].qp_subdirs |= QP_SUBQF;
325264562Sgshapiro
325364562Sgshapiro			(void) snprintf(subdir, sizeof subdir, "%s/%s/%s",
325464562Sgshapiro					qpath, d->d_name, "df");
325564562Sgshapiro			if (chkqdir(subdir, sff))
325664562Sgshapiro				QPaths[NumQueues].qp_subdirs |= QP_SUBDF;
325764562Sgshapiro
325864562Sgshapiro			(void) snprintf(subdir, sizeof subdir, "%s/%s/%s",
325964562Sgshapiro					qpath, d->d_name, "xf");
326064562Sgshapiro			if (chkqdir(subdir, sff))
326164562Sgshapiro				QPaths[NumQueues].qp_subdirs |= QP_SUBXF;
326264562Sgshapiro
326364562Sgshapiro			/* assert(strlen(d->d_name) < MAXPATHLEN - 14) */
326464562Sgshapiro			/* maybe even - 17 (subdirs) */
326564562Sgshapiro			QPaths[NumQueues].qp_name = newstr(d->d_name);
326664562Sgshapiro			if (tTd(41, 2))
326764562Sgshapiro				dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n",
326864562Sgshapiro					NumQueues, d->d_name,
326964562Sgshapiro					QPaths[NumQueues].qp_subdirs);
327064562Sgshapiro			NumQueues++;
327164562Sgshapiro			slotsleft--;
327264562Sgshapiro		}
327364562Sgshapiro		(void) closedir(dp);
327464562Sgshapiro	}
327564562Sgshapiro	if (NumQueues == 0)
327664562Sgshapiro	{
327764562Sgshapiro		if (*cp != '*' && tTd(41, 2))
327864562Sgshapiro			dprintf("multiqueue_cache: \"%s\": No wildcard suffix character\n",
327964562Sgshapiro				QueueDir);
328064562Sgshapiro		QPaths = (QPATHS *)xalloc(sizeof *QPaths);
328164562Sgshapiro		QPaths[0].qp_name = newstr(".");
328264562Sgshapiro		QPaths[0].qp_subdirs = QP_NOSUB;
328364562Sgshapiro		NumQueues = 1;
328464562Sgshapiro
328564562Sgshapiro		/* test path to get warning messages */
328664562Sgshapiro		(void) safedirpath(QueueDir, RunAsUid, RunAsGid,
328764562Sgshapiro				   NULL, sff, 0, 0);
328864562Sgshapiro		if (chdir(QueueDir) < 0)
328964562Sgshapiro		{
329064562Sgshapiro			syserr("can not chdir(%s)", QueueDir);
329164562Sgshapiro			if (tTd(41, 2))
329264562Sgshapiro				dprintf("multiqueue_cache: \"%s\": %s\n",
329364562Sgshapiro					QueueDir, errstring(errno));
329464562Sgshapiro			ExitStat = EX_CONFIG;
329564562Sgshapiro		}
329664562Sgshapiro
329764562Sgshapiro		/* check subdirs */
329864562Sgshapiro		(void) snprintf(subdir, sizeof subdir, "%s/qf", QueueDir);
329964562Sgshapiro		if (chkqdir(subdir, sff))
330064562Sgshapiro			QPaths[0].qp_subdirs |= QP_SUBQF;
330164562Sgshapiro
330264562Sgshapiro		(void) snprintf(subdir, sizeof subdir, "%s/df",	QueueDir);
330364562Sgshapiro		if (chkqdir(subdir, sff))
330464562Sgshapiro			QPaths[0].qp_subdirs |= QP_SUBDF;
330564562Sgshapiro
330664562Sgshapiro		(void) snprintf(subdir, sizeof subdir, "%s/xf", QueueDir);
330764562Sgshapiro		if (chkqdir(subdir, sff))
330864562Sgshapiro			QPaths[0].qp_subdirs |= QP_SUBXF;
330964562Sgshapiro	}
331064562Sgshapiro}
331164562Sgshapiro
331264562Sgshapiro# if 0
331364562Sgshapiro/*
331464562Sgshapiro**  HASHFQN -- calculate a hash value for a fully qualified host name
331564562Sgshapiro**
331664562Sgshapiro**	Arguments:
331764562Sgshapiro**		fqn -- an all lower-case host.domain string
331864562Sgshapiro**		buckets -- the number of buckets (queue directories)
331964562Sgshapiro**
332064562Sgshapiro**	Returns:
332164562Sgshapiro**		a bucket number (signed integer)
332264562Sgshapiro**		-1 on error
332364562Sgshapiro**
332464562Sgshapiro**	Contributed by Exactis.com, Inc.
332564562Sgshapiro*/
332664562Sgshapiro
332764562Sgshapiroint
332864562Sgshapirohashfqn(fqn, buckets)
332964562Sgshapiro	register char *fqn;
333064562Sgshapiro	int buckets;
333164562Sgshapiro{
333264562Sgshapiro	register char *p;
333364562Sgshapiro	register int h = 0, hash, cnt;
333464562Sgshapiro#  define WATERINC (1000)
333564562Sgshapiro
333664562Sgshapiro	if (fqn == NULL)
333764562Sgshapiro		return -1;
333864562Sgshapiro
333964562Sgshapiro	/*
334064562Sgshapiro	**  A variation on the gdb hash
334164562Sgshapiro	**  This is the best as of Feb 19, 1996 --bcx
334264562Sgshapiro	*/
334364562Sgshapiro
334464562Sgshapiro	p = fqn;
334564562Sgshapiro	h = 0x238F13AF * strlen(p);
334664562Sgshapiro	for (cnt = 0; *p != 0; ++p, cnt++)
334764562Sgshapiro	{
334864562Sgshapiro		h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF;
334964562Sgshapiro	}
335064562Sgshapiro	h = (1103515243 * h + 12345) & 0x7FFFFFFF;
335164562Sgshapiro	if (buckets < 2)
335264562Sgshapiro		hash = 0;
335364562Sgshapiro	else
335464562Sgshapiro		hash = (h % buckets);
335564562Sgshapiro
335664562Sgshapiro	return hash;
335764562Sgshapiro}
335864562Sgshapiro# endif /* 0 */
335964562Sgshapiro
336064562Sgshapiro# if _FFR_QUEUEDELAY
336164562Sgshapiro/*
336264562Sgshapiro**  QUEUEDELAY -- compute queue delay time
336364562Sgshapiro**
336464562Sgshapiro**	Parameters:
336564562Sgshapiro**		e -- the envelope to queue up.
336664562Sgshapiro**
336764562Sgshapiro**	Returns:
336864562Sgshapiro**		queue delay time
336964562Sgshapiro**
337064562Sgshapiro**	Side Effects:
337164562Sgshapiro**		may change e_queuedelay
337264562Sgshapiro*/
337364562Sgshapiro
337464562Sgshapirostatic time_t
337564562Sgshapiroqueuedelay(e)
337664562Sgshapiro	ENVELOPE *e;
337764562Sgshapiro{
337864562Sgshapiro	time_t qd;
337964562Sgshapiro
338064562Sgshapiro	if (e->e_queuealg == QD_EXP)
338164562Sgshapiro	{
338264562Sgshapiro		if (e->e_queuedelay == 0)
338364562Sgshapiro			e->e_queuedelay = QueueInitDelay;
338464562Sgshapiro		else
338564562Sgshapiro		{
338664562Sgshapiro			e->e_queuedelay *= 2;
338764562Sgshapiro			if (e->e_queuedelay > QueueMaxDelay)
338864562Sgshapiro				e->e_queuedelay = QueueMaxDelay;
338964562Sgshapiro		}
339064562Sgshapiro		qd = e->e_queuedelay;
339164562Sgshapiro	}
339264562Sgshapiro	else
339364562Sgshapiro		qd = MinQueueAge;
339464562Sgshapiro	return qd;
339564562Sgshapiro}
339664562Sgshapiro# endif /* _FFR_QUEUEDELAY */
339764562Sgshapiro#endif /* QUEUE */
3398