DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T q

⟦6fd0837bf⟧ TextFile

    Length: 37041 (0x90b1)
    Types: TextFile
    Names: »queue.c,v«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦bfebc70e2⟧ »EurOpenD3/mail/sendmail-5.65b+IDA-1.4.3.tar.Z« 
        └─⟦f9e35cd84⟧ 
            └─⟦this⟧ »sendmail/src/RCS/queue.c,v« 

TextFile

head	5.30;
branch	5.30.0;
access;
symbols
	UICSO:5.30.0
	VANILLA:5.30;
locks; strict;
comment	@ * @;


5.30
date	90.06.20.08.36.13;	author paul;	state Exp;
branches
	5.30.0.1;
next	;

5.30.0.1
date	90.06.20.09.43.38;	author paul;	state Exp;
branches;
next	5.30.0.2;

5.30.0.2
date	90.06.21.13.47.18;	author paul;	state Exp;
branches;
next	5.30.0.3;

5.30.0.3
date	90.06.25.09.21.02;	author paul;	state Exp;
branches;
next	5.30.0.4;

5.30.0.4
date	90.07.09.10.17.12;	author paul;	state Exp;
branches;
next	5.30.0.5;

5.30.0.5
date	90.10.13.18.57.10;	author paul;	state Exp;
branches;
next	5.30.0.6;

5.30.0.6
date	90.11.02.00.32.16;	author paul;	state Exp;
branches;
next	5.30.0.7;

5.30.0.7
date	90.11.13.15.20.44;	author paul;	state Exp;
branches;
next	5.30.0.8;

5.30.0.8
date	90.11.15.19.47.10;	author paul;	state Exp;
branches;
next	5.30.0.9;

5.30.0.9
date	90.11.24.16.33.54;	author paul;	state Exp;
branches;
next	5.30.0.10;

5.30.0.10
date	90.11.28.16.26.35;	author paul;	state Exp;
branches;
next	5.30.0.11;

5.30.0.11
date	90.12.11.15.20.41;	author paul;	state Exp;
branches;
next	5.30.0.12;

5.30.0.12
date	91.01.19.19.26.02;	author paul;	state Exp;
branches;
next	5.30.0.13;

5.30.0.13
date	91.02.01.04.49.26;	author paul;	state Exp;
branches;
next	5.30.0.14;

5.30.0.14
date	91.02.17.04.55.44;	author paul;	state Exp;
branches;
next	5.30.0.15;

5.30.0.15
date	91.03.05.21.13.17;	author paul;	state Exp;
branches;
next	5.30.0.16;

5.30.0.16
date	91.03.05.21.14.06;	author paul;	state Exp;
branches;
next	;


desc
@@


5.30
log
@5.64 Berkeley release
@
text
@/*
 * Copyright (c) 1983 Eric P. Allman
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted provided
 * that: (1) source distributions retain this entire copyright notice and
 * comment, and (2) distributions including binaries display the following
 * acknowledgement:  ``This product includes software developed by the
 * University of California, Berkeley and its contributors'' in the
 * documentation or other materials provided with the distribution and in
 * all advertising materials mentioning features or use of this software.
 * Neither the name of the University nor the names of its contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

# include "sendmail.h"

#ifndef lint
#ifdef QUEUE
static char sccsid[] = "@@(#)queue.c	5.30 (Berkeley) 6/1/90 (with queueing)";
#else
static char sccsid[] = "@@(#)queue.c	5.30 (Berkeley) 6/1/90 (without queueing)";
#endif
#endif /* not lint */

# include <sys/stat.h>
# include <sys/dir.h>
# include <sys/file.h>
# include <signal.h>
# include <errno.h>
# include <pwd.h>

# ifdef QUEUE

/*
**  Work queue.
*/

struct work
{
	char		*w_name;	/* name of control file */
	long		w_pri;		/* priority of message, see below */
	time_t		w_ctime;	/* creation time of message */
	struct work	*w_next;	/* next in queue */
};

typedef struct work	WORK;
extern int la;

WORK	*WorkQ;			/* queue of things to be done */
\f

/*
**  QUEUEUP -- queue a message up for future transmission.
**
**	Parameters:
**		e -- the envelope to queue up.
**		queueall -- if TRUE, queue all addresses, rather than
**			just those with the QQUEUEUP flag set.
**		announce -- if TRUE, tell when you are queueing up.
**
**	Returns:
**		locked FILE* to q file
**
**	Side Effects:
**		The current request are saved in a control file.
*/

