⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 queue.c

📁 < linux网络编程工具>>配套源码
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
 * Copyright (c) 1988, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 *
 */


#include <sendmail.h>

#ifndef lint
# if QUEUE
static char id[] = "@(#)$Id: queue.c,v 8.343.4.17 2000/09/15 03:34:51 gshapiro Exp $ (with queueing)";
# else /* QUEUE */
static char id[] = "@(#)$Id: queue.c,v 8.343.4.17 2000/09/15 03:34:51 gshapiro Exp $ (without queueing)";
# endif /* QUEUE */
#endif /* ! lint */

# include <dirent.h>

#if QUEUE

# if _FFR_QUEUEDELAY
#  define QF_VERSION	5	/* version number of this queue format */
static time_t	queuedelay __P((ENVELOPE *));
# else /* _FFR_QUEUEDELAY */
#  define QF_VERSION	4	/* version number of this queue format */
#  define queuedelay(e)	MinQueueAge
# endif /* _FFR_QUEUEDELAY */

/*
**  Work queue.
*/

struct work
{
	char		*w_name;	/* name of control file */
	char		*w_host;	/* name of recipient host */
	bool		w_lock;		/* is message locked? */
	bool		w_tooyoung;	/* is it too young to run? */
	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;

static WORK	*WorkQ;			/* queue of things to be done */

static void	grow_wlist __P((int));
static int	orderq __P((int, bool));
static void	printctladdr __P((ADDRESS *, FILE *));
static int	print_single_queue __P((int));
static bool	readqf __P((ENVELOPE *));
static void	runqueueevent __P((void));
static int	run_single_queue __P((int, bool, bool));
static char	*strrev __P((char *));
static ADDRESS	*setctluser __P((char *, int));
static int	workcmpf0();
static int	workcmpf1();
static int	workcmpf2();
static int	workcmpf3();
static int	workcmpf4();

/*
**  QUEUEUP -- queue a message up for future transmission.
**
**	Parameters:
**		e -- the envelope to queue up.
**		announce -- if TRUE, tell when you are queueing up.
**
**	Returns:
**		none.
**
**	Side Effects:
**		The current request are saved in a control file.
**		The queue file is left locked.
*/

# define TEMPQF_LETTER 'T'

void
queueup(e, announce)
	register ENVELOPE *e;
	bool announce;
{
	char *qf;
	register FILE *tfp;
	register HDR *h;
	register ADDRESS *q;
	int tfd = -1;
	int i;
	bool newid;
	register char *p;
	MAILER nullmailer;
	MCI mcibuf;
	char tf[MAXPATHLEN];
	char buf[MAXLINE];

	/*
	**  Create control file.
	*/

	newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);

	/* if newid, queuename will create a locked qf file in e->lockfp */
	(void) strlcpy(tf, queuename(e, 't'), sizeof tf);
	tfp = e->e_lockfp;
	if (tfp == NULL)
		newid = FALSE;

	/* if newid, just write the qf file directly (instead of tf file) */
	if (!newid)
	{
		int flags;

		flags = O_CREAT|O_WRONLY|O_EXCL;

		/* get a locked tf file */
		for (i = 0; i < 128; i++)
		{
			if (tfd < 0)
			{
#if _FFR_QUEUE_FILE_MODE
				MODE_T oldumask;

				if (bitset(S_IWGRP, QueueFileMode))
					oldumask = umask(002);
				tfd = open(tf, flags, QueueFileMode);
				if (bitset(S_IWGRP, QueueFileMode))
					(void) umask(oldumask);
#else /* _FFR_QUEUE_FILE_MODE */
				tfd = open(tf, flags, FileMode);
#endif /* _FFR_QUEUE_FILE_MODE */

				if (tfd < 0)
				{
					if (errno != EEXIST)
						break;
					if (LogLevel > 0 && (i % 32) == 0)
						sm_syslog(LOG_ALERT, e->e_id,
							  "queueup: cannot create %s, uid=%d: %s",
							  tf, geteuid(), errstring(errno));
				}
			}
			if (tfd >= 0)
			{
				if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB))
					break;
				else if (LogLevel > 0 && (i % 32) == 0)
					sm_syslog(LOG_ALERT, e->e_id,
						  "queueup: cannot lock %s: %s",
						  tf, errstring(errno));
				if ((i % 32) == 31)
				{
					(void) close(tfd);
					tfd = -1;
				}
			}

