50bb4e69fc15d56bc1644fc93baa4125e171b260
* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)queue.c 8.49 (Berkeley) %G% (with queueing)";
static char sccsid
[] = "@(#)queue.c 8.49 (Berkeley) %G% (without queueing)";
char *w_name
; /* name of control file */
char *w_host
; /* name of recipient host */
bool w_lock
; /* is message locked? */
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
;
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.
** The queue file is left locked.
queueup(e
, queueall
, announce
)
char buf
[MAXLINE
], tf
[MAXLINE
];
newid
= (e
->e_id
== NULL
) || !bitset(EF_INQUEUE
, e
->e_flags
);
/* if newid, queuename will create a locked qf file in e->lockfp */
strcpy(tf
, queuename(e
, 't'));
/* if newid, just write the qf file directly (instead of tf file) */
/* get a locked tf file */
for (i
= 0; i
< 128; i
++)
fd
= open(tf
, O_CREAT
|O_WRONLY
|O_EXCL
, FileMode
);
if (LogLevel
> 0 && (i
% 32) == 0)
syslog(LOG_ALERT
, "queueup: cannot create %s, uid=%d: %s",
tf
, geteuid(), errstring(errno
));
if (lockfile(fd
, tf
, NULL
, LOCK_EX
|LOCK_NB
))
else if (LogLevel
> 0 && (i
% 32) == 0)
syslog(LOG_ALERT
, "queueup: cannot lock %s: %s",
/* save the old temp file away */
(void) rename(tf
, queuename(e
, 'T'));
if (fd
< 0 || (tfp
= fdopen(fd
, "w")) == NULL
)
syserr("!queueup: cannot create queue temp file %s, uid=%d",
printf("\n>>>>> queueing %s%s >>>>>\n", e
->e_id
,
newid
? " (new id)" : "");
dumpfd(fileno(tfp
), TRUE
, FALSE
);
dumpfd(fileno(e
->e_lockfp
), TRUE
, FALSE
);
** If there is no data file yet, create one.
e
->e_df
= queuename(e
, 'd');
e
->e_df
= newstr(e
->e_df
);
fd
= open(e
->e_df
, O_WRONLY
|O_CREAT
|O_TRUNC
, FileMode
);
if (fd
< 0 || (dfp
= fdopen(fd
, "w")) == NULL
)
syserr("!queueup: cannot create data temp file %s, uid=%d",
if (fstat(fd
, &stbuf
) < 0)
e
->e_dfdev
= stbuf
.st_dev
;
e
->e_dfino
= stbuf
.st_ino
;
bzero(&mcibuf
, sizeof mcibuf
);
mcibuf
.mci_mailer
= FileMailer
;
(*e
->e_putbody
)(&mcibuf
, e
, NULL
);
(void) xfclose(dfp
, "queueup dfp", e
->e_id
);
** 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 last delivery time */
fprintf(tfp
, "K%ld\n", e
->e_dtime
);
/* output number of delivery attempts */
fprintf(tfp
, "N%d\n", e
->e_ntries
);
/* output inode number of data file */
/* XXX should probably include device major/minor too */
fprintf(tfp
, "I%d/%d/%ld\n",
major(e
->e_dfdev
), minor(e
->e_dfdev
), e
->e_dfino
);
/* output type and name of data file */
if (e
->e_bodytype
!= NULL
)
fprintf(tfp
, "B%s\n", e
->e_bodytype
);
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
);
/* send various flag bits through */
if (bitset(EF_WARNING
, e
->e_flags
))
if (bitset(EF_RESPONSE
, e
->e_flags
))
if (bitset(EF_HAS8BIT
, e
->e_flags
))
fprintf(tfp
, "F%s\n", buf
);
/* $r and $s and $_ macro values */
if ((p
= macvalue('r', e
)) != NULL
)
fprintf(tfp
, "$r%s\n", p
);
if ((p
= macvalue('s', e
)) != NULL
)
fprintf(tfp
, "$s%s\n", p
);
if ((p
= macvalue('_', e
)) != NULL
)
fprintf(tfp
, "$_%s\n", p
);
/* output name of sender */
fprintf(tfp
, "S%s\n", e
->e_from
.q_paddr
);
/* output list of error recipients */
printctladdr(NULL
, NULL
);
for (q
= e
->e_errorqueue
; q
!= NULL
; q
= q
->q_next
)
if (!bitset(QDONTSEND
|QBADADDR
, q
->q_flags
))
fprintf(tfp
, "E%s\n", q
->q_paddr
);
/* output list of recipient addresses */
for (q
= e
->e_sendqueue
; q
!= NULL
; q
= q
->q_next
)
if (bitset(QQUEUEUP
, q
->q_flags
) ||
(queueall
&& !bitset(QDONTSEND
|QBADADDR
|QSENT
, q
->q_flags
)))
fprintf(tfp
, "R%s\n", q
->q_paddr
);
logdelivery(NULL
, NULL
, "queued", NULL
, e
);
** 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_re_rwset
= nullmailer
.m_rh_rwset
=
nullmailer
.m_se_rwset
= nullmailer
.m_sh_rwset
= -1;
bzero(&mcibuf
, sizeof mcibuf
);
mcibuf
.mci_mailer
= &nullmailer
;
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
))
/* expand macros; if null, don't output header at all */
if (bitset(H_DEFAULT
, h
->h_flags
))
(void) expand(h
->h_value
, buf
, &buf
[sizeof buf
], e
);
/* 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
))
fprintf(tfp
, "%s: %s\n", h
->h_field
, buf
);
else if (bitset(H_FROM
|H_RCPT
, h
->h_flags
))
bool oldstyle
= bitset(EF_OLDSTYLE
, e
->e_flags
);
FILE *savetrace
= TrafficLogFile
;
if (bitset(H_FROM
, h
->h_flags
))
commaize(h
, h
->h_value
, oldstyle
, &mcibuf
, e
);
TrafficLogFile
= savetrace
;
fprintf(tfp
, "%s: %s\n", h
->h_field
, h
->h_value
);
if (fflush(tfp
) < 0 || fsync(fileno(tfp
)) < 0 || ferror(tfp
))
syserr("!552 Error writing control file %s", tf
);
syserr("!452 Error writing control file %s", tf
);
/* rename (locked) tf to be (locked) qf */
syserr("cannot rename(%s, %s), df=%s, uid=%d",
tf
, qf
, e
->e_df
, geteuid());
/* close and unlock old (locked) qf */
(void) xfclose(e
->e_lockfp
, "queueup lockfp", e
->e_id
);
e
->e_flags
|= EF_INQUEUE
;
syslog(LOG_DEBUG
, "%s: queueup, qf=%s, df=%s\n", e
->e_id
, qf
, e
->e_df
);
printf("<<<<< done queueing %s <<<<<\n\n", e
->e_id
);
register struct passwd
*pw
;
static ADDRESS
*lastctladdr
;
if (a
== NULL
|| a
->q_alias
== NULL
|| tfp
== NULL
)
if (lastctladdr
!= NULL
&& tfp
!= NULL
)
/* find the active uid */
/* check to see if this is the same as last time */
if (lastctladdr
!= NULL
&& uid
== lastuid
&&
strcmp(lastctladdr
->q_paddr
, a
->q_paddr
) == 0)
if (uid
== 0 || (pw
= getpwuid(uid
)) == NULL
)
fprintf(tfp
, "C%s:%s\n", uname
, a
->q_paddr
);
** RUNQUEUE -- run the jobs in the queue.
** Gets the stuff out of the queue in some presumably logical
** order and processes them.
** 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.
** runs things in the mail queue.
ENVELOPE QueueEnvelope
; /* the queue run envelope */
extern ENVELOPE BlankEnvelope
;
** If no work will ever be selected, don't even bother reading
CurrentLA
= getla(); /* get load average */
if (shouldqueue(0L, curtime()))
printf("Skipping queue run -- load average too high\n");
if (forkflag
&& QueueIntvl
!= 0)
(void) setevent(QueueIntvl
, runqueue
, TRUE
);
** See if we want to go off and do other useful work.
(void) setsignal(SIGCHLD
, reapchild
);
/* parent -- pick up intermediate zombie */
(void) setevent(QueueIntvl
, runqueue
, TRUE
);
/* child -- double fork */
(void) setsignal(SIGCHLD
, SIG_DFL
);
setproctitle("running queue: %s", QueueDir
);
syslog(LOG_DEBUG
, "runqueue %s, pid=%d, forkflag=%d",
QueueDir
, getpid(), forkflag
);
** Release any resources used by the daemon code.
/* force it to run expensive jobs */
** Create ourselves an envelope
e
= newenvelope(&QueueEnvelope
, CurEnv
);
e
->e_flags
= BlankEnvelope
.e_flags
;
** Make sure the alias database is open.
** 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 */
** Ignore jobs that are too expensive for the moment.
if (shouldqueue(w
->w_pri
, w
->w_ctime
))
printf("\nSkipping %s\n", w
->w_name
+ 2);
pid
= dowork(w
->w_name
+ 2, ForkQueueRuns
, FALSE
, e
);
/* exit without the usual cleanup */
** ORDERQ -- order the work queue.
** 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
** 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 opendir(d) fopen(d, "r")
# define readdir(f) ((fread(&dbuf, sizeof dbuf, 1, f) > 0) ? &dbuf : 0)
# define closedir(f) fclose(f)
register struct dirent
*d
;
if (QueueLimitId
!= NULL
)
printf("\tQueueLimitId = %s\n", QueueLimitId
);
if (QueueLimitSender
!= NULL
)
printf("\tQueueLimitSender = %s\n", QueueLimitSender
);
if (QueueLimitRecipient
!= NULL
)
printf("\tQueueLimitRecipient = %s\n", QueueLimitRecipient
);
/* 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
)
extern bool strcontainedin();
/* is this an interesting entry? */
printf("orderq: %12s\n", d
->d_name
);
if (d
->d_name
[0] != 'q' || d
->d_name
[1] != 'f')
if (QueueLimitId
!= NULL
&&
!strcontainedin(QueueLimitId
, d
->d_name
))
** Check queue name for plausibility. This handles
** both old and new type ids.
if (isupper(p
[0]) && isupper(p
[2]))
for (i
= 0; isdigit(*p
); p
++)
printf("orderq: bogus qf name %s\n", d
->d_name
);
syslog(LOG_CRIT
, "orderq: bogus qf name %s",
if (strlen(d
->d_name
) >= MAXNAME
)
d
->d_name
[MAXNAME
- 1] = '\0';
(void) rename(d
->d_name
, lbuf
);
/* 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",
w
->w_name
= newstr(d
->d_name
);
w
->w_lock
= !lockfile(fileno(cf
), w
->w_name
, NULL
, LOCK_SH
|LOCK_NB
);
/* make sure jobs in creation don't clog queue */
/* extract useful information */
if (QueueLimitSender
!= NULL
)
if (SortQueueByHost
|| QueueLimitRecipient
!= NULL
)
while (i
!= 0 && fgets(lbuf
, sizeof lbuf
, cf
) != NULL
)
extern bool strcontainedin();
w
->w_pri
= atol(&lbuf
[1]);
w
->w_ctime
= atol(&lbuf
[1]);
(p
= strrchr(&lbuf
[1], '@')) != NULL
)
w
->w_host
= newstr(&p
[1]);
if (QueueLimitRecipient
== NULL
||
strcontainedin(QueueLimitRecipient
, &lbuf
[1]))
if (QueueLimitSender
!= NULL
&&
strcontainedin(QueueLimitSender
, &lbuf
[1]))
if ((!doall
&& shouldqueue(w
->w_pri
, w
->w_ctime
)) ||
bitset(NEED_R
|NEED_S
, i
))
/* don't even bother sorting this job in */
** Simple sort based on queue priority only.
qsort((char *) wlist
, wc
, sizeof *wlist
, workcmpf0
);
** Sort the work directory for the first time,
** based on host name, lock status, and priority.
qsort((char *) wlist
, wc
, sizeof *wlist
, workcmpf1
);
** If one message to host is locked, "lock" all messages
if (wlist
[i
].w_host
== NULL
&&
else if (wlist
[i
].w_host
!= NULL
&&
strcmp(wlist
[i
].w_host
, w
->w_host
) == 0)
** Sort the work directory for the second time,
** based on lock status, host name, and priority.
qsort((char *) wlist
, wc
, sizeof *wlist
, workcmpf2
);
** Convert the work list into canonical form.
** Should be turning it into a list of envelopes here perhaps.
w
= (WORK
*) xalloc(sizeof *w
);
w
->w_name
= wlist
[i
].w_name
;
w
->w_host
= wlist
[i
].w_host
;
w
->w_lock
= wlist
[i
].w_lock
;
w
->w_pri
= wlist
[i
].w_pri
;
w
->w_ctime
= wlist
[i
].w_ctime
;
for (w
= WorkQ
; w
!= NULL
; w
= w
->w_next
)
printf("%32s: pri=%ld\n", w
->w_name
, w
->w_pri
);
** WORKCMPF0 -- simple priority-only compare function.
** a -- the first argument.
** b -- the second argument.
** WORKCMPF1 -- first compare function for ordering work based on host name.
** Sorts on host name, lock status, and priority in that order.
** a -- the first argument.
** b -- the second argument.
if (a
->w_host
!= NULL
&& b
->w_host
== NULL
)
else if (a
->w_host
== NULL
&& b
->w_host
!= NULL
)
if (a
->w_host
!= NULL
&& b
->w_host
!= NULL
&&
(i
= strcmp(a
->w_host
, b
->w_host
)))
if (a
->w_lock
!= b
->w_lock
)
return b
->w_lock
- a
->w_lock
;
return a
->w_pri
- b
->w_pri
;
** WORKCMPF2 -- second compare function for ordering work based on host name.
** Sorts on lock status, host name, and priority in that order.
** a -- the first argument.
** b -- the second argument.
if (a
->w_lock
!= b
->w_lock
)
return a
->w_lock
- b
->w_lock
;
if (a
->w_host
!= NULL
&& b
->w_host
== NULL
)
else if (a
->w_host
== NULL
&& b
->w_host
!= NULL
)
if (a
->w_host
!= NULL
&& b
->w_host
!= NULL
&&
(i
= strcmp(a
->w_host
, b
->w_host
)))
return a
->w_pri
- b
->w_pri
;
** DOWORK -- do a work request.
** id -- the ID of the job to run.
** forkflag -- if set, run this in background.
** requeueflag -- if set, reinstantiate the queue quickly.
** This is used when expanding aliases in the queue.
** If forkflag is also set, it doesn't wait for the
** e - the envelope in which to run it.
** process id of process that is running the queue job.
** The work request is satisfied if possible.
dowork(id
, forkflag
, requeueflag
, e
)
printf("dowork(%s)\n", id
);
syserr("dowork: cannot fork");
/* parent -- clean out connection cache */
** 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. */
e
->e_flags
|= EF_QUEUERUN
|EF_GLOBALERRS
;
e
->e_errormode
= EM_MAIL
;
GrabTo
= UseErrorsTo
= FALSE
;
syslog(LOG_DEBUG
, "%s: dowork, pid=%d", e
->e_id
,
/* don't use the headers from sendmail.cf... */
/* read the queue control file -- return if locked */
printf("readqf(%s) failed\n", e
->e_id
);
e
->e_flags
|= EF_INQUEUE
;
/* if this has been tried recently, let it be */
if (e
->e_ntries
> 0 && (curtime() - e
->e_dtime
) < MinQueueAge
)
char *howlong
= pintvl(curtime() - e
->e_dtime
, TRUE
);
e
->e_flags
|= EF_KEEPQUEUE
;
if (Verbose
|| tTd(40, 8))
printf("%s: too young (%s)\n",
syslog(LOG_DEBUG
, "%s: too young (%s)",
eatheader(e
, requeueflag
);
** READQF -- read queue file and set up environment.
** e -- the envelope of the job to run.
** TRUE if it successfully read the queue file.
** The queue file is returned locked.
extern ADDRESS
*setctluser();
extern ADDRESS
*sendto();
** Read and process the file.
strcpy(qf
, queuename(e
, 'q'));
printf("readqf(%s): fopen failure (%s)\n",
syserr("readqf: no control file %s", qf
);
if (!lockfile(fileno(qfp
), qf
, NULL
, LOCK_EX
|LOCK_NB
))
/* being processed by another queuer */
if (Verbose
|| tTd(40, 8))
printf("%s: locked\n", e
->e_id
);
syslog(LOG_DEBUG
, "%s: locked", e
->e_id
);
** Check the queue file for plausibility to avoid attacks.
if (fstat(fileno(qfp
), &st
) < 0)
/* must have been being processed by someone else */
printf("readqf(%s): fstat failure (%s)\n",
if (st
.st_uid
!= geteuid())
syslog(LOG_ALERT
, "%s: bogus queue file, uid=%d, mode=%o",
e
->e_id
, st
.st_uid
, st
.st_mode
);
printf("readqf(%s): bogus file\n", qf
);
rename(qf
, queuename(e
, 'Q'));
/* must be a bogus file -- just remove it */
** Race condition -- we got a file just as it was being
** unlinked. Just assume it is zero length.
/* good file -- save this lock */
/* do basic system initialization */
e
->e_flags
|= EF_GLOBALERRS
;
printf("\nRunning %s\n", e
->e_id
);
while ((bp
= fgetfolded(buf
, sizeof buf
, qfp
)) != NULL
)
printf("+++++ %s\n", bp
);
case 'C': /* specify controlling user */
ctladdr
= setctluser(&bp
[1]);
case 'R': /* specify recipient */
(void) sendto(&buf
[1], 1, (ADDRESS
*) NULL
, 0);
case 'E': /* specify error recipient */
(void) sendtolist(&bp
[1], ctladdr
, &e
->e_errorqueue
, e
);
(void) chompheader(&bp
[1], FALSE
, e
);
/* ignore this; we want a new message next time */
setsender(newstr(&bp
[1]), e
, NULL
, TRUE
);
case 'B': /* body type */
e
->e_bodytype
= newstr(&bp
[1]);
case 'D': /* data file name */
e
->e_df
= newstr(&bp
[1]);
e
->e_dfp
= fopen(e
->e_df
, "r");
syserr("readqf: cannot open %s", e
->e_df
);
else if (fstat(fileno(e
->e_dfp
), &st
) >= 0)
e
->e_msgsize
= st
.st_size
;
case 'T': /* init time */
e
->e_ctime
= atol(&bp
[1]);
case 'I': /* data file's inode number */
e
->e_dfino
= atol(&buf
[1]);
case 'K': /* time of last deliver attempt */
e
->e_dtime
= atol(&buf
[1]);
case 'N': /* number of delivery attempts */
e
->e_ntries
= atoi(&buf
[1]);
case 'P': /* message priority */
e
->e_msgpriority
= atol(&bp
[1]) + WkTimeFact
;
case 'F': /* flag bits */
for (p
= &bp
[1]; *p
!= '\0'; p
++)
case 'w': /* warning sent */
e
->e_flags
|= EF_WARNING
;
e
->e_flags
|= EF_RESPONSE
;
case '8': /* has 8 bit data */
e
->e_flags
|= EF_HAS8BIT
;
case '$': /* define macro */
define(bp
[1], newstr(&bp
[2]), e
);
case '\0': /* blank line; ignore */
syserr("readqf: %s: line %d: bad line \"%s\"",
rename(qf
, queuename(e
, 'Q'));
** If we haven't read any lines, this queue file is empty.
** Arrange to remove it without referencing any null pointers.
e
->e_flags
|= EF_CLRQUEUE
| EF_FATALERRS
| EF_RESPONSE
;
** PRINTQUEUE -- print out a representation of the mail queue
** Prints a listing of the mail queue on the standard output.
** Check for permission to print the queue
if (bitset(PRIV_RESTRICTMAILQ
, PrivacyFlags
) && RealUid
!= 0)
GIDSET_T gidset
[NGROUPS
];
if (stat(QueueDir
, &st
) < 0)
syserr("Cannot stat %s", QueueDir
);
n
= getgroups(NGROUPS
, gidset
);
if (gidset
[n
] == st
.st_gid
)
if (RealGid
!= st
.st_gid
)
usrerr("510 You are not permitted to see the queue");
** Read and order the queue.
nrequests
= orderq(TRUE
);
** Print the work list that we have read.
/* first see if there is anything */
printf("Mail queue is empty\n");
CurrentLA
= getla(); /* get load average */
printf("\t\tMail Queue (%d request%s", nrequests
, nrequests
== 1 ? "" : "s");
if (nrequests
> QUEUESIZE
)
printf(", only %d printed", QUEUESIZE
);
printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n");
printf(")\n--Q-ID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
for (w
= WorkQ
; w
!= NULL
; w
= w
->w_next
)
auto time_t submittime
= 0;
printf("%8s", w
->w_name
+ 2);
f
= fopen(w
->w_name
, "r");
printf(" (job completed)\n");
else if (shouldqueue(w
->w_pri
, w
->w_ctime
))
message
[0] = bodytype
[0] = '\0';
while (fgets(buf
, sizeof buf
, f
) != NULL
)
case 'M': /* error message */
if ((i
= strlen(&buf
[1])) >= sizeof message
)
bcopy(&buf
[1], message
, i
);
case 'B': /* body type */
if ((i
= strlen(&buf
[1])) >= sizeof bodytype
)
bcopy(&buf
[1], bodytype
, i
);
case 'S': /* sender name */
printf("%8ld %10ld%c%.12s %.38s",
bitset(EF_WARNING
, flags
) ? '+' : ' ',
printf("%8ld %.16s %.45s", dfsize
,
ctime(&submittime
), &buf
[1]);
if (message
[0] != '\0' || bodytype
[0] != '\0')
printf("\n %10.10s", bodytype
);
printf(" (%.60s)", message
);
case 'C': /* controlling user */
printf("\n\t\t\t\t (---%.34s---)",
case 'R': /* recipient name */
printf("\n\t\t\t\t\t %.38s", &buf
[1]);
printf("\n\t\t\t\t %.45s", &buf
[1]);
case 'T': /* creation time */
submittime
= atol(&buf
[1]);
case 'D': /* data file name */
if (stat(&buf
[1], &st
) >= 0)
case 'F': /* flag bits */
for (p
= &buf
[1]; *p
!= '\0'; p
++)
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.
** 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).
** If no id code is already assigned, queuename will
** assign an id code, create a qf file, and leave a
** locked, open-for-write file pointer in the envelope.
static char buf
[MAXNAME
];
/* new process -- start back at "AA" */
(void) sprintf(qf
, "qf%cAA%05d", c0
, pid
);
while (c1
< '~' || c2
< 'Z')
printf("queuename: trying \"%s\"\n", qf
);
i
= open(qf
, O_WRONLY
|O_CREAT
|O_EXCL
, FileMode
);
syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)",
qf
, QueueDir
, geteuid());
if (lockfile(i
, qf
, NULL
, LOCK_EX
|LOCK_NB
))
e
->e_lockfp
= fdopen(i
, "w");
/* a reader got the file; abandon it and try again */
if (c1
>= '~' && c2
>= 'Z')
syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)",
qf
, QueueDir
, geteuid());
e
->e_id
= newstr(&qf
[2]);
printf("queuename: assigned id %s, env=%x\n", e
->e_id
, e
);
dumpfd(fileno(e
->e_lockfp
), TRUE
, FALSE
);
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'.
printf("unlockqueue(%s)\n", e
->e_id
);
/* if there is a lock file in the envelope, close it */
xfclose(e
->e_lockfp
, "unlockqueue", e
->e_id
);
/* don't create a queue id if we don't already have one */
/* remove the transcript */
syslog(LOG_DEBUG
, "%s: unlock", e
->e_id
);
xunlink(queuename(e
, 'x'));
** SETCTLUSER -- create a controlling address
** Create a fake "address" given only a local login name; this is
** used as a "controlling user" for future recipient addresses.
** user -- the user name of the controlling user.
** An address descriptor for the controlling user.
** See if this clears our concept of controlling user.
if (user
== NULL
|| *user
== '\0')
** Set up addr fields for controlling user.
a
= (ADDRESS
*) xalloc(sizeof *a
);
bzero((char *) a
, sizeof *a
);
if (*user
!= '\0' && (pw
= getpwnam(user
)) != NULL
)
if (strcmp(pw
->pw_dir
, "/") == 0)
a
->q_home
= newstr(pw
->pw_dir
);
a
->q_user
= newstr(user
);
a
->q_user
= newstr(DefUser
);
a
->q_flags
|= QPRIMARY
; /* flag as a "ctladdr" */
a
->q_mailer
= LocalMailer
;