FILE *
queueup(e, queueall, announce)
	register ENVELOPE *e;
	bool queueall;
	bool announce;
{
	char *qf;
	char buf[MAXLINE], tf[MAXLINE];
	register FILE *tfp;
	register HDR *h;
	register ADDRESS *q;
	MAILER nullmailer;
	int fd, ret;

	/*
	**  Create control file.
	*/

	do {
		strcpy(tf, queuename(e, 't'));
		fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode);
		if (fd < 0) {
			if ( errno != EEXIST) {
				syserr("queueup: cannot create temp file %s",
					tf);
				return NULL;
			}
		} else {
			if (flock(fd, LOCK_EX|LOCK_NB) < 0) {
				if (errno != EWOULDBLOCK)
					syserr("cannot flock(%s)", tf);
				close(fd);
				fd = -1;
			}
		}
	} while (fd < 0);

	tfp = fdopen(fd, "w");

	if (tTd(40, 1))
		printf("queueing %s\n", e->e_id);

	/*
	**  If there is no data file yet, create one.
	*/

	if (e->e_df == NULL)
	{
		register FILE *dfp;
		extern putbody();

		e->e_df = newstr(queuename(e, 'd'));
		fd = open(e->e_df, O_WRONLY|O_CREAT, FileMode);
		if (fd < 0)
		{
			syserr("queueup: cannot create %s", e->e_df);
			(void) fclose(tfp);
			return NULL;
		}
		dfp = fdopen(fd, "w");
		(*e->e_putbody)(dfp, ProgMailer, e);
		(void) fclose(dfp);
		e->e_putbody = putbody;
	}

	/*
	**  Output future work requests.
	**	Priority and creation time should be first, since
	**	they are required by orderq.
	*/

	/* output message priority */
	fprintf(tfp, "P%ld\n", e->e_msgpriority);

	/* output creation time */
	fprintf(tfp, "T%ld\n", e->e_ctime);

	/* output name of data file */
	fprintf(tfp, "D%s\n", e->e_df);

	/* message from envelope, if it exists */
	if (e->e_message != NULL)
		fprintf(tfp, "M%s\n", e->e_message);

	/* output name of sender */
	fprintf(tfp, "S%s\n", e->e_from.q_paddr);

	/* output list of recipient addresses */
	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
	{
		if (queueall ? !bitset(QDONTSEND, q->q_flags) :
			       bitset(QQUEUEUP, q->q_flags))
		{
			char *ctluser, *getctluser();

			if ((ctluser = getctluser(q)) != NULL)
				fprintf(tfp, "C%s\n", ctluser);
			fprintf(tfp, "R%s\n", q->q_paddr);
			if (announce)
			{
				e->e_to = q->q_paddr;
				message(Arpa_Info, "queued");
				if (LogLevel > 4)
					logdelivery("queued");
				e->e_to = NULL;
			}
			if (tTd(40, 1))
			{
				printf("queueing ");
				printaddr(q, FALSE);
			}
		}
	}

	/* output list of error recipients */
	for (q = e->e_errorqueue; q != NULL; q = q->q_next)
	{
		if (!bitset(QDONTSEND, q->q_flags))
		{
			char *ctluser, *getctluser();

			if ((ctluser = getctluser(q)) != NULL)
				fprintf(tfp, "C%s\n", ctluser);
			fprintf(tfp, "E%s\n", q->q_paddr);
		}
	}

	/*
	**  Output headers for this message.
	**	Expand macros completely here.  Queue run will deal with
	**	everything as absolute headers.
	**		All headers that must be relative to the recipient
	**		can be cracked later.
	**	We set up a "null mailer" -- i.e., a mailer that will have
	**	no effect on the addresses as they are output.
	*/

	bzero((char *) &nullmailer, sizeof nullmailer);
	nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1;
	nullmailer.m_eol = "\n";

	define('g', "\001f", e);
	for (h = e->e_header; h != NULL; h = h->h_link)
	{
		extern bool bitzerop();

		/* don't output null headers */
		if (h->h_value == NULL || h->h_value[0] == '\0')
			continue;

		/* don't output resent headers on non-resent messages */
		if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
			continue;

		/* output this header */
		fprintf(tfp, "H");

		/* if conditional, output the set of conditions */
		if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags))
		{
			int j;

			(void) putc('?', tfp);
			for (j = '\0'; j <= '\177'; j++)
				if (bitnset(j, h->h_mflags))
					(void) putc(j, tfp);
			(void) putc('?', tfp);
		}

		/* output the header: expand macros, convert addresses */
		if (bitset(H_DEFAULT, h->h_flags))
		{
			(void) expand(h->h_value, buf, &buf[sizeof buf], e);
			fprintf(tfp, "%s: %s\n", h->h_field, buf);
		}
		else if (bitset(H_FROM|H_RCPT, h->h_flags))
		{
			commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags),
				 &nullmailer);
		}
		else
			fprintf(tfp, "%s: %s\n", h->h_field, h->h_value);
	}

	/*
	**  Clean up.
	*/

	qf = queuename(e, 'q');
	if (rename(tf, qf) < 0)
		syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df);
	errno = 0;

# ifdef LOG
	/* save log info */
	if (LogLevel > 15)
		syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
# endif LOG
	fflush(tfp);
	return tfp;
}
\f

/*
**  RUNQUEUE -- run the jobs in the queue.
**
**	Gets the stuff out of the queue in some presumably logical
**	order and processes them.
**
**	Parameters:
**		forkflag -- TRUE if the queue scanning should be done in
**			a child process.  We double-fork so it is not our
**			child and we don't have to clean up after it.
**
**	Returns:
**		none.
**
**	Side Effects:
**		runs things in the mail queue.
*/

runqueue(forkflag)
	bool forkflag;
{
	extern bool shouldqueue();

	/*
	**  If no work will ever be selected, don't even bother reading
	**  the queue.
	*/

	la = getla();	/* get load average */

	if (shouldqueue(-100000000L))
	{
		if (Verbose)
			printf("Skipping queue run -- load average too high\n");

		if (forkflag)
			return;
		finis();
	}

	/*
	**  See if we want to go off and do other useful work.
	*/

	if (forkflag)
	{
		int pid;

		pid = dofork();
		if (pid != 0)
		{
			extern reapchild();

			/* parent -- pick up intermediate zombie */
#ifndef SIGCHLD
			(void) waitfor(pid);
#else SIGCHLD
			(void) signal(SIGCHLD, reapchild);
#endif SIGCHLD
			if (QueueIntvl != 0)
				(void) setevent(QueueIntvl, runqueue, TRUE);
			return;
		}
		/* child -- double fork */
#ifndef SIGCHLD
		if (fork() != 0)
			exit(EX_OK);
#else SIGCHLD
		(void) signal(SIGCHLD, SIG_DFL);
#endif SIGCHLD
	}

	setproctitle("running queue: %s", QueueDir);

# ifdef LOG
	if (LogLevel > 11)
		syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid());