			if ((i % 32) == 31)
			{
				/* save the old temp file away */
				(void) rename(tf, queuename(e, TEMPQF_LETTER));
			}
			else
				(void) sleep(i % 32);
		}
		if (tfd < 0 || (tfp = fdopen(tfd, "w")) == NULL)
		{
			int save_errno = errno;

			printopenfds(TRUE);
			errno = save_errno;
			syserr("!queueup: cannot create queue temp file %s, uid=%d",
				tf, geteuid());
		}
	}

	if (tTd(40, 1))
		dprintf("\n>>>>> queueing %s/qf%s%s >>>>>\n",
			qid_printqueue(e->e_queuedir), e->e_id,
			newid ? " (new id)" : "");
	if (tTd(40, 3))
	{
		dprintf("  e_flags=");
		printenvflags(e);
	}
	if (tTd(40, 32))
	{
		dprintf("  sendq=");
		printaddr(e->e_sendqueue, TRUE);
	}
	if (tTd(40, 9))
	{
		dprintf("  tfp=");
		dumpfd(fileno(tfp), TRUE, FALSE);
		dprintf("  lockfp=");
		if (e->e_lockfp == NULL)
			dprintf("NULL\n");
		else
			dumpfd(fileno(e->e_lockfp), TRUE, FALSE);
	}

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

	if (bitset(EF_HAS_DF, e->e_flags))
	{
		if (e->e_dfp != NULL && bfcommit(e->e_dfp) < 0)
			syserr("!queueup: cannot commit data file %s, uid=%d",
				queuename(e, 'd'), geteuid());
	}
	else
	{
		int dfd;
		register FILE *dfp = NULL;
		char dfname[MAXPATHLEN];
		struct stat stbuf;

		if (e->e_dfp != NULL && bftest(e->e_dfp))
			syserr("committing over bf file");

		(void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
#if _FFR_QUEUE_FILE_MODE
		{
			MODE_T oldumask;

			if (bitset(S_IWGRP, QueueFileMode))
				oldumask = umask(002);
			dfd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC,
				   QueueFileMode);
			if (bitset(S_IWGRP, QueueFileMode))
				(void) umask(oldumask);
		}
#else /* _FFR_QUEUE_FILE_MODE */
		dfd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode);
#endif /* _FFR_QUEUE_FILE_MODE */
		if (dfd < 0 || (dfp = fdopen(dfd, "w")) == NULL)
			syserr("!queueup: cannot create data temp file %s, uid=%d",
				dfname, geteuid());
		if (fstat(dfd, &stbuf) < 0)
			e->e_dfino = -1;
		else
		{
			e->e_dfdev = stbuf.st_dev;
			e->e_dfino = stbuf.st_ino;
		}
		e->e_flags |= EF_HAS_DF;
		memset(&mcibuf, '\0', sizeof mcibuf);
		mcibuf.mci_out = dfp;
		mcibuf.mci_mailer = FileMailer;
		(*e->e_putbody)(&mcibuf, e, NULL);
		if (fclose(dfp) < 0)
			syserr("!queueup: cannot save data temp file %s, uid=%d",
				dfname, geteuid());
		e->e_putbody = putbody;
	}

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

	/* output queue version number (must be first!) */
	fprintf(tfp, "V%d\n", QF_VERSION);

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

	/* output last delivery time */
# if _FFR_QUEUEDELAY
	fprintf(tfp, "K%ld\n", (long) e->e_dtime);
	fprintf(tfp, "G%d\n", e->e_queuealg);
	fprintf(tfp, "Y%ld\n", (long) e->e_queuedelay);
	if (tTd(40, 64))
		sm_syslog(LOG_INFO, e->e_id,
			"queue alg: %d delay %ld next: %ld (now: %ld)\n",
			e->e_queuealg, e->e_queuedelay, e->e_dtime, curtime());
# else /* _FFR_QUEUEDELAY */
	fprintf(tfp, "K%ld\n", (long) e->e_dtime);
