queue.c revision 38032
138032Speter/*
238032Speter * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
338032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
438032Speter * Copyright (c) 1988, 1993
538032Speter *	The Regents of the University of California.  All rights reserved.
638032Speter *
738032Speter * By using this file, you agree to the terms and conditions set
838032Speter * forth in the LICENSE file which can be found at the top level of
938032Speter * the sendmail distribution.
1038032Speter *
1138032Speter */
1238032Speter
1338032Speter# include "sendmail.h"
1438032Speter
1538032Speter#ifndef lint
1638032Speter#if QUEUE
1738032Speterstatic char sccsid[] = "@(#)queue.c	8.202 (Berkeley) 6/15/98 (with queueing)";
1838032Speter#else
1938032Speterstatic char sccsid[] = "@(#)queue.c	8.202 (Berkeley) 6/15/98 (without queueing)";
2038032Speter#endif
2138032Speter#endif /* not lint */
2238032Speter
2338032Speter# include <errno.h>
2438032Speter# include <dirent.h>
2538032Speter
2638032Speter# if QUEUE
2738032Speter
2838032Speter/*
2938032Speter**  Work queue.
3038032Speter*/
3138032Speter
3238032Speterstruct work
3338032Speter{
3438032Speter	char		*w_name;	/* name of control file */
3538032Speter	char		*w_host;	/* name of recipient host */
3638032Speter	bool		w_lock;		/* is message locked? */
3738032Speter	bool		w_tooyoung;	/* is it too young to run? */
3838032Speter	long		w_pri;		/* priority of message, see below */
3938032Speter	time_t		w_ctime;	/* creation time of message */
4038032Speter	struct work	*w_next;	/* next in queue */
4138032Speter};
4238032Speter
4338032Spetertypedef struct work	WORK;
4438032Speter
4538032SpeterWORK	*WorkQ;			/* queue of things to be done */
4638032Speter
4738032Speter#define QF_VERSION	2	/* version number of this queue format */
4838032Speter
4938032Speterextern int orderq __P((bool));
5038032Speter/*
5138032Speter**  QUEUEUP -- queue a message up for future transmission.
5238032Speter**
5338032Speter**	Parameters:
5438032Speter**		e -- the envelope to queue up.
5538032Speter**		announce -- if TRUE, tell when you are queueing up.
5638032Speter**
5738032Speter**	Returns:
5838032Speter**		none.
5938032Speter**
6038032Speter**	Side Effects:
6138032Speter**		The current request are saved in a control file.
6238032Speter**		The queue file is left locked.
6338032Speter*/
6438032Speter
6538032Spetervoid
6638032Speterqueueup(e, announce)
6738032Speter	register ENVELOPE *e;
6838032Speter	bool announce;
6938032Speter{
7038032Speter	char *qf;
7138032Speter	register FILE *tfp;
7238032Speter	register HDR *h;
7338032Speter	register ADDRESS *q;
7438032Speter	int fd;
7538032Speter	int i;
7638032Speter	bool newid;
7738032Speter	register char *p;
7838032Speter	MAILER nullmailer;
7938032Speter	MCI mcibuf;
8038032Speter	char tf[MAXQFNAME];
8138032Speter	char buf[MAXLINE];
8238032Speter	extern void printctladdr __P((ADDRESS *, FILE *));
8338032Speter
8438032Speter	/*
8538032Speter	**  Create control file.
8638032Speter	*/
8738032Speter
8838032Speter	newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);
8938032Speter
9038032Speter	/* if newid, queuename will create a locked qf file in e->lockfp */
9138032Speter	strcpy(tf, queuename(e, 't'));
9238032Speter	tfp = e->e_lockfp;
9338032Speter	if (tfp == NULL)
9438032Speter		newid = FALSE;
9538032Speter
9638032Speter	/* if newid, just write the qf file directly (instead of tf file) */
9738032Speter	if (!newid)
9838032Speter	{
9938032Speter		/* get a locked tf file */
10038032Speter		for (i = 0; i < 128; i++)
10138032Speter		{
10238032Speter			fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode);
10338032Speter			if (fd < 0)
10438032Speter			{
10538032Speter				if (errno != EEXIST)
10638032Speter					break;
10738032Speter				if (LogLevel > 0 && (i % 32) == 0)
10838032Speter					sm_syslog(LOG_ALERT, e->e_id,
10938032Speter						"queueup: cannot create %s, uid=%d: %s",
11038032Speter						tf, geteuid(), errstring(errno));
11138032Speter			}
11238032Speter			else
11338032Speter			{
11438032Speter				if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB))
11538032Speter					break;
11638032Speter				else if (LogLevel > 0 && (i % 32) == 0)
11738032Speter					sm_syslog(LOG_ALERT, e->e_id,
11838032Speter						"queueup: cannot lock %s: %s",
11938032Speter						tf, errstring(errno));
12038032Speter				close(fd);
12138032Speter			}
12238032Speter
12338032Speter			if ((i % 32) == 31)
12438032Speter			{
12538032Speter				/* save the old temp file away */
12638032Speter				(void) rename(tf, queuename(e, 'T'));
12738032Speter			}
12838032Speter			else
12938032Speter				sleep(i % 32);
13038032Speter		}
13138032Speter		if (fd < 0 || (tfp = fdopen(fd, "w")) == NULL)
13238032Speter		{
13338032Speter			printopenfds(TRUE);
13438032Speter			syserr("!queueup: cannot create queue temp file %s, uid=%d",
13538032Speter				tf, geteuid());
13638032Speter		}
13738032Speter	}
13838032Speter
13938032Speter	if (tTd(40, 1))
14038032Speter		printf("\n>>>>> queueing %s%s >>>>>\n", e->e_id,
14138032Speter			newid ? " (new id)" : "");
14238032Speter	if (tTd(40, 3))
14338032Speter	{
14438032Speter		extern void printenvflags __P((ENVELOPE *));
14538032Speter
14638032Speter		printf("  e_flags=");
14738032Speter		printenvflags(e);
14838032Speter	}
14938032Speter	if (tTd(40, 32))
15038032Speter	{
15138032Speter		printf("  sendq=");
15238032Speter		printaddr(e->e_sendqueue, TRUE);
15338032Speter	}
15438032Speter	if (tTd(40, 9))
15538032Speter	{
15638032Speter		printf("  tfp=");
15738032Speter		dumpfd(fileno(tfp), TRUE, FALSE);
15838032Speter		printf("  lockfp=");
15938032Speter		if (e->e_lockfp == NULL)
16038032Speter			printf("NULL\n");
16138032Speter		else
16238032Speter			dumpfd(fileno(e->e_lockfp), TRUE, FALSE);
16338032Speter	}
16438032Speter
16538032Speter	/*
16638032Speter	**  If there is no data file yet, create one.
16738032Speter	*/
16838032Speter
16938032Speter	if (!bitset(EF_HAS_DF, e->e_flags))
17038032Speter	{
17138032Speter		register FILE *dfp = NULL;
17238032Speter		char dfname[MAXQFNAME];
17338032Speter		struct stat stbuf;
17438032Speter
17538032Speter		strcpy(dfname, queuename(e, 'd'));
17638032Speter		fd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode);
17738032Speter		if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL)
17838032Speter			syserr("!queueup: cannot create data temp file %s, uid=%d",
17938032Speter				dfname, geteuid());
18038032Speter		if (fstat(fd, &stbuf) < 0)
18138032Speter			e->e_dfino = -1;
18238032Speter		else
18338032Speter		{
18438032Speter			e->e_dfdev = stbuf.st_dev;
18538032Speter			e->e_dfino = stbuf.st_ino;
18638032Speter		}
18738032Speter		e->e_flags |= EF_HAS_DF;
18838032Speter		bzero(&mcibuf, sizeof mcibuf);
18938032Speter		mcibuf.mci_out = dfp;
19038032Speter		mcibuf.mci_mailer = FileMailer;
19138032Speter		(*e->e_putbody)(&mcibuf, e, NULL);
19238032Speter		(void) xfclose(dfp, "queueup dfp", e->e_id);
19338032Speter		e->e_putbody = putbody;
19438032Speter	}
19538032Speter
19638032Speter	/*
19738032Speter	**  Output future work requests.
19838032Speter	**	Priority and creation time should be first, since
19938032Speter	**	they are required by orderq.
20038032Speter	*/
20138032Speter
20238032Speter	/* output queue version number (must be first!) */
20338032Speter	fprintf(tfp, "V%d\n", QF_VERSION);
20438032Speter
20538032Speter	/* output creation time */
20638032Speter	fprintf(tfp, "T%ld\n", (long) e->e_ctime);
20738032Speter
20838032Speter	/* output last delivery time */
20938032Speter	fprintf(tfp, "K%ld\n", (long) e->e_dtime);
21038032Speter
21138032Speter	/* output number of delivery attempts */
21238032Speter	fprintf(tfp, "N%d\n", e->e_ntries);
21338032Speter
21438032Speter	/* output message priority */
21538032Speter	fprintf(tfp, "P%ld\n", e->e_msgpriority);
21638032Speter
21738032Speter	/* output inode number of data file */
21838032Speter	/* XXX should probably include device major/minor too */
21938032Speter	if (e->e_dfino != -1)
22038032Speter	{
22138032Speter		if (sizeof e->e_dfino > sizeof(long))
22238032Speter			fprintf(tfp, "I%d/%d/%s\n",
22338032Speter				major(e->e_dfdev), minor(e->e_dfdev),
22438032Speter				quad_to_string(e->e_dfino));
22538032Speter		else
22638032Speter			fprintf(tfp, "I%d/%d/%lu\n",
22738032Speter				major(e->e_dfdev), minor(e->e_dfdev),
22838032Speter				(unsigned long) e->e_dfino);
22938032Speter	}
23038032Speter
23138032Speter	/* output body type */
23238032Speter	if (e->e_bodytype != NULL)
23338032Speter		fprintf(tfp, "B%s\n", denlstring(e->e_bodytype, TRUE, FALSE));
23438032Speter
23538032Speter#if _FFR_SAVE_CHARSET
23638032Speter	if (e->e_charset != NULL)
23738032Speter		fprintf(tfp, "X%s\n", denlstring(e->e_charset, TRUE, FALSE));
23838032Speter#endif
23938032Speter
24038032Speter	/* message from envelope, if it exists */
24138032Speter	if (e->e_message != NULL)
24238032Speter		fprintf(tfp, "M%s\n", denlstring(e->e_message, TRUE, FALSE));
24338032Speter
24438032Speter	/* send various flag bits through */
24538032Speter	p = buf;
24638032Speter	if (bitset(EF_WARNING, e->e_flags))
24738032Speter		*p++ = 'w';
24838032Speter	if (bitset(EF_RESPONSE, e->e_flags))
24938032Speter		*p++ = 'r';
25038032Speter	if (bitset(EF_HAS8BIT, e->e_flags))
25138032Speter		*p++ = '8';
25238032Speter	if (bitset(EF_DELETE_BCC, e->e_flags))
25338032Speter		*p++ = 'b';
25438032Speter	if (bitset(EF_RET_PARAM, e->e_flags))
25538032Speter		*p++ = 'd';
25638032Speter	if (bitset(EF_NO_BODY_RETN, e->e_flags))
25738032Speter		*p++ = 'n';
25838032Speter	*p++ = '\0';
25938032Speter	if (buf[0] != '\0')
26038032Speter		fprintf(tfp, "F%s\n", buf);
26138032Speter
26238032Speter	/* $r and $s and $_ macro values */
26338032Speter	if ((p = macvalue('r', e)) != NULL)
26438032Speter		fprintf(tfp, "$r%s\n", denlstring(p, TRUE, FALSE));
26538032Speter	if ((p = macvalue('s', e)) != NULL)
26638032Speter		fprintf(tfp, "$s%s\n", denlstring(p, TRUE, FALSE));
26738032Speter	if ((p = macvalue('_', e)) != NULL)
26838032Speter		fprintf(tfp, "$_%s\n", denlstring(p, TRUE, FALSE));
26938032Speter
27038032Speter	/* output name of sender */
27138032Speter	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
27238032Speter		p = e->e_sender;
27338032Speter	else
27438032Speter		p = e->e_from.q_paddr;
27538032Speter	fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE));
27638032Speter
27738032Speter	/* output ESMTP-supplied "original" information */
27838032Speter	if (e->e_envid != NULL)
27938032Speter		fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE));
28038032Speter
28138032Speter	/* output list of recipient addresses */
28238032Speter	printctladdr(NULL, NULL);
28338032Speter	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
28438032Speter	{
28538032Speter		if (bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags))
28638032Speter		{
28738032Speter#if XDEBUG
28838032Speter			if (bitset(QQUEUEUP, q->q_flags))
28938032Speter				sm_syslog(LOG_DEBUG, e->e_id,
29038032Speter					"dropenvelope: q_flags = %x, paddr = %s",
29138032Speter					q->q_flags, q->q_paddr);
29238032Speter#endif
29338032Speter			continue;
29438032Speter		}
29538032Speter		printctladdr(q, tfp);
29638032Speter		if (q->q_orcpt != NULL)
29738032Speter			fprintf(tfp, "Q%s\n",
29838032Speter				denlstring(q->q_orcpt, TRUE, FALSE));
29938032Speter		putc('R', tfp);
30038032Speter		if (bitset(QPRIMARY, q->q_flags))
30138032Speter			putc('P', tfp);
30238032Speter		if (bitset(QHASNOTIFY, q->q_flags))
30338032Speter			putc('N', tfp);
30438032Speter		if (bitset(QPINGONSUCCESS, q->q_flags))
30538032Speter			putc('S', tfp);
30638032Speter		if (bitset(QPINGONFAILURE, q->q_flags))
30738032Speter			putc('F', tfp);
30838032Speter		if (bitset(QPINGONDELAY, q->q_flags))
30938032Speter			putc('D', tfp);
31038032Speter		putc(':', tfp);
31138032Speter		fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE));
31238032Speter		if (announce)
31338032Speter		{
31438032Speter			e->e_to = q->q_paddr;
31538032Speter			message("queued");
31638032Speter			if (LogLevel > 8)
31738032Speter				logdelivery(q->q_mailer, NULL, "queued",
31838032Speter					    NULL, (time_t) 0, e);
31938032Speter			e->e_to = NULL;
32038032Speter		}
32138032Speter		if (tTd(40, 1))
32238032Speter		{
32338032Speter			printf("queueing ");
32438032Speter			printaddr(q, FALSE);
32538032Speter		}
32638032Speter	}
32738032Speter
32838032Speter	/*
32938032Speter	**  Output headers for this message.
33038032Speter	**	Expand macros completely here.  Queue run will deal with
33138032Speter	**	everything as absolute headers.
33238032Speter	**		All headers that must be relative to the recipient
33338032Speter	**		can be cracked later.
33438032Speter	**	We set up a "null mailer" -- i.e., a mailer that will have
33538032Speter	**	no effect on the addresses as they are output.
33638032Speter	*/
33738032Speter
33838032Speter	bzero((char *) &nullmailer, sizeof nullmailer);
33938032Speter	nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
34038032Speter			nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1;
34138032Speter	nullmailer.m_eol = "\n";
34238032Speter	bzero(&mcibuf, sizeof mcibuf);
34338032Speter	mcibuf.mci_mailer = &nullmailer;
34438032Speter	mcibuf.mci_out = tfp;
34538032Speter
34638032Speter	define('g', "\201f", e);
34738032Speter	for (h = e->e_header; h != NULL; h = h->h_link)
34838032Speter	{
34938032Speter		extern bool bitzerop __P((BITMAP));
35038032Speter
35138032Speter		/* don't output null headers */
35238032Speter		if (h->h_value == NULL || h->h_value[0] == '\0')
35338032Speter			continue;
35438032Speter
35538032Speter		/* don't output resent headers on non-resent messages */
35638032Speter		if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
35738032Speter			continue;
35838032Speter
35938032Speter		/* expand macros; if null, don't output header at all */
36038032Speter		if (bitset(H_DEFAULT, h->h_flags))
36138032Speter		{
36238032Speter			(void) expand(h->h_value, buf, sizeof buf, e);
36338032Speter			if (buf[0] == '\0')
36438032Speter				continue;
36538032Speter		}
36638032Speter
36738032Speter		/* output this header */
36838032Speter		fprintf(tfp, "H");
36938032Speter
37038032Speter		/* if conditional, output the set of conditions */
37138032Speter		if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags))
37238032Speter		{
37338032Speter			int j;
37438032Speter
37538032Speter			(void) putc('?', tfp);
37638032Speter			for (j = '\0'; j <= '\177'; j++)
37738032Speter				if (bitnset(j, h->h_mflags))
37838032Speter					(void) putc(j, tfp);
37938032Speter			(void) putc('?', tfp);
38038032Speter		}
38138032Speter
38238032Speter		/* output the header: expand macros, convert addresses */
38338032Speter		if (bitset(H_DEFAULT, h->h_flags))
38438032Speter		{
38538032Speter			fprintf(tfp, "%s: %s\n",
38638032Speter				h->h_field,
38738032Speter				denlstring(buf, FALSE, TRUE));
38838032Speter		}
38938032Speter		else if (bitset(H_FROM|H_RCPT, h->h_flags))
39038032Speter		{
39138032Speter			bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
39238032Speter			FILE *savetrace = TrafficLogFile;
39338032Speter
39438032Speter			TrafficLogFile = NULL;
39538032Speter
39638032Speter			if (bitset(H_FROM, h->h_flags))
39738032Speter				oldstyle = FALSE;
39838032Speter
39938032Speter			commaize(h, h->h_value, oldstyle, &mcibuf, e);
40038032Speter
40138032Speter			TrafficLogFile = savetrace;
40238032Speter		}
40338032Speter		else
40438032Speter		{
40538032Speter			fprintf(tfp, "%s: %s\n",
40638032Speter				h->h_field,
40738032Speter				denlstring(h->h_value, FALSE, TRUE));
40838032Speter		}
40938032Speter	}
41038032Speter
41138032Speter	/*
41238032Speter	**  Clean up.
41338032Speter	**
41438032Speter	**	Write a terminator record -- this is to prevent
41538032Speter	**	scurrilous crackers from appending any data.
41638032Speter	*/
41738032Speter
41838032Speter	fprintf(tfp, ".\n");
41938032Speter
42038032Speter	if (fflush(tfp) < 0 ||
42138032Speter	    (SuperSafe && fsync(fileno(tfp)) < 0) ||
42238032Speter	    ferror(tfp))
42338032Speter	{
42438032Speter		if (newid)
42538032Speter			syserr("!552 Error writing control file %s", tf);
42638032Speter		else
42738032Speter			syserr("!452 Error writing control file %s", tf);
42838032Speter	}
42938032Speter
43038032Speter	if (!newid)
43138032Speter	{
43238032Speter		/* rename (locked) tf to be (locked) qf */
43338032Speter		qf = queuename(e, 'q');
43438032Speter		if (rename(tf, qf) < 0)
43538032Speter			syserr("cannot rename(%s, %s), uid=%d",
43638032Speter				tf, qf, geteuid());
43738032Speter
43838032Speter		/* close and unlock old (locked) qf */
43938032Speter		if (e->e_lockfp != NULL)
44038032Speter			(void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id);
44138032Speter		e->e_lockfp = tfp;
44238032Speter	}
44338032Speter	else
44438032Speter		qf = tf;
44538032Speter	errno = 0;
44638032Speter	e->e_flags |= EF_INQUEUE;
44738032Speter
44838032Speter	/* save log info */
44938032Speter	if (LogLevel > 79)
45038032Speter		sm_syslog(LOG_DEBUG, e->e_id, "queueup, qf=%s", qf);
45138032Speter
45238032Speter	if (tTd(40, 1))
45338032Speter		printf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
45438032Speter	return;
45538032Speter}
45638032Speter
45738032Spetervoid
45838032Speterprintctladdr(a, tfp)
45938032Speter	register ADDRESS *a;
46038032Speter	FILE *tfp;
46138032Speter{
46238032Speter	char *uname;
46338032Speter	register ADDRESS *q;
46438032Speter	uid_t uid;
46538032Speter	gid_t gid;
46638032Speter	static ADDRESS *lastctladdr = NULL;
46738032Speter	static uid_t lastuid;
46838032Speter
46938032Speter	/* initialization */
47038032Speter	if (a == NULL || a->q_alias == NULL || tfp == NULL)
47138032Speter	{
47238032Speter		if (lastctladdr != NULL && tfp != NULL)
47338032Speter			fprintf(tfp, "C\n");
47438032Speter		lastctladdr = NULL;
47538032Speter		lastuid = 0;
47638032Speter		return;
47738032Speter	}
47838032Speter
47938032Speter	/* find the active uid */
48038032Speter	q = getctladdr(a);
48138032Speter	if (q == NULL)
48238032Speter	{
48338032Speter		uname = NULL;
48438032Speter		uid = 0;
48538032Speter		gid = 0;
48638032Speter	}
48738032Speter	else
48838032Speter	{
48938032Speter		uname = q->q_ruser != NULL ? q->q_ruser : q->q_user;
49038032Speter		uid = q->q_uid;
49138032Speter		gid = q->q_gid;
49238032Speter	}
49338032Speter	a = a->q_alias;
49438032Speter
49538032Speter	/* check to see if this is the same as last time */
49638032Speter	if (lastctladdr != NULL && uid == lastuid &&
49738032Speter	    strcmp(lastctladdr->q_paddr, a->q_paddr) == 0)
49838032Speter		return;
49938032Speter	lastuid = uid;
50038032Speter	lastctladdr = a;
50138032Speter
50238032Speter	if (uid == 0 || uname == NULL || uname[0] == '\0')
50338032Speter		fprintf(tfp, "C");
50438032Speter	else
50538032Speter		fprintf(tfp, "C%s:%ld:%ld",
50638032Speter			denlstring(uname, TRUE, FALSE), (long) uid, (long) gid);
50738032Speter	fprintf(tfp, ":%s\n", denlstring(a->q_paddr, TRUE, FALSE));
50838032Speter}
50938032Speter/*
51038032Speter**  RUNQUEUE -- run the jobs in the queue.
51138032Speter**
51238032Speter**	Gets the stuff out of the queue in some presumably logical
51338032Speter**	order and processes them.
51438032Speter**
51538032Speter**	Parameters:
51638032Speter**		forkflag -- TRUE if the queue scanning should be done in
51738032Speter**			a child process.  We double-fork so it is not our
51838032Speter**			child and we don't have to clean up after it.
51938032Speter**		verbose -- if TRUE, print out status information.
52038032Speter**
52138032Speter**	Returns:
52238032Speter**		TRUE if the queue run successfully began.
52338032Speter**
52438032Speter**	Side Effects:
52538032Speter**		runs things in the mail queue.
52638032Speter*/
52738032Speter
52838032SpeterENVELOPE	QueueEnvelope;		/* the queue run envelope */
52938032Speterextern int	get_num_procs_online __P((void));
53038032Speter
53138032Speterbool
53238032Speterrunqueue(forkflag, verbose)
53338032Speter	bool forkflag;
53438032Speter	bool verbose;
53538032Speter{
53638032Speter	register ENVELOPE *e;
53738032Speter	int njobs;
53838032Speter	int sequenceno = 0;
53938032Speter	time_t current_la_time;
54038032Speter	extern ENVELOPE BlankEnvelope;
54138032Speter	extern void clrdaemon __P((void));
54238032Speter	extern void runqueueevent __P((void));
54338032Speter
54438032Speter	DoQueueRun = FALSE;
54538032Speter
54638032Speter	/*
54738032Speter	**  If no work will ever be selected, don't even bother reading
54838032Speter	**  the queue.
54938032Speter	*/
55038032Speter
55138032Speter	CurrentLA = getla();	/* get load average */
55238032Speter	current_la_time = curtime();
55338032Speter
55438032Speter	if (shouldqueue(WkRecipFact, current_la_time))
55538032Speter	{
55638032Speter		char *msg = "Skipping queue run -- load average too high";
55738032Speter
55838032Speter		if (verbose)
55938032Speter			message("458 %s\n", msg);
56038032Speter		if (LogLevel > 8)
56138032Speter			sm_syslog(LOG_INFO, NOQID,
56238032Speter				"runqueue: %s",
56338032Speter				msg);
56438032Speter		if (forkflag && QueueIntvl != 0)
56538032Speter			(void) setevent(QueueIntvl, runqueueevent, 0);
56638032Speter		return FALSE;
56738032Speter	}
56838032Speter
56938032Speter	/*
57038032Speter	**  See if we already have too many children.
57138032Speter	*/
57238032Speter
57338032Speter	if (forkflag && QueueIntvl != 0 &&
57438032Speter	    MaxChildren > 0 && CurChildren >= MaxChildren)
57538032Speter	{
57638032Speter		(void) setevent(QueueIntvl, runqueueevent, 0);
57738032Speter		return FALSE;
57838032Speter	}
57938032Speter
58038032Speter	/*
58138032Speter	**  See if we want to go off and do other useful work.
58238032Speter	*/
58338032Speter
58438032Speter	if (forkflag)
58538032Speter	{
58638032Speter		pid_t pid;
58738032Speter		extern SIGFUNC_DECL intsig __P((int));
58838032Speter		extern SIGFUNC_DECL reapchild __P((int));
58938032Speter
59038032Speter		blocksignal(SIGCHLD);
59138032Speter		(void) setsignal(SIGCHLD, reapchild);
59238032Speter
59338032Speter		pid = dofork();
59438032Speter		if (pid == -1)
59538032Speter		{
59638032Speter			const char *msg = "Skipping queue run -- fork() failed";
59738032Speter			const char *err = errstring(errno);
59838032Speter
59938032Speter			if (verbose)
60038032Speter				message("458 %s: %s\n", msg, err);
60138032Speter			if (LogLevel > 8)
60238032Speter				sm_syslog(LOG_INFO, NOQID,
60338032Speter					"runqueue: %s: %s",
60438032Speter					msg, err);
60538032Speter			if (QueueIntvl != 0)
60638032Speter				(void) setevent(QueueIntvl, runqueueevent, 0);
60738032Speter			(void) releasesignal(SIGCHLD);
60838032Speter			return FALSE;
60938032Speter		}
61038032Speter		if (pid != 0)
61138032Speter		{
61238032Speter			/* parent -- pick up intermediate zombie */
61338032Speter			(void) blocksignal(SIGALRM);
61438032Speter			proc_list_add(pid);
61538032Speter			(void) releasesignal(SIGALRM);
61638032Speter			releasesignal(SIGCHLD);
61738032Speter			if (QueueIntvl != 0)
61838032Speter				(void) setevent(QueueIntvl, runqueueevent, 0);
61938032Speter			return TRUE;
62038032Speter		}
62138032Speter		/* child -- double fork and clean up signals */
62238032Speter		proc_list_clear();
62338032Speter		releasesignal(SIGCHLD);
62438032Speter		(void) setsignal(SIGCHLD, SIG_DFL);
62538032Speter		(void) setsignal(SIGHUP, intsig);
62638032Speter	}
62738032Speter
62838032Speter	setproctitle("running queue: %s", QueueDir);
62938032Speter
63038032Speter	if (LogLevel > 69)
63138032Speter		sm_syslog(LOG_DEBUG, NOQID,
63238032Speter			"runqueue %s, pid=%d, forkflag=%d",
63338032Speter			QueueDir, getpid(), forkflag);
63438032Speter
63538032Speter	/*
63638032Speter	**  Release any resources used by the daemon code.
63738032Speter	*/
63838032Speter
63938032Speter# if DAEMON
64038032Speter	clrdaemon();
64138032Speter# endif /* DAEMON */
64238032Speter
64338032Speter	/* force it to run expensive jobs */
64438032Speter	NoConnect = FALSE;
64538032Speter
64638032Speter	/* drop privileges */
64738032Speter	if (geteuid() == (uid_t) 0)
64838032Speter		(void) drop_privileges(FALSE);
64938032Speter
65038032Speter	/*
65138032Speter	**  Create ourselves an envelope
65238032Speter	*/
65338032Speter
65438032Speter	CurEnv = &QueueEnvelope;
65538032Speter	e = newenvelope(&QueueEnvelope, CurEnv);
65638032Speter	e->e_flags = BlankEnvelope.e_flags;
65738032Speter
65838032Speter	/* make sure we have disconnected from parent */
65938032Speter	if (forkflag)
66038032Speter	{
66138032Speter		disconnect(1, e);
66238032Speter		QuickAbort = FALSE;
66338032Speter	}
66438032Speter
66538032Speter	/*
66638032Speter	**  Make sure the alias database is open.
66738032Speter	*/
66838032Speter
66938032Speter	initmaps(FALSE, e);
67038032Speter
67138032Speter	/*
67238032Speter	**  If we are running part of the queue, always ignore stored
67338032Speter	**  host status.
67438032Speter	*/
67538032Speter
67638032Speter	if (QueueLimitId != NULL || QueueLimitSender != NULL ||
67738032Speter	    QueueLimitRecipient != NULL)
67838032Speter	{
67938032Speter		IgnoreHostStatus = TRUE;
68038032Speter		MinQueueAge = 0;
68138032Speter	}
68238032Speter
68338032Speter	/*
68438032Speter	**  Start making passes through the queue.
68538032Speter	**	First, read and sort the entire queue.
68638032Speter	**	Then, process the work in that order.
68738032Speter	**		But if you take too long, start over.
68838032Speter	*/
68938032Speter
69038032Speter	/* order the existing work requests */
69138032Speter	njobs = orderq(FALSE);
69238032Speter
69338032Speter	/* process them once at a time */
69438032Speter	while (WorkQ != NULL)
69538032Speter	{
69638032Speter		WORK *w = WorkQ;
69738032Speter
69838032Speter		WorkQ = WorkQ->w_next;
69938032Speter		e->e_to = NULL;
70038032Speter
70138032Speter		/*
70238032Speter		**  Ignore jobs that are too expensive for the moment.
70338032Speter		**
70438032Speter		**	Get new load average every 30 seconds.
70538032Speter		*/
70638032Speter
70738032Speter		if (current_la_time < curtime() - 30)
70838032Speter		{
70938032Speter			CurrentLA = getla();
71038032Speter			current_la_time = curtime();
71138032Speter		}
71238032Speter		if (shouldqueue(WkRecipFact, current_la_time))
71338032Speter		{
71438032Speter			char *msg = "Aborting queue run: load average too high";
71538032Speter
71638032Speter			if (Verbose)
71738032Speter				message("%s", msg);
71838032Speter			if (LogLevel > 8)
71938032Speter				sm_syslog(LOG_INFO, NOQID,
72038032Speter					"runqueue: %s",
72138032Speter					msg);
72238032Speter			break;
72338032Speter		}
72438032Speter		sequenceno++;
72538032Speter		if (shouldqueue(w->w_pri, w->w_ctime))
72638032Speter		{
72738032Speter			if (Verbose)
72838032Speter				message("");
72938032Speter			if (QueueSortOrder == QS_BYPRIORITY)
73038032Speter			{
73138032Speter				if (Verbose)
73238032Speter					message("Skipping %s (sequence %d of %d) and flushing rest of queue",
73338032Speter						w->w_name + 2,
73438032Speter						sequenceno,
73538032Speter						njobs);
73638032Speter				if (LogLevel > 8)
73738032Speter					sm_syslog(LOG_INFO, NOQID,
73838032Speter						"runqueue: Flushing queue from %s (pri %ld, LA %d, %d of %d)",
73938032Speter						w->w_name + 2,
74038032Speter						w->w_pri,
74138032Speter						CurrentLA,
74238032Speter						sequenceno,
74338032Speter						njobs);
74438032Speter				break;
74538032Speter			}
74638032Speter			else if (Verbose)
74738032Speter				message("Skipping %s (sequence %d of %d)",
74838032Speter					w->w_name + 2, sequenceno, njobs);
74938032Speter		}
75038032Speter		else
75138032Speter		{
75238032Speter			pid_t pid;
75338032Speter			extern pid_t dowork __P((char *, bool, bool, ENVELOPE *));
75438032Speter
75538032Speter			if (Verbose)
75638032Speter			{
75738032Speter				message("");
75838032Speter				message("Running %s (sequence %d of %d)",
75938032Speter					w->w_name + 2, sequenceno, njobs);
76038032Speter			}
76138032Speter			pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e);
76238032Speter			errno = 0;
76338032Speter			if (pid != 0)
76438032Speter				(void) waitfor(pid);
76538032Speter		}
76638032Speter		free(w->w_name);
76738032Speter		if (w->w_host)
76838032Speter			free(w->w_host);
76938032Speter		free((char *) w);
77038032Speter	}
77138032Speter
77238032Speter	/* exit without the usual cleanup */
77338032Speter	e->e_id = NULL;
77438032Speter	finis();
77538032Speter	/*NOTREACHED*/
77638032Speter	return TRUE;
77738032Speter}
77838032Speter
77938032Speter
78038032Speter/*
78138032Speter**  RUNQUEUEEVENT -- stub for use in setevent
78238032Speter*/
78338032Speter
78438032Spetervoid
78538032Speterrunqueueevent()
78638032Speter{
78738032Speter	DoQueueRun = TRUE;
78838032Speter}
78938032Speter/*
79038032Speter**  ORDERQ -- order the work queue.
79138032Speter**
79238032Speter**	Parameters:
79338032Speter**		doall -- if set, include everything in the queue (even
79438032Speter**			the jobs that cannot be run because the load
79538032Speter**			average is too high).  Otherwise, exclude those
79638032Speter**			jobs.
79738032Speter**
79838032Speter**	Returns:
79938032Speter**		The number of request in the queue (not necessarily
80038032Speter**		the number of requests in WorkQ however).
80138032Speter**
80238032Speter**	Side Effects:
80338032Speter**		Sets WorkQ to the queue of available work, in order.
80438032Speter*/
80538032Speter
80638032Speter# define NEED_P		001
80738032Speter# define NEED_T		002
80838032Speter# define NEED_R		004
80938032Speter# define NEED_S		010
81038032Speter
81138032Speterstatic WORK	*WorkList = NULL;
81238032Speterstatic int	WorkListSize = 0;
81338032Speter
81438032Speterint
81538032Speterorderq(doall)
81638032Speter	bool doall;
81738032Speter{
81838032Speter	register struct dirent *d;
81938032Speter	register WORK *w;
82038032Speter	register char *p;
82138032Speter	DIR *f;
82238032Speter	register int i;
82338032Speter	int wn = -1;
82438032Speter	int wc;
82538032Speter	QUEUE_CHAR *check;
82638032Speter
82738032Speter	if (tTd(41, 1))
82838032Speter	{
82938032Speter		printf("orderq:\n");
83038032Speter
83138032Speter		check = QueueLimitId;
83238032Speter		while (check != NULL)
83338032Speter		{
83438032Speter			printf("\tQueueLimitId = %s\n",
83538032Speter			       check->queue_match);
83638032Speter			check = check->queue_next;
83738032Speter		}
83838032Speter
83938032Speter		check = QueueLimitSender;
84038032Speter		while (check != NULL)
84138032Speter		{
84238032Speter			printf("\tQueueLimitSender = %s\n",
84338032Speter			       check->queue_match);
84438032Speter			check = check->queue_next;
84538032Speter		}
84638032Speter
84738032Speter		check = QueueLimitRecipient;
84838032Speter		while (check != NULL)
84938032Speter		{
85038032Speter			printf("\tQueueLimitRecipient = %s\n",
85138032Speter			       check->queue_match);
85238032Speter			check = check->queue_next;
85338032Speter		}
85438032Speter	}
85538032Speter
85638032Speter	/* clear out old WorkQ */
85738032Speter	for (w = WorkQ; w != NULL; )
85838032Speter	{
85938032Speter		register WORK *nw = w->w_next;
86038032Speter
86138032Speter		WorkQ = nw;
86238032Speter		free(w->w_name);
86338032Speter		if (w->w_host)
86438032Speter			free(w->w_host);
86538032Speter		free((char *) w);
86638032Speter		w = nw;
86738032Speter	}
86838032Speter
86938032Speter	/* open the queue directory */
87038032Speter	f = opendir(".");
87138032Speter	if (f == NULL)
87238032Speter	{
87338032Speter		syserr("orderq: cannot open \"%s\" as \".\"", QueueDir);
87438032Speter		return (0);
87538032Speter	}
87638032Speter
87738032Speter	/*
87838032Speter	**  Read the work directory.
87938032Speter	*/
88038032Speter
88138032Speter	while ((d = readdir(f)) != NULL)
88238032Speter	{
88338032Speter		FILE *cf;
88438032Speter		int qfver = 0;
88538032Speter		char lbuf[MAXNAME + 1];
88638032Speter		extern bool strcontainedin __P((char *, char *));
88738032Speter
88838032Speter		if (tTd(41, 50))
88938032Speter			printf("orderq: checking %s\n", d->d_name);
89038032Speter
89138032Speter		/* is this an interesting entry? */
89238032Speter		if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
89338032Speter			continue;
89438032Speter
89538032Speter		if (strlen(d->d_name) > MAXQFNAME)
89638032Speter			continue;
89738032Speter
89838032Speter		check = QueueLimitId;
89938032Speter		while (check != NULL)
90038032Speter		{
90138032Speter			if (strcontainedin(check->queue_match, d->d_name))
90238032Speter				break;
90338032Speter			else
90438032Speter				check = check->queue_next;
90538032Speter		}
90638032Speter		if (QueueLimitId != NULL && check == NULL)
90738032Speter			continue;
90838032Speter
90938032Speter#ifdef PICKY_QF_NAME_CHECK
91038032Speter		/*
91138032Speter		**  Check queue name for plausibility.  This handles
91238032Speter		**  both old and new type ids.
91338032Speter		*/
91438032Speter
91538032Speter		p = d->d_name + 2;
91638032Speter		if (isupper(p[0]) && isupper(p[2]))
91738032Speter			p += 3;
91838032Speter		else if (isupper(p[1]))
91938032Speter			p += 2;
92038032Speter		else
92138032Speter			p = d->d_name;
92238032Speter		for (i = 0; isdigit(*p); p++)
92338032Speter			i++;
92438032Speter		if (i < 5 || *p != '\0')
92538032Speter		{
92638032Speter			if (Verbose)
92738032Speter				printf("orderq: bogus qf name %s\n", d->d_name);
92838032Speter			if (LogLevel > 0)
92938032Speter				sm_syslog(LOG_ALERT, NOQID,
93038032Speter					"orderq: bogus qf name %s",
93138032Speter					d->d_name);
93238032Speter			if (strlen(d->d_name) > (SIZE_T) MAXNAME)
93338032Speter				d->d_name[MAXNAME] = '\0';
93438032Speter			strcpy(lbuf, d->d_name);
93538032Speter			lbuf[0] = 'Q';
93638032Speter			(void) rename(d->d_name, lbuf);
93738032Speter			continue;
93838032Speter		}
93938032Speter#endif
94038032Speter
94138032Speter		/* open control file (if not too many files) */
94238032Speter		if (++wn >= MaxQueueRun && MaxQueueRun > 0)
94338032Speter		{
94438032Speter			if (wn == MaxQueueRun && LogLevel > 0)
94538032Speter				sm_syslog(LOG_ALERT, NOQID,
94638032Speter					"WorkList for %s maxed out at %d",
94738032Speter					QueueDir, MaxQueueRun);
94838032Speter			continue;
94938032Speter		}
95038032Speter		if (wn >= WorkListSize)
95138032Speter		{
95238032Speter			extern void grow_wlist __P((void));
95338032Speter
95438032Speter			grow_wlist();
95538032Speter			if (wn >= WorkListSize)
95638032Speter				continue;
95738032Speter		}
95838032Speter
95938032Speter		cf = fopen(d->d_name, "r");
96038032Speter		if (cf == NULL)
96138032Speter		{
96238032Speter			/* this may be some random person sending hir msgs */
96338032Speter			/* syserr("orderq: cannot open %s", cbuf); */
96438032Speter			if (tTd(41, 2))
96538032Speter				printf("orderq: cannot open %s: %s\n",
96638032Speter					d->d_name, errstring(errno));
96738032Speter			errno = 0;
96838032Speter			wn--;
96938032Speter			continue;
97038032Speter		}
97138032Speter		w = &WorkList[wn];
97238032Speter		w->w_name = newstr(d->d_name);
97338032Speter		w->w_host = NULL;
97438032Speter		w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB);
97538032Speter		w->w_tooyoung = FALSE;
97638032Speter
97738032Speter		/* make sure jobs in creation don't clog queue */
97838032Speter		w->w_pri = 0x7fffffff;
97938032Speter		w->w_ctime = 0;
98038032Speter
98138032Speter		/* extract useful information */
98238032Speter		i = NEED_P | NEED_T;
98338032Speter		if (QueueLimitSender != NULL)
98438032Speter			i |= NEED_S;
98538032Speter		if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL)
98638032Speter			i |= NEED_R;
98738032Speter		while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
98838032Speter		{
98938032Speter			int c;
99038032Speter			time_t age;
99138032Speter			extern bool strcontainedin __P((char *, char *));
99238032Speter
99338032Speter			p = strchr(lbuf, '\n');
99438032Speter			if (p != NULL)
99538032Speter				*p = '\0';
99638032Speter			else
99738032Speter			{
99838032Speter				/* flush rest of overly long line */
99938032Speter				while ((c = getc(cf)) != EOF && c != '\n')
100038032Speter					continue;
100138032Speter			}
100238032Speter
100338032Speter			switch (lbuf[0])
100438032Speter			{
100538032Speter			  case 'V':
100638032Speter				qfver = atoi(&lbuf[1]);
100738032Speter				break;
100838032Speter
100938032Speter			  case 'P':
101038032Speter				w->w_pri = atol(&lbuf[1]);
101138032Speter				i &= ~NEED_P;
101238032Speter				break;
101338032Speter
101438032Speter			  case 'T':
101538032Speter				w->w_ctime = atol(&lbuf[1]);
101638032Speter				i &= ~NEED_T;
101738032Speter				break;
101838032Speter
101938032Speter			  case 'R':
102038032Speter				if (w->w_host == NULL &&
102138032Speter				    (p = strrchr(&lbuf[1], '@')) != NULL)
102238032Speter					w->w_host = newstr(&p[1]);
102338032Speter				if (QueueLimitRecipient == NULL)
102438032Speter				{
102538032Speter					i &= ~NEED_R;
102638032Speter					break;
102738032Speter				}
102838032Speter				if (qfver > 0)
102938032Speter				{
103038032Speter					p = strchr(&lbuf[1], ':');
103138032Speter					if (p == NULL)
103238032Speter						p = &lbuf[1];
103338032Speter				}
103438032Speter				else
103538032Speter					p = &lbuf[1];
103638032Speter				check = QueueLimitRecipient;
103738032Speter				while (check != NULL)
103838032Speter				{
103938032Speter					if (strcontainedin(check->queue_match,
104038032Speter							   p))
104138032Speter						break;
104238032Speter					else
104338032Speter						check = check->queue_next;
104438032Speter				}
104538032Speter				if (check != NULL)
104638032Speter					i &= ~NEED_R;
104738032Speter				break;
104838032Speter
104938032Speter			  case 'S':
105038032Speter				  check = QueueLimitSender;
105138032Speter				  while (check != NULL)
105238032Speter				  {
105338032Speter					  if (strcontainedin(check->queue_match,
105438032Speter							     &lbuf[1]))
105538032Speter						  break;
105638032Speter					  else
105738032Speter						  check = check->queue_next;
105838032Speter				  }
105938032Speter				  if (check != NULL)
106038032Speter					  i &= ~NEED_S;
106138032Speter				break;
106238032Speter
106338032Speter			  case 'K':
106438032Speter				age = curtime() - (time_t) atol(&lbuf[1]);
106538032Speter				if (age >= 0 && MinQueueAge > 0 &&
106638032Speter				    age < MinQueueAge)
106738032Speter					w->w_tooyoung = TRUE;
106838032Speter				break;
106938032Speter
107038032Speter			  case 'N':
107138032Speter				if (atol(&lbuf[1]) == 0)
107238032Speter					w->w_tooyoung = FALSE;
107338032Speter				break;
107438032Speter			}
107538032Speter		}
107638032Speter		(void) fclose(cf);
107738032Speter
107838032Speter		if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) ||
107938032Speter		    bitset(NEED_R|NEED_S, i))
108038032Speter		{
108138032Speter			/* don't even bother sorting this job in */
108238032Speter			if (tTd(41, 49))
108338032Speter				printf("skipping %s (%x)\n", w->w_name, i);
108438032Speter			free(w->w_name);
108538032Speter			if (w->w_host)
108638032Speter				free(w->w_host);
108738032Speter			wn--;
108838032Speter		}
108938032Speter	}
109038032Speter	(void) closedir(f);
109138032Speter	wn++;
109238032Speter
109338032Speter	wc = min(wn, WorkListSize);
109438032Speter	if (wc > MaxQueueRun && MaxQueueRun > 0)
109538032Speter		wc = MaxQueueRun;
109638032Speter
109738032Speter	if (QueueSortOrder == QS_BYHOST)
109838032Speter	{
109938032Speter		extern int workcmpf1();
110038032Speter		extern int workcmpf2();
110138032Speter
110238032Speter		/*
110338032Speter		**  Sort the work directory for the first time,
110438032Speter		**  based on host name, lock status, and priority.
110538032Speter		*/
110638032Speter
110738032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1);
110838032Speter
110938032Speter		/*
111038032Speter		**  If one message to host is locked, "lock" all messages
111138032Speter		**  to that host.
111238032Speter		*/
111338032Speter
111438032Speter		i = 0;
111538032Speter		while (i < wc)
111638032Speter		{
111738032Speter			if (!WorkList[i].w_lock)
111838032Speter			{
111938032Speter				i++;
112038032Speter				continue;
112138032Speter			}
112238032Speter			w = &WorkList[i];
112338032Speter			while (++i < wc)
112438032Speter			{
112538032Speter				extern int sm_strcasecmp __P((char *, char *));
112638032Speter
112738032Speter				if (WorkList[i].w_host == NULL &&
112838032Speter				    w->w_host == NULL)
112938032Speter					WorkList[i].w_lock = TRUE;
113038032Speter				else if (WorkList[i].w_host != NULL &&
113138032Speter					 w->w_host != NULL &&
113238032Speter					 sm_strcasecmp(WorkList[i].w_host, w->w_host) == 0)
113338032Speter					WorkList[i].w_lock = TRUE;
113438032Speter				else
113538032Speter					break;
113638032Speter			}
113738032Speter		}
113838032Speter
113938032Speter		/*
114038032Speter		**  Sort the work directory for the second time,
114138032Speter		**  based on lock status, host name, and priority.
114238032Speter		*/
114338032Speter
114438032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2);
114538032Speter	}
114638032Speter	else if (QueueSortOrder == QS_BYTIME)
114738032Speter	{
114838032Speter		extern int workcmpf3();
114938032Speter
115038032Speter		/*
115138032Speter		**  Simple sort based on submission time only.
115238032Speter		*/
115338032Speter
115438032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3);
115538032Speter	}
115638032Speter	else
115738032Speter	{
115838032Speter		extern int workcmpf0();
115938032Speter
116038032Speter		/*
116138032Speter		**  Simple sort based on queue priority only.
116238032Speter		*/
116338032Speter
116438032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0);
116538032Speter	}
116638032Speter
116738032Speter	/*
116838032Speter	**  Convert the work list into canonical form.
116938032Speter	**	Should be turning it into a list of envelopes here perhaps.
117038032Speter	*/
117138032Speter
117238032Speter	WorkQ = NULL;
117338032Speter	for (i = wc; --i >= 0; )
117438032Speter	{
117538032Speter		w = (WORK *) xalloc(sizeof *w);
117638032Speter		w->w_name = WorkList[i].w_name;
117738032Speter		w->w_host = WorkList[i].w_host;
117838032Speter		w->w_lock = WorkList[i].w_lock;
117938032Speter		w->w_tooyoung = WorkList[i].w_tooyoung;
118038032Speter		w->w_pri = WorkList[i].w_pri;
118138032Speter		w->w_ctime = WorkList[i].w_ctime;
118238032Speter		w->w_next = WorkQ;
118338032Speter		WorkQ = w;
118438032Speter	}
118538032Speter	if (WorkList != NULL)
118638032Speter		free(WorkList);
118738032Speter	WorkList = NULL;
118838032Speter	WorkListSize = 0;
118938032Speter
119038032Speter	if (tTd(40, 1))
119138032Speter	{
119238032Speter		for (w = WorkQ; w != NULL; w = w->w_next)
119338032Speter			printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
119438032Speter	}
119538032Speter
119638032Speter	return (wn);
119738032Speter}
119838032Speter/*
119938032Speter**  GROW_WLIST -- make the work list larger
120038032Speter**
120138032Speter**	Parameters:
120238032Speter**		none.
120338032Speter**
120438032Speter**	Returns:
120538032Speter**		none.
120638032Speter**
120738032Speter**	Side Effects:
120838032Speter**		Adds another QUEUESEGSIZE entries to WorkList if possible.
120938032Speter**		It can fail if there isn't enough memory, so WorkListSize
121038032Speter**		should be checked again upon return.
121138032Speter*/
121238032Speter
121338032Spetervoid
121438032Spetergrow_wlist()
121538032Speter{
121638032Speter	if (tTd(41, 1))
121738032Speter		printf("grow_wlist: WorkListSize=%d\n", WorkListSize);
121838032Speter	if (WorkList == NULL)
121938032Speter	{
122038032Speter		WorkList = (WORK *) xalloc(sizeof(WORK) * (QUEUESEGSIZE + 1));
122138032Speter		WorkListSize = QUEUESEGSIZE;
122238032Speter	}
122338032Speter	else
122438032Speter	{
122538032Speter		int newsize = WorkListSize + QUEUESEGSIZE;
122638032Speter		WORK *newlist = (WORK *) realloc((char *)WorkList,
122738032Speter					  (unsigned)sizeof(WORK) * (newsize + 1));
122838032Speter
122938032Speter		if (newlist != NULL)
123038032Speter		{
123138032Speter			WorkListSize = newsize;
123238032Speter			WorkList = newlist;
123338032Speter			if (LogLevel > 1)
123438032Speter			{
123538032Speter				sm_syslog(LOG_NOTICE, NOQID,
123638032Speter					"grew WorkList for %s to %d",
123738032Speter					QueueDir, WorkListSize);
123838032Speter			}
123938032Speter		}
124038032Speter		else if (LogLevel > 0)
124138032Speter		{
124238032Speter			sm_syslog(LOG_ALERT, NOQID,
124338032Speter				"FAILED to grow WorkList for %s to %d",
124438032Speter				QueueDir, newsize);
124538032Speter		}
124638032Speter	}
124738032Speter	if (tTd(41, 1))
124838032Speter		printf("grow_wlist: WorkListSize now %d\n", WorkListSize);
124938032Speter}
125038032Speter/*
125138032Speter**  WORKCMPF0 -- simple priority-only compare function.
125238032Speter**
125338032Speter**	Parameters:
125438032Speter**		a -- the first argument.
125538032Speter**		b -- the second argument.
125638032Speter**
125738032Speter**	Returns:
125838032Speter**		-1 if a < b
125938032Speter**		 0 if a == b
126038032Speter**		+1 if a > b
126138032Speter**
126238032Speter**	Side Effects:
126338032Speter**		none.
126438032Speter*/
126538032Speter
126638032Speterint
126738032Speterworkcmpf0(a, b)
126838032Speter	register WORK *a;
126938032Speter	register WORK *b;
127038032Speter{
127138032Speter	long pa = a->w_pri;
127238032Speter	long pb = b->w_pri;
127338032Speter
127438032Speter	if (pa == pb)
127538032Speter		return 0;
127638032Speter	else if (pa > pb)
127738032Speter		return 1;
127838032Speter	else
127938032Speter		return -1;
128038032Speter}
128138032Speter/*
128238032Speter**  WORKCMPF1 -- first compare function for ordering work based on host name.
128338032Speter**
128438032Speter**	Sorts on host name, lock status, and priority in that order.
128538032Speter**
128638032Speter**	Parameters:
128738032Speter**		a -- the first argument.
128838032Speter**		b -- the second argument.
128938032Speter**
129038032Speter**	Returns:
129138032Speter**		<0 if a < b
129238032Speter**		 0 if a == b
129338032Speter**		>0 if a > b
129438032Speter**
129538032Speter**	Side Effects:
129638032Speter**		none.
129738032Speter*/
129838032Speter
129938032Speterint
130038032Speterworkcmpf1(a, b)
130138032Speter	register WORK *a;
130238032Speter	register WORK *b;
130338032Speter{
130438032Speter	int i;
130538032Speter	extern int sm_strcasecmp __P((char *, char *));
130638032Speter
130738032Speter	/* host name */
130838032Speter	if (a->w_host != NULL && b->w_host == NULL)
130938032Speter		return 1;
131038032Speter	else if (a->w_host == NULL && b->w_host != NULL)
131138032Speter		return -1;
131238032Speter	if (a->w_host != NULL && b->w_host != NULL &&
131338032Speter	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
131438032Speter		return i;
131538032Speter
131638032Speter	/* lock status */
131738032Speter	if (a->w_lock != b->w_lock)
131838032Speter		return b->w_lock - a->w_lock;
131938032Speter
132038032Speter	/* job priority */
132138032Speter	return a->w_pri - b->w_pri;
132238032Speter}
132338032Speter/*
132438032Speter**  WORKCMPF2 -- second compare function for ordering work based on host name.
132538032Speter**
132638032Speter**	Sorts on lock status, host name, and priority in that order.
132738032Speter**
132838032Speter**	Parameters:
132938032Speter**		a -- the first argument.
133038032Speter**		b -- the second argument.
133138032Speter**
133238032Speter**	Returns:
133338032Speter**		<0 if a < b
133438032Speter**		 0 if a == b
133538032Speter**		>0 if a > b
133638032Speter**
133738032Speter**	Side Effects:
133838032Speter**		none.
133938032Speter*/
134038032Speter
134138032Speterint
134238032Speterworkcmpf2(a, b)
134338032Speter	register WORK *a;
134438032Speter	register WORK *b;
134538032Speter{
134638032Speter	int i;
134738032Speter	extern int sm_strcasecmp __P((char *, char *));
134838032Speter
134938032Speter	/* lock status */
135038032Speter	if (a->w_lock != b->w_lock)
135138032Speter		return a->w_lock - b->w_lock;
135238032Speter
135338032Speter	/* host name */
135438032Speter	if (a->w_host != NULL && b->w_host == NULL)
135538032Speter		return 1;
135638032Speter	else if (a->w_host == NULL && b->w_host != NULL)
135738032Speter		return -1;
135838032Speter	if (a->w_host != NULL && b->w_host != NULL &&
135938032Speter	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
136038032Speter		return i;
136138032Speter
136238032Speter	/* job priority */
136338032Speter	return a->w_pri - b->w_pri;
136438032Speter}
136538032Speter/*
136638032Speter**  WORKCMPF3 -- simple submission-time-only compare function.
136738032Speter**
136838032Speter**	Parameters:
136938032Speter**		a -- the first argument.
137038032Speter**		b -- the second argument.
137138032Speter**
137238032Speter**	Returns:
137338032Speter**		-1 if a < b
137438032Speter**		 0 if a == b
137538032Speter**		+1 if a > b
137638032Speter**
137738032Speter**	Side Effects:
137838032Speter**		none.
137938032Speter*/
138038032Speter
138138032Speterint
138238032Speterworkcmpf3(a, b)
138338032Speter	register WORK *a;
138438032Speter	register WORK *b;
138538032Speter{
138638032Speter	if (a->w_ctime > b->w_ctime)
138738032Speter		return 1;
138838032Speter	else if (a->w_ctime < b->w_ctime)
138938032Speter		return -1;
139038032Speter	else
139138032Speter		return 0;
139238032Speter}
139338032Speter/*
139438032Speter**  DOWORK -- do a work request.
139538032Speter**
139638032Speter**	Parameters:
139738032Speter**		id -- the ID of the job to run.
139838032Speter**		forkflag -- if set, run this in background.
139938032Speter**		requeueflag -- if set, reinstantiate the queue quickly.
140038032Speter**			This is used when expanding aliases in the queue.
140138032Speter**			If forkflag is also set, it doesn't wait for the
140238032Speter**			child.
140338032Speter**		e - the envelope in which to run it.
140438032Speter**
140538032Speter**	Returns:
140638032Speter**		process id of process that is running the queue job.
140738032Speter**
140838032Speter**	Side Effects:
140938032Speter**		The work request is satisfied if possible.
141038032Speter*/
141138032Speter
141238032Speterpid_t
141338032Speterdowork(id, forkflag, requeueflag, e)
141438032Speter	char *id;
141538032Speter	bool forkflag;
141638032Speter	bool requeueflag;
141738032Speter	register ENVELOPE *e;
141838032Speter{
141938032Speter	register pid_t pid;
142038032Speter	extern bool readqf __P((ENVELOPE *));
142138032Speter
142238032Speter	if (tTd(40, 1))
142338032Speter		printf("dowork(%s)\n", id);
142438032Speter
142538032Speter	/*
142638032Speter	**  Fork for work.
142738032Speter	*/
142838032Speter
142938032Speter	if (forkflag)
143038032Speter	{
143138032Speter		pid = fork();
143238032Speter		if (pid < 0)
143338032Speter		{
143438032Speter			syserr("dowork: cannot fork");
143538032Speter			return 0;
143638032Speter		}
143738032Speter		else if (pid > 0)
143838032Speter		{
143938032Speter			/* parent -- clean out connection cache */
144038032Speter			mci_flush(FALSE, NULL);
144138032Speter		}
144238032Speter		else
144338032Speter		{
144438032Speter			/* child -- error messages to the transcript */
144538032Speter			QuickAbort = OnlyOneError = FALSE;
144638032Speter		}
144738032Speter	}
144838032Speter	else
144938032Speter	{
145038032Speter		pid = 0;
145138032Speter	}
145238032Speter
145338032Speter	if (pid == 0)
145438032Speter	{
145538032Speter		/*
145638032Speter		**  CHILD
145738032Speter		**	Lock the control file to avoid duplicate deliveries.
145838032Speter		**		Then run the file as though we had just read it.
145938032Speter		**	We save an idea of the temporary name so we
146038032Speter		**		can recover on interrupt.
146138032Speter		*/
146238032Speter
146338032Speter		/* set basic modes, etc. */
146438032Speter		(void) alarm(0);
146538032Speter		clearenvelope(e, FALSE);
146638032Speter		e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
146738032Speter		e->e_sendmode = SM_DELIVER;
146838032Speter		e->e_errormode = EM_MAIL;
146938032Speter		e->e_id = id;
147038032Speter		GrabTo = UseErrorsTo = FALSE;
147138032Speter		ExitStat = EX_OK;
147238032Speter		if (forkflag)
147338032Speter		{
147438032Speter			disconnect(1, e);
147538032Speter			OpMode = MD_DELIVER;
147638032Speter		}
147738032Speter		setproctitle("%s: from queue", id);
147838032Speter		if (LogLevel > 76)
147938032Speter			sm_syslog(LOG_DEBUG, e->e_id,
148038032Speter				"dowork, pid=%d",
148138032Speter				getpid());
148238032Speter
148338032Speter		/* don't use the headers from sendmail.cf... */
148438032Speter		e->e_header = NULL;
148538032Speter
148638032Speter		/* read the queue control file -- return if locked */
148738032Speter		if (!readqf(e))
148838032Speter		{
148938032Speter			if (tTd(40, 4) && e->e_id != NULL)
149038032Speter				printf("readqf(%s) failed\n", e->e_id);
149138032Speter			e->e_id = NULL;
149238032Speter			if (forkflag)
149338032Speter				exit(EX_OK);
149438032Speter			else
149538032Speter				return 0;
149638032Speter		}
149738032Speter
149838032Speter		e->e_flags |= EF_INQUEUE;
149938032Speter		eatheader(e, requeueflag);
150038032Speter
150138032Speter		if (requeueflag)
150238032Speter			queueup(e, FALSE);
150338032Speter
150438032Speter		/* do the delivery */
150538032Speter		sendall(e, SM_DELIVER);
150638032Speter
150738032Speter		/* finish up and exit */
150838032Speter		if (forkflag)
150938032Speter			finis();
151038032Speter		else
151138032Speter			dropenvelope(e, TRUE);
151238032Speter	}
151338032Speter	e->e_id = NULL;
151438032Speter	return pid;
151538032Speter}
151638032Speter/*
151738032Speter**  READQF -- read queue file and set up environment.
151838032Speter**
151938032Speter**	Parameters:
152038032Speter**		e -- the envelope of the job to run.
152138032Speter**
152238032Speter**	Returns:
152338032Speter**		TRUE if it successfully read the queue file.
152438032Speter**		FALSE otherwise.
152538032Speter**
152638032Speter**	Side Effects:
152738032Speter**		The queue file is returned locked.
152838032Speter*/
152938032Speter
153038032Speterbool
153138032Speterreadqf(e)
153238032Speter	register ENVELOPE *e;
153338032Speter{
153438032Speter	register FILE *qfp;
153538032Speter	ADDRESS *ctladdr;
153638032Speter	struct stat st;
153738032Speter	char *bp;
153838032Speter	int qfver = 0;
153938032Speter	long hdrsize = 0;
154038032Speter	register char *p;
154138032Speter	char *orcpt = NULL;
154238032Speter	bool nomore = FALSE;
154338032Speter	char qf[MAXQFNAME];
154438032Speter	char buf[MAXLINE];
154538032Speter	extern ADDRESS *setctluser __P((char *, int));
154638032Speter
154738032Speter	/*
154838032Speter	**  Read and process the file.
154938032Speter	*/
155038032Speter
155138032Speter	strcpy(qf, queuename(e, 'q'));
155238032Speter	qfp = fopen(qf, "r+");
155338032Speter	if (qfp == NULL)
155438032Speter	{
155538032Speter		if (tTd(40, 8))
155638032Speter			printf("readqf(%s): fopen failure (%s)\n",
155738032Speter				qf, errstring(errno));
155838032Speter		if (errno != ENOENT)
155938032Speter			syserr("readqf: no control file %s", qf);
156038032Speter		return FALSE;
156138032Speter	}
156238032Speter
156338032Speter	if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB))
156438032Speter	{
156538032Speter		/* being processed by another queuer */
156638032Speter		if (Verbose || tTd(40, 8))
156738032Speter			printf("%s: locked\n", e->e_id);
156838032Speter		if (LogLevel > 19)
156938032Speter			sm_syslog(LOG_DEBUG, e->e_id, "locked");
157038032Speter		(void) fclose(qfp);
157138032Speter		return FALSE;
157238032Speter	}
157338032Speter
157438032Speter	/*
157538032Speter	**  Check the queue file for plausibility to avoid attacks.
157638032Speter	*/
157738032Speter
157838032Speter	if (fstat(fileno(qfp), &st) < 0)
157938032Speter	{
158038032Speter		/* must have been being processed by someone else */
158138032Speter		if (tTd(40, 8))
158238032Speter			printf("readqf(%s): fstat failure (%s)\n",
158338032Speter				qf, errstring(errno));
158438032Speter		fclose(qfp);
158538032Speter		return FALSE;
158638032Speter	}
158738032Speter
158838032Speter	if ((st.st_uid != geteuid() && geteuid() != RealUid) ||
158938032Speter	    bitset(S_IWOTH|S_IWGRP, st.st_mode))
159038032Speter	{
159138032Speter		if (LogLevel > 0)
159238032Speter		{
159338032Speter			sm_syslog(LOG_ALERT, e->e_id,
159438032Speter				"bogus queue file, uid=%d, mode=%o",
159538032Speter				st.st_uid, st.st_mode);
159638032Speter		}
159738032Speter		if (tTd(40, 8))
159838032Speter			printf("readqf(%s): bogus file\n", qf);
159938032Speter		loseqfile(e, "bogus file uid in mqueue");
160038032Speter		fclose(qfp);
160138032Speter		return FALSE;
160238032Speter	}
160338032Speter
160438032Speter	if (st.st_size == 0)
160538032Speter	{
160638032Speter		/* must be a bogus file -- if also old, just remove it */
160738032Speter		if (st.st_ctime + 10 * 60 < curtime())
160838032Speter		{
160938032Speter			qf[0] = 'd';
161038032Speter			(void) unlink(qf);
161138032Speter			qf[0] = 'q';
161238032Speter			(void) unlink(qf);
161338032Speter		}
161438032Speter		fclose(qfp);
161538032Speter		return FALSE;
161638032Speter	}
161738032Speter
161838032Speter	if (st.st_nlink == 0)
161938032Speter	{
162038032Speter		/*
162138032Speter		**  Race condition -- we got a file just as it was being
162238032Speter		**  unlinked.  Just assume it is zero length.
162338032Speter		*/
162438032Speter
162538032Speter		fclose(qfp);
162638032Speter		return FALSE;
162738032Speter	}
162838032Speter
162938032Speter	/* good file -- save this lock */
163038032Speter	e->e_lockfp = qfp;
163138032Speter
163238032Speter	/* do basic system initialization */
163338032Speter	initsys(e);
163438032Speter	define('i', e->e_id, e);
163538032Speter
163638032Speter	LineNumber = 0;
163738032Speter	e->e_flags |= EF_GLOBALERRS;
163838032Speter	OpMode = MD_DELIVER;
163938032Speter	ctladdr = NULL;
164038032Speter	e->e_dfino = -1;
164138032Speter	e->e_msgsize = -1;
164238032Speter	while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL)
164338032Speter	{
164438032Speter		register char *p;
164538032Speter		u_long qflags;
164638032Speter		ADDRESS *q;
164738032Speter		int mid;
164838032Speter		auto char *ep;
164938032Speter
165038032Speter		if (tTd(40, 4))
165138032Speter			printf("+++++ %s\n", bp);
165238032Speter		if (nomore)
165338032Speter		{
165438032Speter			/* hack attack */
165538032Speter			syserr("SECURITY ALERT: extra data in qf: %s", bp);
165638032Speter			fclose(qfp);
165738032Speter			loseqfile(e, "bogus queue line");
165838032Speter			return FALSE;
165938032Speter		}
166038032Speter		switch (bp[0])
166138032Speter		{
166238032Speter		  case 'V':		/* queue file version number */
166338032Speter			qfver = atoi(&bp[1]);
166438032Speter			if (qfver <= QF_VERSION)
166538032Speter				break;
166638032Speter			syserr("Version number in qf (%d) greater than max (%d)",
166738032Speter				qfver, QF_VERSION);
166838032Speter			fclose(qfp);
166938032Speter			loseqfile(e, "unsupported qf file version");
167038032Speter			return FALSE;
167138032Speter
167238032Speter		  case 'C':		/* specify controlling user */
167338032Speter			ctladdr = setctluser(&bp[1], qfver);
167438032Speter			break;
167538032Speter
167638032Speter		  case 'Q':		/* original recipient */
167738032Speter			orcpt = newstr(&bp[1]);
167838032Speter			break;
167938032Speter
168038032Speter		  case 'R':		/* specify recipient */
168138032Speter			p = bp;
168238032Speter			qflags = 0;
168338032Speter			if (qfver >= 1)
168438032Speter			{
168538032Speter				/* get flag bits */
168638032Speter				while (*++p != '\0' && *p != ':')
168738032Speter				{
168838032Speter					switch (*p)
168938032Speter					{
169038032Speter					  case 'N':
169138032Speter						qflags |= QHASNOTIFY;
169238032Speter						break;
169338032Speter
169438032Speter					  case 'S':
169538032Speter						qflags |= QPINGONSUCCESS;
169638032Speter						break;
169738032Speter
169838032Speter					  case 'F':
169938032Speter						qflags |= QPINGONFAILURE;
170038032Speter						break;
170138032Speter
170238032Speter					  case 'D':
170338032Speter						qflags |= QPINGONDELAY;
170438032Speter						break;
170538032Speter
170638032Speter					  case 'P':
170738032Speter						qflags |= QPRIMARY;
170838032Speter						break;
170938032Speter					}
171038032Speter				}
171138032Speter			}
171238032Speter			else
171338032Speter				qflags |= QPRIMARY;
171438032Speter			q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e);
171538032Speter			if (q != NULL)
171638032Speter			{
171738032Speter				q->q_alias = ctladdr;
171838032Speter				if (qfver >= 1)
171938032Speter					q->q_flags &= ~Q_PINGFLAGS;
172038032Speter				q->q_flags |= qflags;
172138032Speter				q->q_orcpt = orcpt;
172238032Speter				(void) recipient(q, &e->e_sendqueue, 0, e);
172338032Speter			}
172438032Speter			orcpt = NULL;
172538032Speter			break;
172638032Speter
172738032Speter		  case 'E':		/* specify error recipient */
172838032Speter			/* no longer used */
172938032Speter			break;
173038032Speter
173138032Speter		  case 'H':		/* header */
173238032Speter			(void) chompheader(&bp[1], FALSE, NULL, e);
173338032Speter			hdrsize += strlen(&bp[1]);
173438032Speter			break;
173538032Speter
173638032Speter		  case 'L':		/* Solaris Content-Length: */
173738032Speter		  case 'M':		/* message */
173838032Speter			/* ignore this; we want a new message next time */
173938032Speter			break;
174038032Speter
174138032Speter		  case 'S':		/* sender */
174238032Speter			setsender(newstr(&bp[1]), e, NULL, '\0', TRUE);
174338032Speter			break;
174438032Speter
174538032Speter		  case 'B':		/* body type */
174638032Speter			e->e_bodytype = newstr(&bp[1]);
174738032Speter			break;
174838032Speter
174938032Speter#if _FFR_SAVE_CHARSET
175038032Speter		  case 'X':		/* character set */
175138032Speter			e->e_charset = newstr(&bp[1]);
175238032Speter			break;
175338032Speter#endif
175438032Speter
175538032Speter		  case 'D':		/* data file name */
175638032Speter			/* obsolete -- ignore */
175738032Speter			break;
175838032Speter
175938032Speter		  case 'T':		/* init time */
176038032Speter			e->e_ctime = atol(&bp[1]);
176138032Speter			break;
176238032Speter
176338032Speter		  case 'I':		/* data file's inode number */
176438032Speter			/* regenerated below */
176538032Speter			break;
176638032Speter
176738032Speter		  case 'K':		/* time of last deliver attempt */
176838032Speter			e->e_dtime = atol(&buf[1]);
176938032Speter			break;
177038032Speter
177138032Speter		  case 'N':		/* number of delivery attempts */
177238032Speter			e->e_ntries = atoi(&buf[1]);
177338032Speter
177438032Speter			/* if this has been tried recently, let it be */
177538032Speter			if (e->e_ntries > 0 &&
177638032Speter			    MinQueueAge > 0 && e->e_dtime <= curtime() &&
177738032Speter			    curtime() < e->e_dtime + MinQueueAge)
177838032Speter			{
177938032Speter				char *howlong = pintvl(curtime() - e->e_dtime, TRUE);
178038032Speter				extern void unlockqueue __P((ENVELOPE *));
178138032Speter
178238032Speter				if (Verbose || tTd(40, 8))
178338032Speter					printf("%s: too young (%s)\n",
178438032Speter						e->e_id, howlong);
178538032Speter				if (LogLevel > 19)
178638032Speter					sm_syslog(LOG_DEBUG, e->e_id,
178738032Speter						"too young (%s)",
178838032Speter						howlong);
178938032Speter				e->e_id = NULL;
179038032Speter				unlockqueue(e);
179138032Speter				return FALSE;
179238032Speter			}
179338032Speter			break;
179438032Speter
179538032Speter		  case 'P':		/* message priority */
179638032Speter			e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
179738032Speter			break;
179838032Speter
179938032Speter		  case 'F':		/* flag bits */
180038032Speter			if (strncmp(bp, "From ", 5) == 0)
180138032Speter			{
180238032Speter				/* we are being spoofed! */
180338032Speter				syserr("SECURITY ALERT: bogus qf line %s", bp);
180438032Speter				fclose(qfp);
180538032Speter				loseqfile(e, "bogus queue line");
180638032Speter				return FALSE;
180738032Speter			}
180838032Speter			for (p = &bp[1]; *p != '\0'; p++)
180938032Speter			{
181038032Speter				switch (*p)
181138032Speter				{
181238032Speter				  case 'w':	/* warning sent */
181338032Speter					e->e_flags |= EF_WARNING;
181438032Speter					break;
181538032Speter
181638032Speter				  case 'r':	/* response */
181738032Speter					e->e_flags |= EF_RESPONSE;
181838032Speter					break;
181938032Speter
182038032Speter				  case '8':	/* has 8 bit data */
182138032Speter					e->e_flags |= EF_HAS8BIT;
182238032Speter					break;
182338032Speter
182438032Speter				  case 'b':	/* delete Bcc: header */
182538032Speter					e->e_flags |= EF_DELETE_BCC;
182638032Speter					break;
182738032Speter
182838032Speter				  case 'd':	/* envelope has DSN RET= */
182938032Speter					e->e_flags |= EF_RET_PARAM;
183038032Speter					break;
183138032Speter
183238032Speter				  case 'n':	/* don't return body */
183338032Speter					e->e_flags |= EF_NO_BODY_RETN;
183438032Speter					break;
183538032Speter				}
183638032Speter			}
183738032Speter			break;
183838032Speter
183938032Speter		  case 'Z':		/* original envelope id from ESMTP */
184038032Speter			e->e_envid = newstr(&bp[1]);
184138032Speter			break;
184238032Speter
184338032Speter		  case '$':		/* define macro */
184438032Speter			mid = macid(&bp[1], &ep);
184538032Speter			define(mid, newstr(ep), e);
184638032Speter			break;
184738032Speter
184838032Speter		  case '.':		/* terminate file */
184938032Speter			nomore = TRUE;
185038032Speter			break;
185138032Speter
185238032Speter		  default:
185338032Speter			syserr("readqf: %s: line %d: bad line \"%s\"",
185438032Speter				qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
185538032Speter			fclose(qfp);
185638032Speter			loseqfile(e, "unrecognized line");
185738032Speter			return FALSE;
185838032Speter		}
185938032Speter
186038032Speter		if (bp != buf)
186138032Speter			free(bp);
186238032Speter	}
186338032Speter
186438032Speter	/*
186538032Speter	**  If we haven't read any lines, this queue file is empty.
186638032Speter	**  Arrange to remove it without referencing any null pointers.
186738032Speter	*/
186838032Speter
186938032Speter	if (LineNumber == 0)
187038032Speter	{
187138032Speter		errno = 0;
187238032Speter		e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
187338032Speter		return TRUE;
187438032Speter	}
187538032Speter
187638032Speter	/*
187738032Speter	**  Arrange to read the data file.
187838032Speter	*/
187938032Speter
188038032Speter	p = queuename(e, 'd');
188138032Speter	e->e_dfp = fopen(p, "r");
188238032Speter	if (e->e_dfp == NULL)
188338032Speter	{
188438032Speter		syserr("readqf: cannot open %s", p);
188538032Speter	}
188638032Speter	else
188738032Speter	{
188838032Speter		e->e_flags |= EF_HAS_DF;
188938032Speter		if (fstat(fileno(e->e_dfp), &st) >= 0)
189038032Speter		{
189138032Speter			e->e_msgsize = st.st_size + hdrsize;
189238032Speter			e->e_dfdev = st.st_dev;
189338032Speter			e->e_dfino = st.st_ino;
189438032Speter		}
189538032Speter	}
189638032Speter
189738032Speter	return TRUE;
189838032Speter}
189938032Speter/*
190038032Speter**  PRINTQUEUE -- print out a representation of the mail queue
190138032Speter**
190238032Speter**	Parameters:
190338032Speter**		none.
190438032Speter**
190538032Speter**	Returns:
190638032Speter**		none.
190738032Speter**
190838032Speter**	Side Effects:
190938032Speter**		Prints a listing of the mail queue on the standard output.
191038032Speter*/
191138032Speter
191238032Spetervoid
191338032Speterprintqueue()
191438032Speter{
191538032Speter	register WORK *w;
191638032Speter	FILE *f;
191738032Speter	int nrequests;
191838032Speter	char buf[MAXLINE];
191938032Speter
192038032Speter	/*
192138032Speter	**  Check for permission to print the queue
192238032Speter	*/
192338032Speter
192438032Speter	if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
192538032Speter	{
192638032Speter		struct stat st;
192738032Speter# ifdef NGROUPS_MAX
192838032Speter		int n;
192938032Speter		extern GIDSET_T InitialGidSet[NGROUPS_MAX];
193038032Speter# endif
193138032Speter
193238032Speter		if (stat(QueueDir, &st) < 0)
193338032Speter		{
193438032Speter			syserr("Cannot stat %s", QueueDir);
193538032Speter			return;
193638032Speter		}
193738032Speter# ifdef NGROUPS_MAX
193838032Speter		n = NGROUPS_MAX;
193938032Speter		while (--n >= 0)
194038032Speter		{
194138032Speter			if (InitialGidSet[n] == st.st_gid)
194238032Speter				break;
194338032Speter		}
194438032Speter		if (n < 0 && RealGid != st.st_gid)
194538032Speter# else
194638032Speter		if (RealGid != st.st_gid)
194738032Speter# endif
194838032Speter		{
194938032Speter			usrerr("510 You are not permitted to see the queue");
195038032Speter			setstat(EX_NOPERM);
195138032Speter			return;
195238032Speter		}
195338032Speter	}
195438032Speter
195538032Speter	/*
195638032Speter	**  Read and order the queue.
195738032Speter	*/
195838032Speter
195938032Speter	nrequests = orderq(TRUE);
196038032Speter
196138032Speter	/*
196238032Speter	**  Print the work list that we have read.
196338032Speter	*/
196438032Speter
196538032Speter	/* first see if there is anything */
196638032Speter	if (nrequests <= 0)
196738032Speter	{
196838032Speter		printf("Mail queue is empty\n");
196938032Speter		return;
197038032Speter	}
197138032Speter
197238032Speter	CurrentLA = getla();	/* get load average */
197338032Speter
197438032Speter	printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
197538032Speter	if (MaxQueueRun > 0 && nrequests > MaxQueueRun)
197638032Speter		printf(", only %d printed", MaxQueueRun);
197738032Speter	if (Verbose)
197838032Speter		printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n");
197938032Speter	else
198038032Speter		printf(")\n--Q-ID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
198138032Speter	for (w = WorkQ; w != NULL; w = w->w_next)
198238032Speter	{
198338032Speter		struct stat st;
198438032Speter		auto time_t submittime = 0;
198538032Speter		long dfsize;
198638032Speter		int flags = 0;
198738032Speter		int qfver;
198838032Speter		char statmsg[MAXLINE];
198938032Speter		char bodytype[MAXNAME + 1];
199038032Speter
199138032Speter		printf("%8s", w->w_name + 2);
199238032Speter		f = fopen(w->w_name, "r");
199338032Speter		if (f == NULL)
199438032Speter		{
199538032Speter			printf(" (job completed)\n");
199638032Speter			errno = 0;
199738032Speter			continue;
199838032Speter		}
199938032Speter		w->w_name[0] = 'd';
200038032Speter		if (stat(w->w_name, &st) >= 0)
200138032Speter			dfsize = st.st_size;
200238032Speter		else
200338032Speter			dfsize = -1;
200438032Speter		if (w->w_lock)
200538032Speter			printf("*");
200638032Speter		else if (w->w_tooyoung)
200738032Speter			printf("-");
200838032Speter		else if (shouldqueue(w->w_pri, w->w_ctime))
200938032Speter			printf("X");
201038032Speter		else
201138032Speter			printf(" ");
201238032Speter		errno = 0;
201338032Speter
201438032Speter		statmsg[0] = bodytype[0] = '\0';
201538032Speter		qfver = 0;
201638032Speter		while (fgets(buf, sizeof buf, f) != NULL)
201738032Speter		{
201838032Speter			register int i;
201938032Speter			register char *p;
202038032Speter
202138032Speter			fixcrlf(buf, TRUE);
202238032Speter			switch (buf[0])
202338032Speter			{
202438032Speter			  case 'V':	/* queue file version */
202538032Speter				qfver = atoi(&buf[1]);
202638032Speter				break;
202738032Speter
202838032Speter			  case 'M':	/* error message */
202938032Speter				if ((i = strlen(&buf[1])) >= sizeof statmsg)
203038032Speter					i = sizeof statmsg - 1;
203138032Speter				bcopy(&buf[1], statmsg, i);
203238032Speter				statmsg[i] = '\0';
203338032Speter				break;
203438032Speter
203538032Speter			  case 'B':	/* body type */
203638032Speter				if ((i = strlen(&buf[1])) >= sizeof bodytype)
203738032Speter					i = sizeof bodytype - 1;
203838032Speter				bcopy(&buf[1], bodytype, i);
203938032Speter				bodytype[i] = '\0';
204038032Speter				break;
204138032Speter
204238032Speter			  case 'S':	/* sender name */
204338032Speter				if (Verbose)
204438032Speter					printf("%8ld %10ld%c%.12s %.78s",
204538032Speter					    dfsize,
204638032Speter					    w->w_pri,
204738032Speter					    bitset(EF_WARNING, flags) ? '+' : ' ',
204838032Speter					    ctime(&submittime) + 4,
204938032Speter					    &buf[1]);
205038032Speter				else
205138032Speter					printf("%8ld %.16s %.45s", dfsize,
205238032Speter					    ctime(&submittime), &buf[1]);
205338032Speter				if (statmsg[0] != '\0' || bodytype[0] != '\0')
205438032Speter				{
205538032Speter					printf("\n    %10.10s", bodytype);
205638032Speter					if (statmsg[0] != '\0')
205738032Speter						printf("   (%.*s)",
205838032Speter							Verbose ? 100 : 60,
205938032Speter							statmsg);
206038032Speter				}
206138032Speter				break;
206238032Speter
206338032Speter			  case 'C':	/* controlling user */
206438032Speter				if (Verbose)
206538032Speter					printf("\n\t\t\t\t      (---%.74s---)",
206638032Speter						&buf[1]);
206738032Speter				break;
206838032Speter
206938032Speter			  case 'R':	/* recipient name */
207038032Speter				p = &buf[1];
207138032Speter				if (qfver >= 1)
207238032Speter				{
207338032Speter					p = strchr(p, ':');
207438032Speter					if (p == NULL)
207538032Speter						break;
207638032Speter					p++;
207738032Speter				}
207838032Speter				if (Verbose)
207938032Speter					printf("\n\t\t\t\t\t  %.78s", p);
208038032Speter				else
208138032Speter					printf("\n\t\t\t\t   %.45s", p);
208238032Speter				break;
208338032Speter
208438032Speter			  case 'T':	/* creation time */
208538032Speter				submittime = atol(&buf[1]);
208638032Speter				break;
208738032Speter
208838032Speter			  case 'F':	/* flag bits */
208938032Speter				for (p = &buf[1]; *p != '\0'; p++)
209038032Speter				{
209138032Speter					switch (*p)
209238032Speter					{
209338032Speter					  case 'w':
209438032Speter						flags |= EF_WARNING;
209538032Speter						break;
209638032Speter					}
209738032Speter				}
209838032Speter			}
209938032Speter		}
210038032Speter		if (submittime == (time_t) 0)
210138032Speter			printf(" (no control file)");
210238032Speter		printf("\n");
210338032Speter		(void) fclose(f);
210438032Speter	}
210538032Speter}
210638032Speter
210738032Speter# endif /* QUEUE */
210838032Speter/*
210938032Speter**  QUEUENAME -- build a file name in the queue directory for this envelope.
211038032Speter**
211138032Speter**	Assigns an id code if one does not already exist.
211238032Speter**	This code is very careful to avoid trashing existing files
211338032Speter**	under any circumstances.
211438032Speter**
211538032Speter**	Parameters:
211638032Speter**		e -- envelope to build it in/from.
211738032Speter**		type -- the file type, used as the first character
211838032Speter**			of the file name.
211938032Speter**
212038032Speter**	Returns:
212138032Speter**		a pointer to the new file name (in a static buffer).
212238032Speter**
212338032Speter**	Side Effects:
212438032Speter**		If no id code is already assigned, queuename will
212538032Speter**		assign an id code, create a qf file, and leave a
212638032Speter**		locked, open-for-write file pointer in the envelope.
212738032Speter*/
212838032Speter
212938032Speter#ifndef ENOLCK
213038032Speter# define ENOLCK		-1
213138032Speter#endif
213238032Speter#ifndef ENOSPC
213338032Speter# define ENOSPC		-1
213438032Speter#endif
213538032Speter
213638032Speterchar *
213738032Speterqueuename(e, type)
213838032Speter	register ENVELOPE *e;
213938032Speter	int type;
214038032Speter{
214138032Speter	static pid_t pid = -1;
214238032Speter	static char c0;
214338032Speter	static char c1;
214438032Speter	static char c2;
214538032Speter	time_t now;
214638032Speter	struct tm *tm;
214738032Speter	static char buf[MAXNAME + 1];
214838032Speter
214938032Speter	if (e->e_id == NULL)
215038032Speter	{
215138032Speter		char qf[MAXQFNAME];
215238032Speter
215338032Speter		/* find a unique id */
215438032Speter		if (pid != getpid())
215538032Speter		{
215638032Speter			/* new process -- start back at "AA" */
215738032Speter			pid = getpid();
215838032Speter			now = curtime();
215938032Speter			tm = localtime(&now);
216038032Speter			c0 = 'A' + tm->tm_hour;
216138032Speter			c1 = 'A';
216238032Speter			c2 = 'A' - 1;
216338032Speter		}
216438032Speter		(void) snprintf(qf, sizeof qf, "qf%cAA%05d", c0, pid);
216538032Speter
216638032Speter		while (c1 < '~' || c2 < 'Z')
216738032Speter		{
216838032Speter			int i;
216938032Speter			int attempts = 0;
217038032Speter
217138032Speter			if (c2 >= 'Z')
217238032Speter			{
217338032Speter				c1++;
217438032Speter				c2 = 'A' - 1;
217538032Speter			}
217638032Speter			qf[3] = c1;
217738032Speter			qf[4] = ++c2;
217838032Speter			if (tTd(7, 20))
217938032Speter				printf("queuename: trying \"%s\"\n", qf);
218038032Speter
218138032Speter			i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode);
218238032Speter			if (i < 0)
218338032Speter			{
218438032Speter				if (errno == EEXIST)
218538032Speter					continue;
218638032Speter				syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)",
218738032Speter					qf, QueueDir, geteuid());
218838032Speter				exit(EX_UNAVAILABLE);
218938032Speter			}
219038032Speter			do
219138032Speter			{
219238032Speter				if (attempts > 0)
219338032Speter					sleep(attempts);
219438032Speter				e->e_lockfp = 0;
219538032Speter				if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB))
219638032Speter				{
219738032Speter					e->e_lockfp = fdopen(i, "w");
219838032Speter					break;
219938032Speter				}
220038032Speter			} while ((errno == ENOLCK || errno == ENOSPC) &&
220138032Speter				 attempts++ < 4);
220238032Speter
220338032Speter			/* Successful lock */
220438032Speter			if (e->e_lockfp != 0)
220538032Speter				break;
220638032Speter
220738032Speter#if !HASFLOCK
220838032Speter			if (errno != EAGAIN && errno != EACCES)
220938032Speter#else
221038032Speter			if (errno != EWOULDBLOCK)
221138032Speter#endif
221238032Speter			{
221338032Speter				syserr("queuename: Cannot lock \"%s\" in \"%s\" (euid=%d)",
221438032Speter					qf, QueueDir, geteuid());
221538032Speter				exit(EX_OSERR);
221638032Speter			}
221738032Speter
221838032Speter			/* a reader got the file; abandon it and try again */
221938032Speter			(void) close(i);
222038032Speter		}
222138032Speter		if (c1 >= '~' && c2 >= 'Z')
222238032Speter		{
222338032Speter			syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)",
222438032Speter				qf, QueueDir, geteuid());
222538032Speter			exit(EX_OSERR);
222638032Speter		}
222738032Speter		e->e_id = newstr(&qf[2]);
222838032Speter		define('i', e->e_id, e);
222938032Speter		if (tTd(7, 1))
223038032Speter			printf("queuename: assigned id %s, env=%lx\n",
223138032Speter			       e->e_id, (u_long) e);
223238032Speter		if (tTd(7, 9))
223338032Speter		{
223438032Speter			printf("  lockfd=");
223538032Speter			dumpfd(fileno(e->e_lockfp), TRUE, FALSE);
223638032Speter		}
223738032Speter		if (LogLevel > 93)
223838032Speter			sm_syslog(LOG_DEBUG, e->e_id, "assigned id");
223938032Speter	}
224038032Speter
224138032Speter	if (type == '\0')
224238032Speter		return (NULL);
224338032Speter	(void) snprintf(buf, sizeof buf, "%cf%s", type, e->e_id);
224438032Speter	if (tTd(7, 2))
224538032Speter		printf("queuename: %s\n", buf);
224638032Speter	return (buf);
224738032Speter}
224838032Speter/*
224938032Speter**  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
225038032Speter**
225138032Speter**	Parameters:
225238032Speter**		e -- the envelope to unlock.
225338032Speter**
225438032Speter**	Returns:
225538032Speter**		none
225638032Speter**
225738032Speter**	Side Effects:
225838032Speter**		unlocks the queue for `e'.
225938032Speter*/
226038032Speter
226138032Spetervoid
226238032Speterunlockqueue(e)
226338032Speter	ENVELOPE *e;
226438032Speter{
226538032Speter	if (tTd(51, 4))
226638032Speter		printf("unlockqueue(%s)\n",
226738032Speter			e->e_id == NULL ? "NOQUEUE" : e->e_id);
226838032Speter
226938032Speter	/* if there is a lock file in the envelope, close it */
227038032Speter	if (e->e_lockfp != NULL)
227138032Speter		xfclose(e->e_lockfp, "unlockqueue", e->e_id);
227238032Speter	e->e_lockfp = NULL;
227338032Speter
227438032Speter	/* don't create a queue id if we don't already have one */
227538032Speter	if (e->e_id == NULL)
227638032Speter		return;
227738032Speter
227838032Speter	/* remove the transcript */
227938032Speter	if (LogLevel > 87)
228038032Speter		sm_syslog(LOG_DEBUG, e->e_id, "unlock");
228138032Speter	if (!tTd(51, 104))
228238032Speter		xunlink(queuename(e, 'x'));
228338032Speter
228438032Speter}
228538032Speter/*
228638032Speter**  SETCTLUSER -- create a controlling address
228738032Speter**
228838032Speter**	Create a fake "address" given only a local login name; this is
228938032Speter**	used as a "controlling user" for future recipient addresses.
229038032Speter**
229138032Speter**	Parameters:
229238032Speter**		user -- the user name of the controlling user.
229338032Speter**		qfver -- the version stamp of this qf file.
229438032Speter**
229538032Speter**	Returns:
229638032Speter**		An address descriptor for the controlling user.
229738032Speter**
229838032Speter**	Side Effects:
229938032Speter**		none.
230038032Speter*/
230138032Speter
230238032SpeterADDRESS *
230338032Spetersetctluser(user, qfver)
230438032Speter	char *user;
230538032Speter	int qfver;
230638032Speter{
230738032Speter	register ADDRESS *a;
230838032Speter	struct passwd *pw;
230938032Speter	char *p;
231038032Speter
231138032Speter	/*
231238032Speter	**  See if this clears our concept of controlling user.
231338032Speter	*/
231438032Speter
231538032Speter	if (user == NULL || *user == '\0')
231638032Speter		return NULL;
231738032Speter
231838032Speter	/*
231938032Speter	**  Set up addr fields for controlling user.
232038032Speter	*/
232138032Speter
232238032Speter	a = (ADDRESS *) xalloc(sizeof *a);
232338032Speter	bzero((char *) a, sizeof *a);
232438032Speter
232538032Speter	if (*user == '\0')
232638032Speter	{
232738032Speter		p = NULL;
232838032Speter		a->q_user = newstr(DefUser);
232938032Speter	}
233038032Speter	else if (*user == ':')
233138032Speter	{
233238032Speter		p = &user[1];
233338032Speter		a->q_user = newstr(p);
233438032Speter	}
233538032Speter	else
233638032Speter	{
233738032Speter		p = strtok(user, ":");
233838032Speter		a->q_user = newstr(user);
233938032Speter		if (qfver >= 2)
234038032Speter		{
234138032Speter			if ((p = strtok(NULL, ":")) != NULL)
234238032Speter				a->q_uid = atoi(p);
234338032Speter			if ((p = strtok(NULL, ":")) != NULL)
234438032Speter				a->q_gid = atoi(p);
234538032Speter			if ((p = strtok(NULL, ":")) != NULL)
234638032Speter				a->q_flags |= QGOODUID;
234738032Speter		}
234838032Speter		else if ((pw = sm_getpwnam(user)) != NULL)
234938032Speter		{
235038032Speter			if (strcmp(pw->pw_dir, "/") == 0)
235138032Speter				a->q_home = "";
235238032Speter			else
235338032Speter				a->q_home = newstr(pw->pw_dir);
235438032Speter			a->q_uid = pw->pw_uid;
235538032Speter			a->q_gid = pw->pw_gid;
235638032Speter			a->q_flags |= QGOODUID;
235738032Speter		}
235838032Speter	}
235938032Speter
236038032Speter	a->q_flags |= QPRIMARY;		/* flag as a "ctladdr"  */
236138032Speter	a->q_mailer = LocalMailer;
236238032Speter	if (p == NULL)
236338032Speter		a->q_paddr = a->q_user;
236438032Speter	else
236538032Speter		a->q_paddr = newstr(p);
236638032Speter	return a;
236738032Speter}
236838032Speter/*
236938032Speter**  LOSEQFILE -- save the qf as Qf and try to let someone know
237038032Speter**
237138032Speter**	Parameters:
237238032Speter**		e -- the envelope (e->e_id will be used).
237338032Speter**		why -- reported to whomever can hear.
237438032Speter**
237538032Speter**	Returns:
237638032Speter**		none.
237738032Speter*/
237838032Speter
237938032Spetervoid
238038032Speterloseqfile(e, why)
238138032Speter	register ENVELOPE *e;
238238032Speter	char *why;
238338032Speter{
238438032Speter	char *p;
238538032Speter	char buf[MAXQFNAME + 1];
238638032Speter
238738032Speter	if (e == NULL || e->e_id == NULL)
238838032Speter		return;
238938032Speter	p = queuename(e, 'q');
239038032Speter	if (strlen(p) > MAXQFNAME)
239138032Speter	{
239238032Speter		syserr("loseqfile: queuename (%s) too long", p);
239338032Speter		return;
239438032Speter	}
239538032Speter	strcpy(buf, p);
239638032Speter	p = queuename(e, 'Q');
239738032Speter	if (rename(buf, p) < 0)
239838032Speter		syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid());
239938032Speter	else if (LogLevel > 0)
240038032Speter		sm_syslog(LOG_ALERT, e->e_id,
240138032Speter			"Losing %s: %s", buf, why);
240238032Speter}
2403