# endif LOG

	/*
	**  Release any resources used by the daemon code.
	*/

# ifdef DAEMON
	clrdaemon();
# endif DAEMON

	/*
	**  Make sure the alias database is open.
	*/

	initaliases(AliasFile, FALSE);

	/*
	**  Start making passes through the queue.
	**	First, read and sort the entire queue.
	**	Then, process the work in that order.
	**		But if you take too long, start over.
	*/

	/* order the existing work requests */
	(void) orderq(FALSE);

	/* process them once at a time */
	while (WorkQ != NULL)
	{
		WORK *w = WorkQ;

		WorkQ = WorkQ->w_next;
		dowork(w);
		free(w->w_name);
		free((char *) w);
	}

	/* exit without the usual cleanup */
	exit(ExitStat);
}
\f

/*
**  ORDERQ -- order the work queue.
**
**	Parameters:
**		doall -- if set, include everything in the queue (even
**			the jobs that cannot be run because the load
**			average is too high).  Otherwise, exclude those
**			jobs.
**
**	Returns:
**		The number of request in the queue (not necessarily
**		the number of requests in WorkQ however).
**
**	Side Effects:
**		Sets WorkQ to the queue of available work, in order.
*/

# define NEED_P		001
# define NEED_T		002

orderq(doall)
	bool doall;
{
	register struct direct *d;
	register WORK *w;
	DIR *f;
	register int i;
	WORK wlist[QUEUESIZE+1];
	int wn = -1;
	extern workcmpf();

	/* clear out old WorkQ */
	for (w = WorkQ; w != NULL; )
	{
		register WORK *nw = w->w_next;

		WorkQ = nw;
		free(w->w_name);
		free((char *) w);
		w = nw;
	}

	/* open the queue directory */
	f = opendir(".");
	if (f == NULL)
	{
		syserr("orderq: cannot open \"%s\" as \".\"", QueueDir);
		return (0);
	}

	/*
	**  Read the work directory.
	*/

	while ((d = readdir(f)) != NULL)
	{
		FILE *cf;
		char lbuf[MAXNAME];

		/* is this an interesting entry? */
		if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
			continue;

		/* yes -- open control file (if not too many files) */
		if (++wn >= QUEUESIZE)
			continue;
		cf = fopen(d->d_name, "r");
		if (cf == NULL)
		{
			/* this may be some random person sending hir msgs */
			/* syserr("orderq: cannot open %s", cbuf); */
			if (tTd(41, 2))
				printf("orderq: cannot open %s (%d)\n",
					d->d_name, errno);
			errno = 0;
			wn--;
			continue;
		}
		w = &wlist[wn];
		w->w_name = newstr(d->d_name);

		/* make sure jobs in creation don't clog queue */
		w->w_pri = 0x7fffffff;
		w->w_ctime = 0;

		/* extract useful information */
		i = NEED_P | NEED_T;
		while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
		{
			extern long atol();

			switch (lbuf[0])
			{
			  case 'P':
				w->w_pri = atol(&lbuf[1]);
				i &= ~NEED_P;
				break;

			  case 'T':
				w->w_ctime = atol(&lbuf[1]);
				i &= ~NEED_T;
				break;
			}
		}
		(void) fclose(cf);

		if (!doall && shouldqueue(w->w_pri))
		{
			/* don't even bother sorting this job in */
			wn--;
		}
	}
	(void) closedir(f);
	wn++;

	/*
	**  Sort the work directory.
	*/

	qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf);

	/*
	**  Convert the work list into canonical form.
	**	Should be turning it into a list of envelopes here perhaps.
	*/

	WorkQ = NULL;
	for (i = min(wn, QUEUESIZE); --i >= 0; )
	{
		w = (WORK *) xalloc(sizeof *w);
		w->w_name = wlist[i].w_name;
		w->w_pri = wlist[i].w_pri;
		w->w_ctime = wlist[i].w_ctime;
		w->w_next = WorkQ;
		WorkQ = w;
	}

	if (tTd(40, 1))
	{
		for (w = WorkQ; w != NULL; w = w->w_next)
			printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
	}

	return (wn);
}
\f

/*
**  WORKCMPF -- compare function for ordering work.
**
**	Parameters:
**		a -- the first argument.
**		b -- the second argument.
**
**	Returns:
**		-1 if a < b
**		 0 if a == b
**		+1 if a > b
**
**	Side Effects:
**		none.
*/

workcmpf(a, b)
	register WORK *a;
	register WORK *b;
{
	long pa = a->w_pri + a->w_ctime;
	long pb = b->w_pri + b->w_ctime;

	if (pa == pb)
		return (0);
	else if (pa > pb)
		return (1);
	else
		return (-1);
}
\f

/*
**  DOWORK -- do a work request.
**
**	Parameters:
**		w -- the work request to be satisfied.
**
**	Returns:
**		none.
**
**	Side Effects:
**		The work request is satisfied if possible.
*/

dowork(w)
	register WORK *w;
{
	register int i;
	extern bool shouldqueue();

	if (tTd(40, 1))
		printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);

	/*
	**  Ignore jobs that are too expensive for the moment.
	*/

	if (shouldqueue(w->w_pri))
	{
		if (Verbose)
			printf("\nSkipping %s\n", w->w_name + 2);
		return;
	}

	/*
	**  Fork for work.
	*/

	if (ForkQueueRuns)
	{
		i = fork();
		if (i < 0)
		{
			syserr("dowork: cannot fork");
			return;
		}
	}
	else
	{
		i = 0;
	}

	if (i == 0)
	{
		FILE *qflock, *readqf();
		/*
		**  CHILD
		**	Lock the control file to avoid duplicate deliveries.
		**		Then run the file as though we had just read it.
		**	We save an idea of the temporary name so we
		**		can recover on interrupt.
		*/

		/* set basic modes, etc. */
		(void) alarm(0);
		clearenvelope(CurEnv, FALSE);
		QueueRun = TRUE;
		ErrorMode = EM_MAIL;
		CurEnv->e_id = &w->w_name[2];
# ifdef LOG
		if (LogLevel > 11)
			syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id,
			       getpid());
# endif LOG

		/* don't use the headers from sendmail.cf... */
		CurEnv->e_header = NULL;

		/* read the queue control file */
		/*  and lock the control file during processing */
		if ((qflock=readqf(CurEnv, TRUE)) == NULL)
		{
			if (ForkQueueRuns)
				exit(EX_OK);
			else
				return;
		}

		CurEnv->e_flags |= EF_INQUEUE;
		eatheader(CurEnv);

		/* do the delivery */
		if (!bitset(EF_FATALERRS, CurEnv->e_flags))
			sendall(CurEnv, SM_DELIVER);

		/* finish up and exit */
		if (ForkQueueRuns)
			finis();
		else
			dropenvelope(CurEnv);
		fclose(qflock);
	}
	else
	{
		/*
		**  Parent -- pick up results.
		*/

		errno = 0;
		(void) waitfor(i);
	}
}
\f

/*
**  READQF -- read queue file and set up environment.
**
**	Parameters:
**		e -- the envelope of the job to run.
**		full -- if set, read in all information.  Otherwise just
**			read in info needed for a queue print.
**
**	Returns:
**		FILE * pointing to flock()ed fd so it can be closed
**		after the mail is delivered
**
**	Side Effects:
**		cf is read and created as the current job, as though
**		we had been invoked by argument.
*/

FILE *
readqf(e, full)
	register ENVELOPE *e;
	bool full;
{
	char *qf;
	register FILE *qfp;
	char buf[MAXFIELD];
	extern char *fgetfolded();
	extern long atol();
	int gotctluser = 0;
	int fd;

	/*
	**  Read and process the file.
	*/

	qf = queuename(e, 'q');
	qfp = fopen(qf, "r");
	if (qfp == NULL)
	{
		if (errno != ENOENT)
			syserr("readqf: no control file %s", qf);
		return NULL;
	}

	if (flock(fileno(qfp), LOCK_EX|LOCK_NB) < 0)
	{
# ifdef LOG
		/* being processed by another queuer */
		if (Verbose)
			printf("%s: locked\n", CurEnv->e_id);
# endif LOG
		(void) fclose(qfp);
		return NULL;
	}

	/* do basic system initialization */
	initsys();

	FileName = qf;
	LineNumber = 0;
	if (Verbose && full)
		printf("\nRunning %s\n", e->e_id);
	while (fgetfolded(buf, sizeof buf, qfp) != NULL)
	{
		if (tTd(40, 4))
			printf("+++++ %s\n", buf);
		switch (buf[0])
		{
		  case 'C':		/* specify controlling user */
			setctluser(&buf[1]);
			gotctluser = 1;
			break;

		  case 'R':		/* specify recipient */
			sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue);
			break;

		  case 'E':		/* specify error recipient */
			sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_errorqueue);
			break;

		  case 'H':		/* header */
			if (full)
				(void) chompheader(&buf[1], FALSE);
			break;

		  case 'M':		/* message */
			e->e_message = newstr(&buf[1]);
			break;

		  case 'S':		/* sender */
			setsender(newstr(&buf[1]));
			break;

		  case 'D':		/* data file name */
			if (!full)
				break;
			e->e_df = newstr(&buf[1]);
			e->e_dfp = fopen(e->e_df, "r");
			if (e->e_dfp == NULL)
				syserr("readqf: cannot open %s", e->e_df);
			break;

		  case 'T':		/* init time */
			e->e_ctime = atol(&buf[1]);
			break;

		  case 'P':		/* message priority */
			e->e_msgpriority = atol(&buf[1]) + WkTimeFact;
			break;

		  case '\0':		/* blank line; ignore */
			break;

		  default:
			syserr("readqf(%s:%d): bad line \"%s\"", e->e_id,
				LineNumber, buf);
			break;
		}
		/*
		**  The `C' queue file command operates on the next line,
		**  so we use "gotctluser" to maintain state as follows:
		**      0 - no controlling user,
		**      1 - controlling user has been set but not used,
		**      2 - controlling user must be used on next iteration.
		*/
		if (gotctluser == 1)
			gotctluser++;
		else if (gotctluser == 2)
		{
			clrctluser();
			gotctluser = 0;
		}
	}

	/* clear controlling user in case we break out prematurely */
	clrctluser();

	FileName = NULL;

	/*
	**  If we haven't read any lines, this queue file is empty.
	**  Arrange to remove it without referencing any null pointers.
	*/

	if (LineNumber == 0)
	{
		errno = 0;
		e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
	}
	return qfp;
}
\f

/*
**  PRINTQUEUE -- print out a representation of the mail queue
**
**	Parameters:
**		none.
**
**	Returns:
**		none.
**
**	Side Effects:
**		Prints a listing of the mail queue on the standard output.
*/