# endif /* _FFR_QUEUEDELAY */

	/* output number of delivery attempts */
	fprintf(tfp, "N%d\n", e->e_ntries);

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

	/* output inode number of data file */
	/* XXX should probably include device major/minor too */
	if (e->e_dfino != -1)
	{
		/*CONSTCOND*/
		if (sizeof e->e_dfino > sizeof(long))
			fprintf(tfp, "I%ld/%ld/%s\n",
				(long) major(e->e_dfdev),
				(long) minor(e->e_dfdev),
				quad_to_string(e->e_dfino));
		else
			fprintf(tfp, "I%ld/%ld/%lu\n",
				(long) major(e->e_dfdev),
				(long) minor(e->e_dfdev),
				(unsigned long) e->e_dfino);
	}

	/* output body type */
	if (e->e_bodytype != NULL)
		fprintf(tfp, "B%s\n", denlstring(e->e_bodytype, TRUE, FALSE));

# if _FFR_SAVE_CHARSET
	if (e->e_charset != NULL)
		fprintf(tfp, "X%s\n", denlstring(e->e_charset, TRUE, FALSE));
# endif /* _FFR_SAVE_CHARSET */

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

	/* send various flag bits through */
	p = buf;
	if (bitset(EF_WARNING, e->e_flags))
		*p++ = 'w';
	if (bitset(EF_RESPONSE, e->e_flags))
		*p++ = 'r';
	if (bitset(EF_HAS8BIT, e->e_flags))
		*p++ = '8';
	if (bitset(EF_DELETE_BCC, e->e_flags))
		*p++ = 'b';
	if (bitset(EF_RET_PARAM, e->e_flags))
		*p++ = 'd';
	if (bitset(EF_NO_BODY_RETN, e->e_flags))
		*p++ = 'n';
	*p++ = '\0';
	if (buf[0] != '\0')
		fprintf(tfp, "F%s\n", buf);

	/* save $={persistentMacros} macro values */
	queueup_macros(macid("{persistentMacros}", NULL), tfp, e);

	/* output name of sender */
	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
		p = e->e_sender;
	else
		p = e->e_from.q_paddr;
	fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE));

	/* output ESMTP-supplied "original" information */
	if (e->e_envid != NULL)
		fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE));

	/* output AUTH= parameter */
	if (e->e_auth_param != NULL)
		fprintf(tfp, "A%s\n", denlstring(e->e_auth_param,
						 TRUE, FALSE));

	/* output list of recipient addresses */
	printctladdr(NULL, NULL);
	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
	{
		if (!QS_IS_UNDELIVERED(q->q_state))
			continue;

		printctladdr(q, tfp);
		if (q->q_orcpt != NULL)
			fprintf(tfp, "Q%s\n",
				denlstring(q->q_orcpt, TRUE, FALSE));
		(void) putc('R', tfp);
		if (bitset(QPRIMARY, q->q_flags))
			(void) putc('P', tfp);
		if (bitset(QHASNOTIFY, q->q_flags))
			(void) putc('N', tfp);
		if (bitset(QPINGONSUCCESS, q->q_flags))
			(void) putc('S', tfp);
		if (bitset(QPINGONFAILURE, q->q_flags))
			(void) putc('F', tfp);
		if (bitset(QPINGONDELAY, q->q_flags))
			(void) putc('D', tfp);
		(void) putc(':', tfp);
		(void) fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE));
		if (announce)
		{
			e->e_to = q->q_paddr;
			message("queued");
			if (LogLevel > 8)
				logdelivery(q->q_mailer, NULL, q->q_status,
					    "queued", NULL, (time_t) 0, e);
			e->e_to = NULL;
		}
		if (tTd(40, 1))
		{
			dprintf("queueing ");
			printaddr(q, FALSE);
		}
	}

	/*
	**  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.
	*/

	memset((char *) &nullmailer, '\0', sizeof nullmailer);
	nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
			nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1;
	nullmailer.m_eol = "\n";
	memset(&mcibuf, '\0', sizeof mcibuf);
	mcibuf.mci_mailer = &nullmailer;
	mcibuf.mci_out = tfp;

	define('g', "\201f", e);
	for (h = e->e_header; h != NULL; h = h->h_link)
	{
		if (h->h_value == NULL)
			continue;

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

		/* expand macros; if null, don't output header at all */
		if (bitset(H_DEFAULT, h->h_flags))
		{
			(void) expand(h->h_value, buf, sizeof buf, e);
			if (buf[0] == '\0')
				continue;
		}

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

		/* output conditional macro if present */
		if (h->h_macro != '\0')
		{
			if (bitset(0200, h->h_macro))
				fprintf(tfp, "${%s}",
					macname(h->h_macro & 0377));
			else
				fprintf(tfp, "$%c", h->h_macro);
		}
		else if (!bitzerop(h->h_mflags) &&
			 bitset(H_CHECK|H_ACHECK, h->h_flags))
		{
			int j;

			/* if conditional, output the set of conditions */
			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) &&
		    !bitset(H_BINDLATE, h->h_flags))
		{
			fprintf(tfp, "%s: %s\n",
				h->h_field,
				denlstring(buf, FALSE, TRUE));
		}
		else if (bitset(H_FROM|H_RCPT, h->h_flags) &&
			 !bitset(H_BINDLATE, h->h_flags))
		{
			bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
			FILE *savetrace = TrafficLogFile;

			TrafficLogFile = NULL;

			if (bitset(H_FROM, h->h_flags))
				oldstyle = FALSE;

			commaize(h, h->h_value, oldstyle, &mcibuf, e);

			TrafficLogFile = savetrace;
		}
		else
		{
			fprintf(tfp, "%s: %s\n",
				h->h_field,
				denlstring(h->h_value, FALSE, TRUE));
		}
	}

	/*
	**  Clean up.
	**
	**	Write a terminator record -- this is to prevent
	**	scurrilous crackers from appending any data.
	*/

	fprintf(tfp, ".\n");

	if (fflush(tfp) < 0 ||
	    (SuperSafe && fsync(fileno(tfp)) < 0) ||
	    ferror(tfp))
	{
		if (newid)
			syserr("!552 Error writing control file %s", tf);
		else
			syserr("!452 Error writing control file %s", tf);
	}

	if (!newid)
	{
		/* rename (locked) tf to be (locked) qf */
		qf = queuename(e, 'q');
		if (rename(tf, qf) < 0)
			syserr("cannot rename(%s, %s), uid=%d",
				tf, qf, geteuid());

		/*
		**  fsync() after renaming to make sure
		**  metadata is written to disk on
		**  filesystems in which renames are
		**  not guaranteed such as softupdates.
		*/

		if (tfd >= 0 && SuperSafe && fsync(tfd) < 0)
			syserr("!queueup: cannot fsync queue temp file %s",
			       tf);

		/* close and unlock old (locked) qf */
		if (e->e_lockfp != NULL)
			(void) fclose(e->e_lockfp);
		e->e_lockfp = tfp;
	}
	else
		qf = tf;
	errno = 0;
	e->e_flags |= EF_INQUEUE;

	/* save log info */
	if (LogLevel > 79)
		sm_syslog(LOG_DEBUG, e->e_id, "queueup, qf=%s", qf);

	if (tTd(40, 1))
		dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
	return;
}

static void
printctladdr(a, tfp)
	register ADDRESS *a;
	FILE *tfp;
{
	char *user;
	register ADDRESS *q;
	uid_t uid;
	gid_t gid;
	static ADDRESS *lastctladdr = NULL;
	static uid_t lastuid;

	/* initialization */
	if (a == NULL || a->q_alias == NULL || tfp == NULL)
	{
		if (lastctladdr != NULL && tfp != NULL)
			fprintf(tfp, "C\n");
		lastctladdr = NULL;
		lastuid = 0;
		return;
	}

	/* find the active uid */
	q = getctladdr(a);
	if (q == NULL)
	{
		user = NULL;
		uid = 0;
		gid = 0;
	}
	else
	{
		user = q->q_ruser != NULL ? q->q_ruser : q->q_user;
		uid = q->q_uid;
		gid = q->q_gid;
	}
	a = a->q_alias;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -