xref: /csrg-svn/usr.sbin/sendmail/src/queue.c (revision 25184)
1 /*
2 **  Sendmail
3 **  Copyright (c) 1983  Eric P. Allman
4 **  Berkeley, California
5 **
6 **  Copyright (c) 1983 Regents of the University of California.
7 **  All rights reserved.  The Berkeley software License Agreement
8 **  specifies the terms and conditions for redistribution.
9 */
10 
11 
12 # include "sendmail.h"
13 # include <sys/stat.h>
14 # include <sys/dir.h>
15 # include <signal.h>
16 # include <errno.h>
17 
18 # ifndef QUEUE
19 # ifndef lint
20 static char	SccsId[] = "@(#)queue.c	5.17 (Berkeley) 10/13/85	(no queueing)";
21 # endif not lint
22 # else QUEUE
23 
24 # ifndef lint
25 static char	SccsId[] = "@(#)queue.c	5.17 (Berkeley) 10/13/85";
26 # endif not lint
27 
28 /*
29 **  Work queue.
30 */
31 
32 struct work
33 {
34 	char		*w_name;	/* name of control file */
35 	long		w_pri;		/* priority of message, see below */
36 	time_t		w_ctime;	/* creation time of message */
37 	struct work	*w_next;	/* next in queue */
38 };
39 
40 typedef struct work	WORK;
41 
42 WORK	*WorkQ;			/* queue of things to be done */
43 /*
44 **  QUEUEUP -- queue a message up for future transmission.
45 **
46 **	Parameters:
47 **		e -- the envelope to queue up.
48 **		queueall -- if TRUE, queue all addresses, rather than
49 **			just those with the QQUEUEUP flag set.
50 **		announce -- if TRUE, tell when you are queueing up.
51 **
52 **	Returns:
53 **		none.
54 **
55 **	Side Effects:
56 **		The current request are saved in a control file.
57 */
58 
59 queueup(e, queueall, announce)
60 	register ENVELOPE *e;
61 	bool queueall;
62 	bool announce;
63 {
64 	char *tf;
65 	char *qf;
66 	char buf[MAXLINE];
67 	register FILE *tfp;
68 	register HDR *h;
69 	register ADDRESS *q;
70 	MAILER nullmailer;
71 
72 	/*
73 	**  Create control file.
74 	*/
75 
76 	tf = newstr(queuename(e, 't'));
77 	tfp = fopen(tf, "w");
78 	if (tfp == NULL)
79 	{
80 		syserr("queueup: cannot create temp file %s", tf);
81 		return;
82 	}
83 	(void) chmod(tf, FileMode);
84 
85 # ifdef DEBUG
86 	if (tTd(40, 1))
87 		printf("queueing %s\n", e->e_id);
88 # endif DEBUG
89 
90 	/*
91 	**  If there is no data file yet, create one.
92 	*/
93 
94 	if (e->e_df == NULL)
95 	{
96 		register FILE *dfp;
97 		extern putbody();
98 
99 		e->e_df = newstr(queuename(e, 'd'));
100 		dfp = fopen(e->e_df, "w");
101 		if (dfp == NULL)
102 		{
103 			syserr("queueup: cannot create %s", e->e_df);
104 			(void) fclose(tfp);
105 			return;
106 		}
107 		(void) chmod(e->e_df, FileMode);
108 		(*e->e_putbody)(dfp, ProgMailer, e);
109 		(void) fclose(dfp);
110 		e->e_putbody = putbody;
111 	}
112 
113 	/*
114 	**  Output future work requests.
115 	**	Priority should be first, since it is read by orderq.
116 	*/
117 
118 	/* output message priority */
119 	fprintf(tfp, "P%ld\n", e->e_msgpriority);
120 
121 	/* output creation time */
122 	fprintf(tfp, "T%ld\n", e->e_ctime);
123 
124 	/* output name of data file */
125 	fprintf(tfp, "D%s\n", e->e_df);
126 
127 	/* message from envelope, if it exists */
128 	if (e->e_message != NULL)
129 		fprintf(tfp, "M%s\n", e->e_message);
130 
131 	/* output name of sender */
132 	fprintf(tfp, "S%s\n", e->e_from.q_paddr);
133 
134 	/* output list of recipient addresses */
135 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
136 	{
137 		if (queueall ? !bitset(QDONTSEND, q->q_flags) :
138 			       bitset(QQUEUEUP, q->q_flags))
139 		{
140 			fprintf(tfp, "R%s\n", q->q_paddr);
141 			if (announce)
142 			{
143 				e->e_to = q->q_paddr;
144 				message(Arpa_Info, "queued");
145 				if (LogLevel > 4)
146 					logdelivery("queued");
147 				e->e_to = NULL;
148 			}
149 #ifdef DEBUG
150 			if (tTd(40, 1))
151 			{
152 				printf("queueing ");
153 				printaddr(q, FALSE);
154 			}
155 #endif DEBUG
156 		}
157 	}
158 
159 	/*
160 	**  Output headers for this message.
161 	**	Expand macros completely here.  Queue run will deal with
162 	**	everything as absolute headers.
163 	**		All headers that must be relative to the recipient
164 	**		can be cracked later.
165 	**	We set up a "null mailer" -- i.e., a mailer that will have
166 	**	no effect on the addresses as they are output.
167 	*/
168 
169 	bzero((char *) &nullmailer, sizeof nullmailer);
170 	nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1;
171 	nullmailer.m_eol = "\n";
172 
173 	define('g', "\001f", e);
174 	for (h = e->e_header; h != NULL; h = h->h_link)
175 	{
176 		extern bool bitzerop();
177 
178 		/* don't output null headers */
179 		if (h->h_value == NULL || h->h_value[0] == '\0')
180 			continue;
181 
182 		/* don't output resent headers on non-resent messages */
183 		if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
184 			continue;
185 
186 		/* output this header */
187 		fprintf(tfp, "H");
188 
189 		/* if conditional, output the set of conditions */
190 		if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags))
191 		{
192 			int j;
193 
194 			(void) putc('?', tfp);
195 			for (j = '\0'; j <= '\177'; j++)
196 				if (bitnset(j, h->h_mflags))
197 					(void) putc(j, tfp);
198 			(void) putc('?', tfp);
199 		}
200 
201 		/* output the header: expand macros, convert addresses */
202 		if (bitset(H_DEFAULT, h->h_flags))
203 		{
204 			(void) expand(h->h_value, buf, &buf[sizeof buf], e);
205 			fprintf(tfp, "%s: %s\n", h->h_field, buf);
206 		}
207 		else if (bitset(H_FROM|H_RCPT, h->h_flags))
208 		{
209 			commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags),
210 				 &nullmailer);
211 		}
212 		else
213 			fprintf(tfp, "%s: %s\n", h->h_field, h->h_value);
214 	}
215 
216 	/*
217 	**  Clean up.
218 	*/
219 
220 	(void) fclose(tfp);
221 	qf = queuename(e, 'q');
222 	if (tf != NULL)
223 	{
224 		(void) unlink(qf);
225 		if (rename(tf, qf) < 0)
226 			syserr("cannot unlink(%s, %s), df=%s", tf, qf, e->e_df);
227 		errno = 0;
228 	}
229 
230 # ifdef LOG
231 	/* save log info */
232 	if (LogLevel > 15)
233 		syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
234 # endif LOG
235 }
236 /*
237 **  RUNQUEUE -- run the jobs in the queue.
238 **
239 **	Gets the stuff out of the queue in some presumably logical
240 **	order and processes them.
241 **
242 **	Parameters:
243 **		forkflag -- TRUE if the queue scanning should be done in
244 **			a child process.  We double-fork so it is not our
245 **			child and we don't have to clean up after it.
246 **
247 **	Returns:
248 **		none.
249 **
250 **	Side Effects:
251 **		runs things in the mail queue.
252 */
253 
254 runqueue(forkflag)
255 	bool forkflag;
256 {
257 	extern bool shouldqueue();
258 
259 	/*
260 	**  If no work will ever be selected, don't even bother reading
261 	**  the queue.
262 	*/
263 
264 	if (shouldqueue(-100000000L))
265 	{
266 		if (Verbose)
267 			printf("Skipping queue run -- load average too high\n");
268 
269 		if (forkflag)
270 			return;
271 		finis();
272 	}
273 
274 	/*
275 	**  See if we want to go off and do other useful work.
276 	*/
277 
278 	if (forkflag)
279 	{
280 		int pid;
281 
282 		pid = dofork();
283 		if (pid != 0)
284 		{
285 			extern reapchild();
286 
287 			/* parent -- pick up intermediate zombie */
288 #ifndef SIGCHLD
289 			(void) waitfor(pid);
290 #else SIGCHLD
291 			(void) signal(SIGCHLD, reapchild);
292 #endif SIGCHLD
293 			if (QueueIntvl != 0)
294 				(void) setevent(QueueIntvl, runqueue, TRUE);
295 			return;
296 		}
297 		/* child -- double fork */
298 #ifndef SIGCHLD
299 		if (fork() != 0)
300 			exit(EX_OK);
301 #else SIGCHLD
302 		(void) signal(SIGCHLD, SIG_DFL);
303 #endif SIGCHLD
304 	}
305 
306 	setproctitle("running queue");
307 
308 # ifdef LOG
309 	if (LogLevel > 11)
310 		syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid());
311 # endif LOG
312 
313 	/*
314 	**  Release any resources used by the daemon code.
315 	*/
316 
317 # ifdef DAEMON
318 	clrdaemon();
319 # endif DAEMON
320 
321 	/*
322 	**  Start making passes through the queue.
323 	**	First, read and sort the entire queue.
324 	**	Then, process the work in that order.
325 	**		But if you take too long, start over.
326 	*/
327 
328 	/* order the existing work requests */
329 	(void) orderq(FALSE);
330 
331 	/* process them once at a time */
332 	while (WorkQ != NULL)
333 	{
334 		WORK *w = WorkQ;
335 
336 		WorkQ = WorkQ->w_next;
337 		dowork(w);
338 		free(w->w_name);
339 		free((char *) w);
340 	}
341 	finis();
342 }
343 /*
344 **  ORDERQ -- order the work queue.
345 **
346 **	Parameters:
347 **		doall -- if set, include everything in the queue (even
348 **			the jobs that cannot be run because the load
349 **			average is too high).  Otherwise, exclude those
350 **			jobs.
351 **
352 **	Returns:
353 **		The number of request in the queue (not necessarily
354 **		the number of requests in WorkQ however).
355 **
356 **	Side Effects:
357 **		Sets WorkQ to the queue of available work, in order.
358 */
359 
360 # define WLSIZE		120	/* max size of worklist per sort */
361 
362 orderq(doall)
363 	bool doall;
364 {
365 	register struct direct *d;
366 	register WORK *w;
367 	DIR *f;
368 	register int i;
369 	WORK wlist[WLSIZE+1];
370 	int wn = -1;
371 	extern workcmpf();
372 
373 	/* clear out old WorkQ */
374 	for (w = WorkQ; w != NULL; )
375 	{
376 		register WORK *nw = w->w_next;
377 
378 		WorkQ = nw;
379 		free(w->w_name);
380 		free((char *) w);
381 		w = nw;
382 	}
383 
384 	/* open the queue directory */
385 	f = opendir(".");
386 	if (f == NULL)
387 	{
388 		syserr("orderq: cannot open \"%s\" as \".\"", QueueDir);
389 		return (0);
390 	}
391 
392 	/*
393 	**  Read the work directory.
394 	*/
395 
396 	while ((d = readdir(f)) != NULL)
397 	{
398 		FILE *cf;
399 		char lbuf[MAXNAME];
400 
401 		/* is this an interesting entry? */
402 		if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
403 			continue;
404 
405 		/* yes -- open control file (if not too many files) */
406 		if (++wn >= WLSIZE)
407 			continue;
408 		cf = fopen(d->d_name, "r");
409 		if (cf == NULL)
410 		{
411 			/* this may be some random person sending hir msgs */
412 			/* syserr("orderq: cannot open %s", cbuf); */
413 #ifdef DEBUG
414 			if (tTd(41, 2))
415 				printf("orderq: cannot open %s (%d)\n",
416 					d->d_name, errno);
417 #endif DEBUG
418 			errno = 0;
419 			wn--;
420 			continue;
421 		}
422 		wlist[wn].w_name = newstr(d->d_name);
423 
424 		/* make sure jobs in creation don't clog queue */
425 		wlist[wn].w_pri = 0x7fffffff;
426 		wlist[wn].w_ctime = 0;
427 
428 		/* extract useful information */
429 		while (fgets(lbuf, sizeof lbuf, cf) != NULL)
430 		{
431 			extern long atol();
432 
433 			switch (lbuf[0])
434 			{
435 			  case 'P':
436 				wlist[wn].w_pri = atol(&lbuf[1]);
437 				break;
438 
439 			  case 'T':
440 				wlist[wn].w_ctime = atol(&lbuf[1]);
441 				break;
442 			}
443 		}
444 		(void) fclose(cf);
445 
446 		if (!doall && shouldqueue(wlist[wn].w_pri))
447 		{
448 			/* don't even bother sorting this job in */
449 			wn--;
450 		}
451 	}
452 	(void) closedir(f);
453 	wn++;
454 
455 	/*
456 	**  Sort the work directory.
457 	*/
458 
459 	qsort((char *) wlist, min(wn, WLSIZE), sizeof *wlist, workcmpf);
460 
461 	/*
462 	**  Convert the work list into canonical form.
463 	**	Should be turning it into a list of envelopes here perhaps.
464 	*/
465 
466 	WorkQ = NULL;
467 	for (i = min(wn, WLSIZE); --i >= 0; )
468 	{
469 		w = (WORK *) xalloc(sizeof *w);
470 		w->w_name = wlist[i].w_name;
471 		w->w_pri = wlist[i].w_pri;
472 		w->w_ctime = wlist[i].w_ctime;
473 		w->w_next = WorkQ;
474 		WorkQ = w;
475 	}
476 
477 # ifdef DEBUG
478 	if (tTd(40, 1))
479 	{
480 		for (w = WorkQ; w != NULL; w = w->w_next)
481 			printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
482 	}
483 # endif DEBUG
484 
485 	return (wn);
486 }
487 /*
488 **  WORKCMPF -- compare function for ordering work.
489 **
490 **	Parameters:
491 **		a -- the first argument.
492 **		b -- the second argument.
493 **
494 **	Returns:
495 **		-1 if a < b
496 **		 0 if a == b
497 **		+1 if a > b
498 **
499 **	Side Effects:
500 **		none.
501 */
502 
503 workcmpf(a, b)
504 	register WORK *a;
505 	register WORK *b;
506 {
507 	long pa = a->w_pri + a->w_ctime;
508 	long pb = b->w_pri + b->w_ctime;
509 
510 	if (pa == pb)
511 		return (0);
512 	else if (pa > pb)
513 		return (1);
514 	else
515 		return (-1);
516 }
517 /*
518 **  DOWORK -- do a work request.
519 **
520 **	Parameters:
521 **		w -- the work request to be satisfied.
522 **
523 **	Returns:
524 **		none.
525 **
526 **	Side Effects:
527 **		The work request is satisfied if possible.
528 */
529 
530 dowork(w)
531 	register WORK *w;
532 {
533 	register int i;
534 	extern bool shouldqueue();
535 
536 # ifdef DEBUG
537 	if (tTd(40, 1))
538 		printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);
539 # endif DEBUG
540 
541 	/*
542 	**  Ignore jobs that are too expensive for the moment.
543 	*/
544 
545 	if (shouldqueue(w->w_pri))
546 	{
547 		if (Verbose)
548 			printf("\nSkipping %s\n", w->w_name + 2);
549 		return;
550 	}
551 
552 	/*
553 	**  Fork for work.
554 	*/
555 
556 	if (ForkQueueRuns)
557 	{
558 		i = fork();
559 		if (i < 0)
560 		{
561 			syserr("dowork: cannot fork");
562 			return;
563 		}
564 	}
565 	else
566 	{
567 		i = 0;
568 	}
569 
570 	if (i == 0)
571 	{
572 		/*
573 		**  CHILD
574 		**	Lock the control file to avoid duplicate deliveries.
575 		**		Then run the file as though we had just read it.
576 		**	We save an idea of the temporary name so we
577 		**		can recover on interrupt.
578 		*/
579 
580 		/* set basic modes, etc. */
581 		(void) alarm(0);
582 		clearenvelope(CurEnv);
583 		QueueRun = TRUE;
584 		ErrorMode = EM_MAIL;
585 		CurEnv->e_id = &w->w_name[2];
586 # ifdef LOG
587 		if (LogLevel > 11)
588 			syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id,
589 			       getpid());
590 # endif LOG
591 
592 		/* don't use the headers from sendmail.cf... */
593 		CurEnv->e_header = NULL;
594 
595 		/* lock the control file during processing */
596 		if (link(w->w_name, queuename(CurEnv, 'l')) < 0)
597 		{
598 			/* being processed by another queuer */
599 # ifdef LOG
600 			if (LogLevel > 4)
601 				syslog(LOG_DEBUG, "%s: locked", CurEnv->e_id);
602 # endif LOG
603 			if (ForkQueueRuns)
604 				exit(EX_OK);
605 			else
606 				return;
607 		}
608 
609 		/* do basic system initialization */
610 		initsys();
611 
612 		/* read the queue control file */
613 		readqf(CurEnv, TRUE);
614 		CurEnv->e_flags |= EF_INQUEUE;
615 		eatheader(CurEnv);
616 
617 		/* do the delivery */
618 		if (!bitset(EF_FATALERRS, CurEnv->e_flags))
619 			sendall(CurEnv, SM_DELIVER);
620 
621 		/* finish up and exit */
622 		if (ForkQueueRuns)
623 			finis();
624 		else
625 			dropenvelope(CurEnv);
626 	}
627 	else
628 	{
629 		/*
630 		**  Parent -- pick up results.
631 		*/
632 
633 		errno = 0;
634 		(void) waitfor(i);
635 	}
636 }
637 /*
638 **  READQF -- read queue file and set up environment.
639 **
640 **	Parameters:
641 **		e -- the envelope of the job to run.
642 **		full -- if set, read in all information.  Otherwise just
643 **			read in info needed for a queue print.
644 **
645 **	Returns:
646 **		none.
647 **
648 **	Side Effects:
649 **		cf is read and created as the current job, as though
650 **		we had been invoked by argument.
651 */
652 
653 readqf(e, full)
654 	register ENVELOPE *e;
655 	bool full;
656 {
657 	char *qf;
658 	register FILE *qfp;
659 	char buf[MAXFIELD];
660 	extern char *fgetfolded();
661 	extern long atol();
662 
663 	/*
664 	**  Read and process the file.
665 	*/
666 
667 	qf = queuename(e, 'q');
668 	qfp = fopen(qf, "r");
669 	if (qfp == NULL)
670 	{
671 		syserr("readqf: no control file %s", qf);
672 		return;
673 	}
674 	FileName = qf;
675 	LineNumber = 0;
676 	if (Verbose && full)
677 		printf("\nRunning %s\n", e->e_id);
678 	while (fgetfolded(buf, sizeof buf, qfp) != NULL)
679 	{
680 		switch (buf[0])
681 		{
682 		  case 'R':		/* specify recipient */
683 			sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue);
684 			break;
685 
686 		  case 'H':		/* header */
687 			if (full)
688 				(void) chompheader(&buf[1], FALSE);
689 			break;
690 
691 		  case 'M':		/* message */
692 			e->e_message = newstr(&buf[1]);
693 			break;
694 
695 		  case 'S':		/* sender */
696 			setsender(newstr(&buf[1]));
697 			break;
698 
699 		  case 'D':		/* data file name */
700 			if (!full)
701 				break;
702 			e->e_df = newstr(&buf[1]);
703 			e->e_dfp = fopen(e->e_df, "r");
704 			if (e->e_dfp == NULL)
705 				syserr("readqf: cannot open %s", e->e_df);
706 			break;
707 
708 		  case 'T':		/* init time */
709 			e->e_ctime = atol(&buf[1]);
710 			break;
711 
712 		  case 'P':		/* message priority */
713 			e->e_msgpriority = atol(&buf[1]) + WkTimeFact;
714 			break;
715 
716 		  case '\0':		/* blank line; ignore */
717 			break;
718 
719 		  default:
720 			syserr("readqf(%s:%d): bad line \"%s\"", e->e_id,
721 				LineNumber, buf);
722 			break;
723 		}
724 	}
725 
726 	(void) fclose(qfp);
727 	FileName = NULL;
728 
729 	/*
730 	**  If we haven't read any lines, this queue file is empty.
731 	**  Arrange to remove it without referencing any null pointers.
732 	*/
733 
734 	if (LineNumber == 0)
735 	{
736 		errno = 0;
737 		e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
738 	}
739 }
740 /*
741 **  PRINTQUEUE -- print out a representation of the mail queue
742 **
743 **	Parameters:
744 **		none.
745 **
746 **	Returns:
747 **		none.
748 **
749 **	Side Effects:
750 **		Prints a listing of the mail queue on the standard output.
751 */
752 
753 printqueue()
754 {
755 	register WORK *w;
756 	FILE *f;
757 	int nrequests;
758 	char buf[MAXLINE];
759 
760 	/*
761 	**  Read and order the queue.
762 	*/
763 
764 	nrequests = orderq(TRUE);
765 
766 	/*
767 	**  Print the work list that we have read.
768 	*/
769 
770 	/* first see if there is anything */
771 	if (nrequests <= 0)
772 	{
773 		printf("Mail queue is empty\n");
774 		return;
775 	}
776 
777 	printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
778 	if (nrequests > WLSIZE)
779 		printf(", only %d printed", WLSIZE);
780 	if (Verbose)
781 		printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n");
782 	else
783 		printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
784 	for (w = WorkQ; w != NULL; w = w->w_next)
785 	{
786 		struct stat st;
787 		auto time_t submittime = 0;
788 		long dfsize = -1;
789 		char lf[20];
790 		char message[MAXLINE];
791 		extern bool shouldqueue();
792 
793 		f = fopen(w->w_name, "r");
794 		if (f == NULL)
795 		{
796 			errno = 0;
797 			continue;
798 		}
799 		printf("%7s", w->w_name + 2);
800 		(void) strcpy(lf, w->w_name);
801 		lf[0] = 'l';
802 		if (stat(lf, &st) >= 0)
803 			printf("*");
804 		else if (shouldqueue(w->w_pri))
805 			printf("X");
806 		else
807 			printf(" ");
808 		errno = 0;
809 
810 		message[0] = '\0';
811 		while (fgets(buf, sizeof buf, f) != NULL)
812 		{
813 			fixcrlf(buf, TRUE);
814 			switch (buf[0])
815 			{
816 			  case 'M':	/* error message */
817 				(void) strcpy(message, &buf[1]);
818 				break;
819 
820 			  case 'S':	/* sender name */
821 				if (Verbose)
822 					printf("%8ld %10ld %.12s %.38s", dfsize,
823 					    w->w_pri, ctime(&submittime) + 4,
824 					    &buf[1]);
825 				else
826 					printf("%8ld %.16s %.45s", dfsize,
827 					    ctime(&submittime), &buf[1]);
828 				if (message[0] != '\0')
829 					printf("\n\t\t (%.60s)", message);
830 				break;
831 
832 			  case 'R':	/* recipient name */
833 				if (Verbose)
834 					printf("\n\t\t\t\t\t %.38s", &buf[1]);
835 				else
836 					printf("\n\t\t\t\t  %.45s", &buf[1]);
837 				break;
838 
839 			  case 'T':	/* creation time */
840 				submittime = atol(&buf[1]);
841 				break;
842 
843 			  case 'D':	/* data file name */
844 				if (stat(&buf[1], &st) >= 0)
845 					dfsize = st.st_size;
846 				break;
847 			}
848 		}
849 		if (submittime == (time_t) 0)
850 			printf(" (no control file)");
851 		printf("\n");
852 		(void) fclose(f);
853 	}
854 }
855 
856 # endif QUEUE
857 /*
858 **  QUEUENAME -- build a file name in the queue directory for this envelope.
859 **
860 **	Assigns an id code if one does not already exist.
861 **	This code is very careful to avoid trashing existing files
862 **	under any circumstances.
863 **		We first create an nf file that is only used when
864 **		assigning an id.  This file is always empty, so that
865 **		we can never accidently truncate an lf file.
866 **
867 **	Parameters:
868 **		e -- envelope to build it in/from.
869 **		type -- the file type, used as the first character
870 **			of the file name.
871 **
872 **	Returns:
873 **		a pointer to the new file name (in a static buffer).
874 **
875 **	Side Effects:
876 **		Will create the lf and qf files if no id code is
877 **		already assigned.  This will cause the envelope
878 **		to be modified.
879 */
880 
881 char *
882 queuename(e, type)
883 	register ENVELOPE *e;
884 	char type;
885 {
886 	static char buf[MAXNAME];
887 	static int pid = -1;
888 	char c1 = 'A';
889 	char c2 = 'A';
890 
891 	if (e->e_id == NULL)
892 	{
893 		char qf[20];
894 		char nf[20];
895 		char lf[20];
896 
897 		/* find a unique id */
898 		if (pid != getpid())
899 		{
900 			/* new process -- start back at "AA" */
901 			pid = getpid();
902 			c1 = 'A';
903 			c2 = 'A' - 1;
904 		}
905 		(void) sprintf(qf, "qfAA%05d", pid);
906 		(void) strcpy(lf, qf);
907 		lf[0] = 'l';
908 		(void) strcpy(nf, qf);
909 		nf[0] = 'n';
910 
911 		while (c1 < '~' || c2 < 'Z')
912 		{
913 			int i;
914 
915 			if (c2 >= 'Z')
916 			{
917 				c1++;
918 				c2 = 'A' - 1;
919 			}
920 			lf[2] = nf[2] = qf[2] = c1;
921 			lf[3] = nf[3] = qf[3] = ++c2;
922 # ifdef DEBUG
923 			if (tTd(7, 20))
924 				printf("queuename: trying \"%s\"\n", nf);
925 # endif DEBUG
926 
927 # ifdef QUEUE
928 			if (access(lf, 0) >= 0 || access(qf, 0) >= 0)
929 				continue;
930 			errno = 0;
931 			i = creat(nf, FileMode);
932 			if (i < 0)
933 			{
934 				(void) unlink(nf);	/* kernel bug */
935 				continue;
936 			}
937 			(void) close(i);
938 			i = link(nf, lf);
939 			(void) unlink(nf);
940 			if (i < 0)
941 				continue;
942 			if (link(lf, qf) >= 0)
943 				break;
944 			(void) unlink(lf);
945 # else QUEUE
946 			if (close(creat(qf, FileMode)) >= 0)
947 				break;
948 # endif QUEUE
949 		}
950 		if (c1 >= '~' && c2 >= 'Z')
951 		{
952 			syserr("queuename: Cannot create \"%s\" in \"%s\"",
953 				qf, QueueDir);
954 			exit(EX_OSERR);
955 		}
956 		e->e_id = newstr(&qf[2]);
957 		define('i', e->e_id, e);
958 # ifdef DEBUG
959 		if (tTd(7, 1))
960 			printf("queuename: assigned id %s, env=%x\n", e->e_id, e);
961 # ifdef LOG
962 		if (LogLevel > 16)
963 			syslog(LOG_DEBUG, "%s: assigned id", e->e_id);
964 # endif LOG
965 # endif DEBUG
966 	}
967 
968 	if (type == '\0')
969 		return (NULL);
970 	(void) sprintf(buf, "%cf%s", type, e->e_id);
971 # ifdef DEBUG
972 	if (tTd(7, 2))
973 		printf("queuename: %s\n", buf);
974 # endif DEBUG
975 	return (buf);
976 }
977 /*
978 **  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
979 **
980 **	Parameters:
981 **		e -- the envelope to unlock.
982 **
983 **	Returns:
984 **		none
985 **
986 **	Side Effects:
987 **		unlocks the queue for `e'.
988 */
989 
990 unlockqueue(e)
991 	ENVELOPE *e;
992 {
993 	/* remove the transcript */
994 #ifdef DEBUG
995 # ifdef LOG
996 	if (LogLevel > 19)
997 		syslog(LOG_DEBUG, "%s: unlock", e->e_id);
998 # endif LOG
999 	if (!tTd(51, 4))
1000 #endif DEBUG
1001 		xunlink(queuename(e, 'x'));
1002 
1003 # ifdef QUEUE
1004 	/* last but not least, remove the lock */
1005 	xunlink(queuename(e, 'l'));
1006 # endif QUEUE
1007 }
1008