printqueue()
{
	register WORK *w;
	FILE *f;
	int nrequests;
	char buf[MAXLINE];
	char cbuf[MAXLINE];

	/*
	**  Read and order the queue.
	*/

	nrequests = orderq(TRUE);

	/*
	**  Print the work list that we have read.
	*/

	/* first see if there is anything */
	if (nrequests <= 0)
	{
		printf("Mail queue is empty\n");
		return;
	}

	la = getla();	/* get load average */

	printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
	if (nrequests > QUEUESIZE)
		printf(", only %d printed", QUEUESIZE);
	if (Verbose)
		printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n");
	else
		printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
	for (w = WorkQ; w != NULL; w = w->w_next)
	{
		struct stat st;
		auto time_t submittime = 0;
		long dfsize = -1;
		char message[MAXLINE];
		extern bool shouldqueue();

		f = fopen(w->w_name, "r");
		if (f == NULL)
		{
			errno = 0;
			continue;
		}
		printf("%7s", w->w_name + 2);
		if (flock(fileno(f), LOCK_SH|LOCK_NB) < 0)
			printf("*");
		else if (shouldqueue(w->w_pri))
			printf("X");
		else
			printf(" ");
		errno = 0;

		message[0] = '\0';
		cbuf[0] = '\0';
		while (fgets(buf, sizeof buf, f) != NULL)
		{
			fixcrlf(buf, TRUE);
			switch (buf[0])
			{
			  case 'M':	/* error message */
				(void) strcpy(message, &buf[1]);
				break;

			  case 'S':	/* sender name */
				if (Verbose)
					printf("%8ld %10ld %.12s %.38s", dfsize,
					    w->w_pri, ctime(&submittime) + 4,
					    &buf[1]);
				else
					printf("%8ld %.16s %.45s", dfsize,
					    ctime(&submittime), &buf[1]);
				if (message[0] != '\0')
					printf("\n\t\t (%.60s)", message);
				break;
			  case 'C':	/* controlling user */
				if (strlen(buf) < MAXLINE-3)	/* sanity */
					(void) strcat(buf, ") ");
				cbuf[0] = cbuf[1] = '(';
				(void) strncpy(&cbuf[2], &buf[1], MAXLINE-1);
				cbuf[MAXLINE-1] = '\0';
				break;

			  case 'R':	/* recipient name */
				if (cbuf[0] != '\0') {
					/* prepend controlling user to `buf' */
					(void) strncat(cbuf, &buf[1],
					              MAXLINE-strlen(cbuf));
					cbuf[MAXLINE-1] = '\0';
					(void) strcpy(buf, cbuf);
					cbuf[0] = '\0';
				}
				if (Verbose)
					printf("\n\t\t\t\t\t %.38s", &buf[1]);
				else
					printf("\n\t\t\t\t  %.45s", &buf[1]);
				break;

			  case 'T':	/* creation time */
				submittime = atol(&buf[1]);
				break;

			  case 'D':	/* data file name */
				if (stat(&buf[1], &st) >= 0)
					dfsize = st.st_size;
				break;
			}
		}
		if (submittime == (time_t) 0)
			printf(" (no control file)");
		printf("\n");
		(void) fclose(f);
	}
}

# endif QUEUE
\f

/*
**  QUEUENAME -- build a file name in the queue directory for this envelope.
**
**	Assigns an id code if one does not already exist.
**	This code is very careful to avoid trashing existing files
**	under any circumstances.
**
**	Parameters:
**		e -- envelope to build it in/from.
**		type -- the file type, used as the first character
**			of the file name.
**
**	Returns:
**		a pointer to the new file name (in a static buffer).
**
**	Side Effects:
**		Will create the qf file if no id code is
**		already assigned.  This will cause the envelope
**		to be modified.
*/

char *
queuename(e, type)
	register ENVELOPE *e;
	char type;
{
	static char buf[MAXNAME];
	static int pid = -1;
	char c1 = 'A';
	char c2 = 'A';

	if (e->e_id == NULL)
	{
		char qf[20];

		/* find a unique id */
		if (pid != getpid())
		{
			/* new process -- start back at "AA" */
			pid = getpid();
			c1 = 'A';
			c2 = 'A' - 1;
		}
		(void) sprintf(qf, "qfAA%05d", pid);

		while (c1 < '~' || c2 < 'Z')
		{
			int i;

			if (c2 >= 'Z')
			{
				c1++;
				c2 = 'A' - 1;
			}
			qf[2] = c1;
			qf[3] = ++c2;
			if (tTd(7, 20))
				printf("queuename: trying \"%s\"\n", qf);

			i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode);
			if (i < 0) {
				if (errno != EEXIST) {
					syserr("queuename: Cannot create \"%s\" in \"%s\"",
						qf, QueueDir);
					exit(EX_UNAVAILABLE);
				}
			} else {
				(void) close(i);
				break;
			}
		}
		if (c1 >= '~' && c2 >= 'Z')
		{
			syserr("queuename: Cannot create \"%s\" in \"%s\"",
				qf, QueueDir);
			exit(EX_OSERR);
		}
		e->e_id = newstr(&qf[2]);
		define('i', e->e_id, e);
		if (tTd(7, 1))
			printf("queuename: assigned id %s, env=%x\n", e->e_id, e);
# ifdef LOG
		if (LogLevel > 16)
			syslog(LOG_DEBUG, "%s: assigned id", e->e_id);
# endif LOG
	}

	if (type == '\0')
		return (NULL);
	(void) sprintf(buf, "%cf%s", type, e->e_id);
	if (tTd(7, 2))
		printf("queuename: %s\n", buf);
	return (buf);
}
\f

/*
**  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
**
**	Parameters:
**		e -- the envelope to unlock.
**
**	Returns:
**		none
**
**	Side Effects:
**		unlocks the queue for `e'.
*/

unlockqueue(e)
	ENVELOPE *e;
{
	/* remove the transcript */
# ifdef LOG
	if (LogLevel > 19)
		syslog(LOG_DEBUG, "%s: unlock", e->e_id);
# endif LOG
	if (!tTd(51, 4))
		xunlink(queuename(e, 'x'));

}
\f

/*
**  GETCTLUSER -- return controlling user if mailing to prog or file
**
**	Check for a "|" or "/" at the beginning of the address.  If
**	found, return a controlling username.
**
**	Parameters:
**		a - the address to check out
**
**	Returns:
**		Either NULL, if we werent mailing to a program or file,
**		or a controlling user name (possibly in getpwuid's
**		static buffer).
**
**	Side Effects:
**		none.
*/

char *
getctluser(a)
	ADDRESS *a;
{
	extern ADDRESS *getctladdr();
	struct passwd *pw;
	char *retstr;

	/*
	**  Get unquoted user for file, program or user.name check.
	**  N.B. remove this code block to always emit controlling
	**  addresses (at the expense of backward compatibility).
	*/

	{
		char buf[MAXNAME];
		(void) strncpy(buf, a->q_paddr, MAXNAME);
		buf[MAXNAME-1] = '\0';
		stripquotes(buf, TRUE);

		if (buf[0] != '|' && buf[0] != '/')
			return((char *)NULL);
	}

	a = getctladdr(a);		/* find controlling address */

	if (a != NULL && a->q_uid != 0 && (pw = getpwuid(a->q_uid)) != NULL)
		retstr = pw->pw_name;
	else				/* use default user */
		retstr = DefUser;

	if (tTd(40, 5))
		printf("Set controlling user for `%s' to `%s'\n",
		       (a == NULL)? "<null>": a->q_paddr, retstr);

	return(retstr);
}
\f

/*
**  SETCTLUSER - sets `CtlUser' to controlling user
**  CLRCTLUSER - clears controlling user (no params, nothing returned)
**
**	These routines manipulate `CtlUser'.
**
**	Parameters:
**		str  - controlling user as passed to setctluser()
**
**	Returns:
**		None.
**
**	Side Effects:
**		`CtlUser' is changed.
*/

static char CtlUser[MAXNAME];

setctluser(str)
register char *str;
{
	(void) strncpy(CtlUser, str, MAXNAME);
	CtlUser[MAXNAME-1] = '\0';
}

clrctluser()
{
	CtlUser[0] = '\0';
}

\f

/*
**  SETCTLADDR -- create a controlling address
**
**	If global variable `CtlUser' is set and we are given a valid
**	address, make that address a controlling address; change the
**	`q_uid', `q_gid', and `q_ruser' fields and set QGOODUID.
**
**	Parameters:
**		a - address for which control uid/gid info may apply
**
**	Returns:
**		None.	
**
**	Side Effects:
**		Fills in uid/gid fields in address and sets QGOODUID
**		flag if appropriate.
*/

setctladdr(a)
	ADDRESS *a;
{
	struct passwd *pw;

	/*
	**  If there is no current controlling user, or we were passed a
	**  NULL addr ptr or we already have a controlling user, return.
	*/

	if (CtlUser[0] == '\0' || a == NULL || a->q_ruser)
		return;

	/*
	**  Set up addr fields for controlling user.  If `CtlUser' is no
	**  longer valid, use the default user/group.
	*/

	if ((pw = getpwnam(CtlUser)) != NULL)
	{
		if (a->q_home)
			free(a->q_home);
		a->q_home = newstr(pw->pw_dir);
		a->q_uid = pw->pw_uid;
		a->q_gid = pw->pw_gid;
		a->q_ruser = newstr(CtlUser);
	}
	else
	{
		a->q_uid = DefUid;
		a->q_gid = DefGid;
		a->q_ruser = newstr(DefUser);
	}

	a->q_flags |= QGOODUID;		/* flag as a "ctladdr"  */

	if (tTd(40, 5))
		printf("Restored controlling user for `%s' to `%s'\n",
		       a->q_paddr, a->q_ruser);
}
@


5.30.0.1
log
@IDA patches
@
text
@d210 1
a210 2
	nullmailer.m_re_rwset = nullmailer.m_se_rwset = -1;
	nullmailer.m_rh_rwset = nullmailer.m_sh_rwset = -1;
d269 1
a269 1
# endif /* LOG */
d329 1
a329 1
#else /* SIGCHLD */
d331 1
a331 1
#endif /* !SIGCHLD */
d340 1
a340 1
#else /* SIGCHLD */
d342 1
a342 1
#endif /* !SIGCHLD */
d350 1
a350 1
# endif /* LOG */
d358 1
a358 1
# endif /* DAEMON */
d364 1
a364 1
	initaliases(FALSE);
d637 1
a637 1
# endif /* LOG */
d959 1
a959 1
# endif /* QUEUE */
d1044 1
a1044 1
# endif /* LOG */
d1074 1
a1074 1
# endif /* LOG */
@


5.30.0.2
log
@Date: Wed, 20 Jun 90 15:39:37 -0400 (EDT)
From: Craig_Everhart@@transarc.com
To: paul@@uxc.cso.uiuc.edu (Paul Pomes - UofIllinois CSO)
Subject: Re: Sendmail V5.64 + IDA enhancements available for anon-FTP

Here's a sendmail patch for you that I've been sending around.  I've
been running it for years, and it was only when Peter Neumann had such
trouble with RISKS duplications that I decided to try to propagate it.

The problem addressed by the fix is that if sendmail delivers to a long
address list (in a single request) but crashes in the middle, it has no
record of the successful deliveries that it has made so far, so when it
eventually gets around to retrying that request, it re-delivers to all
the addresses to which it had been able to deliver earlier.  Not only
does this generate duplicate mail, but the re-delivery process also
requires additional queue processing time, so that it might continue to
take a long time even to attempt delivery to the last address in the
list.

The solution that this patch builds is simple and not very expensive:
after every successful mail delivery, the qf* file is rewritten,
omitting the recipient to whom delivery was successful.  The qf* file
gets shorter even as the request is processed.  A crash in the middle of
processing the list will result in duplicate delivery to at most one
host.  It doesn't require that much time to rewrite the qf* file;
certainly less time than the delivery itself takes.

The only real disadvantage I can imagine is that my sendmail sources
have diverged in many ways from a UCB version of about 1986. 
Nonetheless, it shouldn't be difficult to install this change.

As you can see, this change works by adding an additional flag parameter
to queueup(), then calling queueup() (from sendall()) with this
parameter set sometimes.  The queueup() flag parameter controls which
addresses are rewritten, looking at different flags.

So here's the patch.  I hope that you find it useful.  I look forward to
your reply.

		Thanks,
		Craig Everhart
@
text
@a63 4
** (CFE)		Checkpoint -- if TRUE, we're doing a checkpoint of a
**			list currently being delivered.  Write the
**			addresses not yet tried as well as the queued
**			addresses.
d73 1
a73 1
queueup(e, queueall, announce, Checkpoint)
a76 1
	bool Checkpoint;
d162 2
a163 12
		int writeit;

		if (Checkpoint) {	/* Delete the already-sent addresses. */
			writeit = TRUE;
			if (bitset(QDONTSEND, q->q_flags)
			  && !bitset(QQUEUEUP, q->q_flags)
			  && !bitset(QBADADDR, q->q_flags)) writeit = FALSE;
		} else if (queueall)
			writeit = !bitset(QDONTSEND, q->q_flags);
		else
			writeit = bitset(QQUEUEUP, q->q_flags);
		if (writeit)
@


5.30.0.3
log
@Patches for HP-UX from Andy Linton <root@@comp.vuw.ac.nz>.  Thanks Andy!
@
text
@a726 7
#if defined(hpux)
        /*
         * We can't get an exclusive lock on a file that
         * isn't opened for writing - sigh!
         */
	qfp = fopen(qf, "r+");
#else
a727 1
#endif /* hpux */
@


5.30.0.4
log
@Dropped re-write of queue file due to bug w. too many open files.  This
isn't important to us and so will be dropped for now.

@
text
@d64 4
d77 1
a77 1
queueup(e, queueall, announce)
d81 1
d167 12
a178 2
		if (queueall ? !bitset(QDONTSEND, q->q_flags) :
			       bitset(QQUEUEUP, q->q_flags))
@


5.30.0.5
log
@Bruce Lilly (bruce%balilly@@sonyd1.broadcast.sony.com) provided System 5
support, corrected several typos, and deleted extraneous newlines from
syslog() calls.  Re-did #statement indentation.  Made selection of
lockf() emulation of flock() more transparent: changed file open of
files to be exclusively locked from "r" (read-only) to "r+" (read/write);
added EAGAIN test to EWOULDBLOCK tests.  Fixed fencepost bug in third
expand() argument.
@
text
@d21 1
a21 2
#include <signal.h>
#include "sendmail.h"
d24 1
a24 1
# ifdef QUEUE
d26 1
a26 1
# else /* !QUEUE */
d28 1
a28 1
# endif /* QUEUE */
d31 1
a31 2
#include <sys/stat.h>
#ifndef SYSTEM5
d33 4
a36 7
#endif /* !SYSTEM5 */
#include <sys/file.h>
#ifndef LOCK_EX
# include "flock.h"
#endif /* !LOCK_EX */
#include <errno.h>
#include <pwd.h>
d38 1
a38 1
#ifdef QUEUE
d101 1
a101 1
				if (errno != EWOULDBLOCK && errno != EAGAIN)
a103 2
				unlink(tf);
				sleep(1);
d109 1
a109 2
	if ((tfp = fdopen(fd, "w")) == NULL)
		syserr("cannot fdopen(%s)", tf);
d131 1
a131 2
		if ((dfp = fdopen(fd, "w")) == NULL)
			syserr("cannot fdopen(%s)", e->e_df);
d245 1
a245 1
			(void) expand(h->h_value, buf, &buf[(sizeof(buf)-1)], e);
d269 1
a269 1
		syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s", e->e_id, qf, e->e_df);
d328 1
a328 1
# ifndef SIGCHLD
d330 1
a330 1
# else /* SIGCHLD */
d332 1
a332 1
# endif /* !SIGCHLD */
d338 1
a338 1
# ifndef SIGCHLD
d341 1
a341 1
# else /* SIGCHLD */
d343 1
a343 1
# endif /* !SIGCHLD */
d460 1
a460 1
			/* this may be some random person sending their msgs */
d712 1
d714 2
a715 2
         * We can't get an exclusive lock on a file that isn't opened for
	 * writing on most systems - sigh!
d718 3
d968 1
a968 1
#endif /* QUEUE */
d1050 1
a1050 1
#ifdef LOG
d1053 1
a1053 1
#endif /* LOG */
d1080 1
a1080 1
#ifdef LOG
d1083 1
a1083 1
#endif /* LOG */
@


5.30.0.6
log
@Save the values of the '$r' and '$s' macros in the queueing file.  Since
some parts of the parsing depend on these (and have since IDA-1.2.x),
this just makes the rewrite rules more consistent between items that are
immediately deliverable and items that must be queued for later.  I made
the character that marks the queueing record part of the '#define' so it can
be changed if some future changes from Berkeley should ever cause a conflict.

Neil Rickert (rickert@@cs.niu.edu)
@
text
@a265 14
#ifdef QUEUE_MACVALUE /* Save some macro values in the queue file */
	{
		char *p, *q;
		extern char *macvalue();

		for (p = "rs"; *p; p++)
		{
			q = macvalue(*p, CurEnv);
			if (q != NULL && *q)
				fprintf(tfp, "%c%c%s\n", QUEUE_MACVALUE, *p, q);
		}
	}
#endif /* QUEUE_MACVALUE */

a798 6

#ifdef QUEUE_MACVALUE /* if macro values saved in queue file, restore them */
		  case QUEUE_MACVALUE:
			setoption('M', &buf[1], FALSE, TRUE);
			break;
#endif /* QUEUE_MACVALUE */
@


5.30.0.7
log
@Changed #ifdef SYSTEM5 to SYSV to be consistent with current usage.  Moved
#include <sys/file.h> to sendmail.h .
@
text
@d33 1
a33 1
#ifndef SYSV
d35 5
a39 1
#endif /* !SYSV */
@


5.30.0.8
log
@Add calls to unlink tempfile before queueup() gives up and returns NULL.
Added syserr() and sleep() to do loop for case when tempfile exists.  If
an old tempfile somehow is there, the loop will spin very fast otherwise
w.o. saying why.  
@
text
@a99 5
			else {
				syserr("queueup: temp file %s exists, sleeping",
					tf);
				sleep(30);
			}
d105 2
a107 1
				sleep(1);
a132 1
			(void) unlink(tf);
@


5.30.0.9
log
@Commented out tokens following #endif's.
@
text
@d267 1
a267 1
# ifdef QUEUE_MACVALUE /* Save some macro values in the queue file */
d279 1
a279 1
# endif /* QUEUE_MACVALUE */
d754 1
a754 1
# endif /* LOG */
d815 1
a815 1
# ifdef QUEUE_MACVALUE /* if macro values saved in queue file, restore them */
d819 1
a819 1
# endif /* QUEUE_MACVALUE */
@


5.30.0.10
log
@Declare reapchild() to be SIG_TYPE.
@
text
@d349 1
a349 1
			extern SIG_TYPE reapchild();
@


5.30.0.11
log
@sys/dir.h now included if direct is not defined rather than SYSV.
@
text
@d33 1
a33 1
#ifndef direct
d35 1
a35 1
#endif /* !direct */
@


5.30.0.12
log
@Deleted #include <sys/types.h> as it's already included via sendmail.h from
useful.h.  #include "sendmail.h" relocated to top of #include list.
@
text
@d21 1
a22 1
#include <signal.h>
@


5.30.0.13
log
@Added fsync() calls before close of df and cf files.  Helps prevent lost
mail on a system that's crashing often.  Depending on performance lossage
and portability constraints, this may need #ifdef PARANOID later.
@
text
@a142 2
		(void) fflush(dfp);
		(void) fsync(fileno(dfp));
a518 2
		(void) fflush(cf);
		(void) fsync(fileno(cf));
@


5.30.0.14
log
@Added static keyword to declaration for getctluser(), orderq(), dowork(),
workcmpf(), readqf(), clrctluser(), setctluser().
@
text
@d177 1
a177 2
			char *ctluser;
			static char *getctluser();
d203 1
a203 2
			char *ctluser;
			static char *getctluser();
a321 1
	static orderq(), dowork();
a436 1
static
d446 1
a446 1
	static workcmpf();
a579 1
static
a606 1
static
d647 1
a647 2
		FILE *qflock;
		static FILE *readqf();
d722 1
a722 1
static FILE *
a732 1
	static clrctluser(), setctluser();
d1136 1
a1136 1
static char *
a1190 1
static
a1197 1
static
@


5.30.0.15
log
@ANSIfied.
@
text
@a38 9
#ifdef __STDC__
static char * getctluser(ADDRESS *);
static void setctluser(const char *);
#else /* !__STDC__ */
static char * getctluser();
static void setctluser();
#endif /* __STDC__ */
static void clrctluser();

a56 12

# ifdef __STDC__
static orderq(int);
static workcmpf(const WORK *, const WORK *);
static void dowork(WORK *);
static FILE * readqf(ENVELOPE *, int);
# else /* !__STDC__ */
static orderq();
static workcmpf();
static void dowork();
static FILE * readqf();
# endif /* __STDC__ */
d103 1
a103 1
				Xsleep(30);
d111 1
a111 1
				Xsleep(1);
d129 1
d178 1
d205 1
d231 2
d274 1
a319 1
void
d323 3
a351 2
		if (pid > 0 && tTd(4, 2))
			printf("runqueue: forking (pid = %d)\n", pid);
d354 2
d450 1
d510 2
d586 2
a587 2
	register const WORK *a;
	register const WORK *b;
d612 1
a612 1
static void
d617 1
a639 2
		if (i > 0 && tTd(4, 2))
			printf("dowork: forking (pid = %d)\n", i);
d654 1
a654 1

d737 2
d740 1
a886 1
void
d927 1
a1113 1
void
d1148 1
d1199 1
a1199 1
static void
d1201 1
a1201 1
	register const char *str;
d1207 1
a1207 1
static void
a1230 1
void
@


5.30.0.16
log
@Put overly long headers (> 500 bytes) at end of message body.  Adapted
from Paul Vixie's patch.
@
text
@d700 1
a700 1
		eatheader(CurEnv, NULL);
@