SCCSID(@
(#)queue.c 4.4 %G% (no queueing));
SCCSID(@
(#)queue.c 4.4 %G%);
char *w_name
; /* name of control file */
long w_pri
; /* priority of message, see below */
struct work
*w_next
; /* next in queue */
typedef struct work WORK
;
WORK
*WorkQ
; /* queue of things to be done */
** QUEUEUP -- queue a message up for future transmission.
** 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.
** The current request are saved in a control file.
queueup(e
, queueall
, announce
)
tf
= newstr(queuename(e
, 't'));
syserr("queueup: cannot create temp file %s", tf
);
(void) chmod(tf
, FileMode
);
printf("queueing %s\n", e
->e_id
);
** If there is no data file yet, create one.
e
->e_df
= newstr(queuename(e
, 'd'));
dfp
= fopen(e
->e_df
, "w");
syserr("queueup: cannot create %s", e
->e_df
);
(void) chmod(e
->e_df
, FileMode
);
(*e
->e_putbody
)(dfp
, ProgMailer
, e
);
** Output future work requests.
** Priority should be first, since it is read 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
))
fprintf(tfp
, "R%s\n", q
->q_paddr
);
message(Arpa_Info
, "queued");
** 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
** 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;
for (h
= e
->e_header
; h
!= NULL
; h
= h
->h_link
)
/* don't output null headers */
if (h
->h_value
== NULL
|| h
->h_value
[0] == '\0')
/* don't output resent headers on non-resent messages */
if (bitset(H_RESENT
, h
->h_flags
) && !bitset(EF_RESENT
, e
->e_flags
))
/* if conditional, output the set of conditions */
if (!bitzerop(h
->h_mflags
) && bitset(H_CHECK
|H_ACHECK
, h
->h_flags
))
for (j
= '\0'; j
<= '\177'; j
++)
if (bitnset(j
, h
->h_mflags
))
/* 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
),
fprintf(tfp
, "%s: %s\n", h
->h_field
, h
->h_value
);
syserr("cannot link(%s, %s), df=%s", tf
, qf
, e
->e_df
);
syslog(LOG_DEBUG
, "%s: queueup, qf=%s, df=%s\n", e
->e_id
, qf
, e
->e_df
);
** RUNQUEUE -- run the jobs in the queue.
** Gets the stuff out of the queue in some presumably logical
** order and processes them.
** runs things in the mail queue.
** See if we want to go off and do other useful work.
/* parent -- pick up intermediate zombie */
(void) setevent(QueueIntvl
, runqueue
, TRUE
);
/* child -- double fork */
syslog(LOG_DEBUG
, "runqueue %s, pid=%d", QueueDir
, getpid());
** Release any resources used by the daemon code.
** 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 */
/* process them once at a time */
** ORDERQ -- order the work queue.
** The number of request in the queue (not necessarily
** the number of requests in WorkQ however).
** Sets WorkQ to the queue of available work, in order.
# define WLSIZE 120 /* max size of worklist per sort */
# define opendir(d) fopen(d, "r")
# define readdir(f) ((fread(&dbuf, sizeof dbuf, 1, f) > 0) ? &dbuf : 0)
# define closedir(f) fclose(f)
register struct direct
*d
;
register WORK
**wp
; /* parent of w */
/* clear out old WorkQ */
for (w
= WorkQ
; w
!= NULL
; )
register WORK
*nw
= w
->w_next
;
/* open the queue directory */
syserr("orderq: cannot open \"%s\" as \".\"", QueueDir
);
** Read the work directory.
while ((d
= readdir(f
)) != NULL
)
/* is this an interesting entry? */
printf("orderq: %12s\n", d
->d_name
);
if (d
->d_name
[0] != 'q' || d
->d_name
[1] != 'f')
/* yes -- open control file (if not too many files) */
cf
= fopen(d
->d_name
, "r");
/* this may be some random person sending hir msgs */
/* syserr("orderq: cannot open %s", cbuf); */
printf("orderq: cannot open %s (%d)\n",
wlist
[wn
].w_name
= newstr(d
->d_name
);
/* extract useful information */
while (fgets(lbuf
, sizeof lbuf
, cf
) != NULL
)
(void) sscanf(&lbuf
[1], "%ld", &wlist
[wn
].w_pri
);
** Sort the work directory.
qsort(wlist
, min(wn
, WLSIZE
), sizeof *wlist
, workcmpf
);
** Convert the work list into canonical form.
** Should be turning it into a list of envelopes here perhaps.
for (i
= min(wn
, WLSIZE
); --i
>= 0; )
w
= (WORK
*) xalloc(sizeof *w
);
w
->w_name
= wlist
[i
].w_name
;
w
->w_pri
= wlist
[i
].w_pri
;
for (w
= WorkQ
; w
!= NULL
; w
= w
->w_next
)
printf("%32s: pri=%ld\n", w
->w_name
, w
->w_pri
);
** WORKCMPF -- compare function for ordering work.
** a -- the first argument.
** b -- the second argument.
if (a
->w_pri
== b
->w_pri
)
else if (a
->w_pri
> b
->w_pri
)
** DOWORK -- do a work request.
** w -- the work request to be satisfied.
** The work request is satisfied if possible.
printf("dowork: %s pri %ld\n", w
->w_name
, w
->w_pri
);
syserr("dowork: cannot fork");
** 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. */
CurEnv
->e_flags
&= ~EF_FATALERRS
;
CurEnv
->e_id
= &w
->w_name
[2];
syslog(LOG_DEBUG
, "%s: dowork, pid=%d", CurEnv
->e_id
,
/* don't use the headers from sendmail.cf... */
/* lock the control file during processing */
if (link(w
->w_name
, queuename(CurEnv
, 'l')) < 0)
/* being processed by another queuer */
syslog(LOG_DEBUG
, "%s: locked", CurEnv
->e_id
);
/* do basic system initialization */
/* read the queue control file */
CurEnv
->e_flags
|= EF_INQUEUE
;
if (!bitset(EF_FATALERRS
, CurEnv
->e_flags
))
sendall(CurEnv
, SM_DELIVER
);
** Parent -- pick up results.
** READQF -- read queue file and set up environment.
** 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.
** cf is read and created as the current job, as though
** we had been invoked by argument.
extern char *fgetfolded();
extern ADDRESS
*sendto();
** Read and process the file.
syserr("readqf: no control file %s", qf
);
printf("\nRunning %s\n", e
->e_id
);
while (fgetfolded(buf
, sizeof buf
, qfp
) != NULL
)
case 'R': /* specify recipient */
(void) sendto(&buf
[1], 1, (ADDRESS
*) NULL
, 0);
(void) chompheader(&buf
[1], FALSE
);
e
->e_message
= newstr(&buf
[1]);
setsender(newstr(&buf
[1]));
case 'D': /* data file name */
e
->e_df
= newstr(&buf
[1]);
e
->e_dfp
= fopen(e
->e_df
, "r");
syserr("readqf: cannot open %s", e
->e_df
);
case 'T': /* init time */
(void) sscanf(&buf
[1], "%ld", &e
->e_ctime
);
case 'P': /* message priority */
(void) sscanf(&buf
[1], "%ld", &e
->e_msgpriority
);
/* make sure that big things get sent eventually */
e
->e_msgpriority
-= WKTIMEFACT
;
syserr("readqf(%s): bad line \"%s\"", e
->e_id
, buf
);
** PRINTQUEUE -- print out a representation of the mail queue
** Prints a listing of the mail queue on the standard output.
** Read and order the queue.
** Print the work list that we have read.
/* first see if there is anything */
printf("Mail queue is empty\n");
printf("\t\tMail Queue (%d request%s", nrequests
, nrequests
== 1 ? "" : "s");
printf(", only %d printed", WLSIZE
);
printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
for (w
= WorkQ
; w
!= NULL
; w
= w
->w_next
)
auto time_t submittime
= 0;
f
= fopen(w
->w_name
, "r");
printf("%7s", w
->w_name
+ 2);
while (fgets(buf
, sizeof buf
, f
) != NULL
)
case 'M': /* error message */
strcpy(message
, &buf
[1]);
case 'S': /* sender name */
printf("%8ld %.16s %.45s", dfsize
,
ctime(&submittime
), &buf
[1]);
printf("\n\t\t\t\t (%.43s)", message
);
case 'R': /* recipient name */
printf("\n\t\t\t\t %.45s", &buf
[1]);
case 'T': /* creation time */
sscanf(&buf
[1], "%ld", &submittime
);
case 'D': /* data file name */
if (stat(&buf
[1], &st
) >= 0)
if (submittime
== (time_t) 0)
printf(" (no control file)");
** 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.
** We first create an nf file that is only used when
** assigning an id. This file is always empty, so that
** we can never accidently truncate an lf file.
** e -- envelope to build it in/from.
** type -- the file type, used as the first character
** a pointer to the new file name (in a static buffer).
** Will create the lf and qf files if no id code is
** already assigned. This will cause the envelope
static char buf
[MAXNAME
];
/* new process -- start back at "AA" */
(void) sprintf(qf
, "qfAA%05d", pid
);
while (c1
< '~' || c2
< 'Z')
lf
[2] = nf
[2] = qf
[2] = c1
;
lf
[3] = nf
[3] = qf
[3] = ++c2
;
printf("queuename: trying \"%s\"\n", nf
);
if (access(lf
, 0) >= 0 || access(qf
, 0) >= 0)
(void) unlink(nf
); /* kernel bug */
if (close(creat(qf
, FileMode
)) < 0)
if (c1
>= '~' && c2
>= 'Z')
syserr("queuename: Cannot create \"%s\" in \"%s\"",
e
->e_id
= newstr(&qf
[2]);
printf("queuename: assigned id %s, env=%x\n", e
->e_id
, e
);
syslog(LOG_DEBUG
, "%s: assigned id", e
->e_id
);
(void) sprintf(buf
, "%cf%s", type
, e
->e_id
);
printf("queuename: %s\n", buf
);
** UNLOCKQUEUE -- unlock the queue entry for a specified envelope
** e -- the envelope to unlock.
** unlocks the queue for `e'.
/* remove the transcript */
syslog(LOG_DEBUG
, "%s: unlock", e
->e_id
);
xunlink(queuename(e
, 'x'));
/* last but not least, remove the lock */
xunlink(queuename(e
, 'l'));