xref: /csrg-svn/usr.sbin/sendmail/src/queue.c (revision 10205)
1 # include "sendmail.h"
2 # include <sys/stat.h>
3 # include <dir.h>
4 # include <signal.h>
5 # include <errno.h>
6 
7 # ifndef QUEUE
8 SCCSID(@(#)queue.c	3.69		01/08/83	(no queueing));
9 # else QUEUE
10 
11 SCCSID(@(#)queue.c	3.69		01/08/83);
12 
13 /*
14 **  Work queue.
15 */
16 
17 struct work
18 {
19 	char		*w_name;	/* name of control file */
20 	long		w_pri;		/* priority of message, see below */
21 	struct work	*w_next;	/* next in queue */
22 };
23 
24 typedef struct work	WORK;
25 
26 WORK	*WorkQ;			/* queue of things to be done */
27 /*
28 **  QUEUEUP -- queue a message up for future transmission.
29 **
30 **	Parameters:
31 **		e -- the envelope to queue up.
32 **		queueall -- if TRUE, queue all addresses, rather than
33 **			just those with the QQUEUEUP flag set.
34 **		announce -- if TRUE, tell when you are queueing up.
35 **
36 **	Returns:
37 **		none.
38 **
39 **	Side Effects:
40 **		The current request are saved in a control file.
41 */
42 
43 queueup(e, queueall, announce)
44 	register ENVELOPE *e;
45 	bool queueall;
46 	bool announce;
47 {
48 	char *tf;
49 	char *qf;
50 	char buf[MAXLINE];
51 	register FILE *tfp;
52 	register HDR *h;
53 	register ADDRESS *q;
54 	MAILER nullmailer;
55 
56 	/*
57 	**  Create control file.
58 	*/
59 
60 	tf = newstr(queuename(e, 't'));
61 	tfp = fopen(tf, "w");
62 	if (tfp == NULL)
63 	{
64 		syserr("queueup: cannot create temp file %s", tf);
65 		return;
66 	}
67 	(void) chmod(tf, FileMode);
68 
69 # ifdef DEBUG
70 	if (tTd(40, 1))
71 		printf("queueing in %s\n", tf);
72 # endif DEBUG
73 
74 	/*
75 	**  If there is no data file yet, create one.
76 	*/
77 
78 	if (e->e_df == NULL)
79 	{
80 		register FILE *dfp;
81 		extern putbody();
82 
83 		e->e_df = newstr(queuename(e, 'd'));
84 		dfp = fopen(e->e_df, "w");
85 		if (dfp == NULL)
86 		{
87 			syserr("queueup: cannot create %s", e->e_df);
88 			(void) fclose(tfp);
89 			return;
90 		}
91 		(void) chmod(e->e_df, FileMode);
92 		(*e->e_putbody)(dfp, ProgMailer, e);
93 		(void) fclose(dfp);
94 		e->e_putbody = putbody;
95 	}
96 
97 	/*
98 	**  Output future work requests.
99 	**	Priority should be first, since it is read by orderq.
100 	*/
101 
102 	/* output message priority */
103 	fprintf(tfp, "P%ld\n", e->e_msgpriority);
104 
105 	/* output creation time */
106 	fprintf(tfp, "T%ld\n", e->e_ctime);
107 
108 	/* output name of data file */
109 	fprintf(tfp, "D%s\n", e->e_df);
110 
111 	/* message from envelope, if it exists */
112 	if (e->e_message != NULL)
113 		fprintf(tfp, "M%s\n", e->e_message);
114 
115 	/* output name of sender */
116 	fprintf(tfp, "S%s\n", e->e_from.q_paddr);
117 
118 	/* output list of recipient addresses */
119 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
120 	{
121 		if (queueall ? !bitset(QDONTSEND, q->q_flags) :
122 			       bitset(QQUEUEUP, q->q_flags))
123 		{
124 			fprintf(tfp, "R%s\n", q->q_paddr);
125 			if (announce)
126 			{
127 				e->e_to = q->q_paddr;
128 				message(Arpa_Info, "queued");
129 				if (LogLevel > 4)
130 					logdelivery("queued");
131 				e->e_to = NULL;
132 			}
133 #ifdef DEBUG
134 			if (tTd(40, 1))
135 			{
136 				printf("queueing ");
137 				printaddr(q, FALSE);
138 			}
139 #endif DEBUG
140 		}
141 	}
142 
143 	/*
144 	**  Output headers for this message.
145 	**	Expand macros completely here.  Queue run will deal with
146 	**	everything as absolute headers.
147 	**		All headers that must be relative to the recipient
148 	**		can be cracked later.
149 	**	We set up a "null mailer" -- i.e., a mailer that will have
150 	**	no effect on the addresses as they are output.
151 	*/
152 
153 	bzero(&nullmailer, sizeof nullmailer);
154 	nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1;
155 
156 	define('g', "$f", e);
157 	for (h = e->e_header; h != NULL; h = h->h_link)
158 	{
159 		if (h->h_value == NULL || h->h_value[0] == '\0')
160 			continue;
161 		fprintf(tfp, "H");
162 		if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags))
163 			mfdecode(h->h_mflags, tfp);
164 		if (bitset(H_DEFAULT, h->h_flags))
165 		{
166 			(void) expand(h->h_value, buf, &buf[sizeof buf], e);
167 			fprintf(tfp, "%s: %s\n", h->h_field, buf);
168 		}
169 		else if (bitset(H_FROM|H_RCPT, h->h_flags))
170 		{
171 			commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags),
172 				 &nullmailer);
173 		}
174 		else
175 			fprintf(tfp, "%s: %s\n", h->h_field, h->h_value);
176 	}
177 
178 	/*
179 	**  Clean up.
180 	*/
181 
182 	(void) fclose(tfp);
183 	qf = queuename(e, 'q');
184 	holdsigs();
185 	(void) unlink(qf);
186 	if (link(tf, qf) < 0)
187 		syserr("cannot link(%s, %s), df=%s", tf, qf, e->e_df);
188 	else
189 		(void) unlink(tf);
190 	rlsesigs();
191 
192 # ifdef LOG
193 	/* save log info */
194 	if (LogLevel > 15)
195 		syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
196 # endif LOG
197 }
198 /*
199 **  RUNQUEUE -- run the jobs in the queue.
200 **
201 **	Gets the stuff out of the queue in some presumably logical
202 **	order and processes them.
203 **
204 **	Parameters:
205 **		none.
206 **
207 **	Returns:
208 **		none.
209 **
210 **	Side Effects:
211 **		runs things in the mail queue.
212 */
213 
214 runqueue(forkflag)
215 	bool forkflag;
216 {
217 	/*
218 	**  See if we want to go off and do other useful work.
219 	*/
220 
221 	if (forkflag)
222 	{
223 		int pid;
224 
225 		pid = dofork();
226 		if (pid != 0)
227 		{
228 			/* parent -- pick up intermediate zombie */
229 			(void) waitfor(pid);
230 			if (QueueIntvl != 0)
231 				(void) setevent(QueueIntvl, runqueue, TRUE);
232 			return;
233 		}
234 		/* child -- double fork */
235 		if (fork() != 0)
236 			exit(EX_OK);
237 	}
238 # ifdef LOG
239 	if (LogLevel > 11)
240 		syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid());
241 # endif LOG
242 
243 	/*
244 	**  Release any resources used by the daemon code.
245 	*/
246 
247 # ifdef DAEMON
248 	clrdaemon();
249 # endif DAEMON
250 
251 	/*
252 	**  Start making passes through the queue.
253 	**	First, read and sort the entire queue.
254 	**	Then, process the work in that order.
255 	**		But if you take too long, start over.
256 	*/
257 
258 	/* order the existing work requests */
259 	(void) orderq();
260 
261 	/* process them once at a time */
262 	while (WorkQ != NULL)
263 	{
264 		WORK *w = WorkQ;
265 
266 		WorkQ = WorkQ->w_next;
267 		dowork(w);
268 		free(w->w_name);
269 		free((char *) w);
270 	}
271 	finis();
272 }
273 /*
274 **  ORDERQ -- order the work queue.
275 **
276 **	Parameters:
277 **		none.
278 **
279 **	Returns:
280 **		The number of request in the queue (not necessarily
281 **		the number of requests in WorkQ however).
282 **
283 **	Side Effects:
284 **		Sets WorkQ to the queue of available work, in order.
285 */
286 
287 # define WLSIZE		120	/* max size of worklist per sort */
288 
289 orderq()
290 {
291 	register struct direct *d;
292 	register WORK *w;
293 	register WORK **wp;		/* parent of w */
294 	DIR *f;
295 	register int i;
296 	WORK wlist[WLSIZE+1];
297 	int wn = -1;
298 	extern workcmpf();
299 
300 	/* clear out old WorkQ */
301 	for (w = WorkQ; w != NULL; )
302 	{
303 		register WORK *nw = w->w_next;
304 
305 		WorkQ = nw;
306 		free(w->w_name);
307 		free((char *) w);
308 		w = nw;
309 	}
310 
311 	/* open the queue directory */
312 	f = opendir(".");
313 	if (f == NULL)
314 	{
315 		syserr("orderq: cannot open \"%s\" as \".\"", QueueDir);
316 		return (0);
317 	}
318 
319 	/*
320 	**  Read the work directory.
321 	*/
322 
323 	while ((d = readdir(f)) != NULL)
324 	{
325 		FILE *cf;
326 		char lbuf[MAXNAME];
327 
328 		/* is this an interesting entry? */
329 		if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
330 			continue;
331 
332 		/* yes -- open control file (if not too many files) */
333 		if (++wn >= WLSIZE)
334 			continue;
335 		cf = fopen(d->d_name, "r");
336 		if (cf == NULL)
337 		{
338 			/* this may be some random person sending hir msgs */
339 			/* syserr("orderq: cannot open %s", cbuf); */
340 #ifdef DEBUG
341 			if (tTd(41, 2))
342 				printf("orderq: cannot open %s (%d)\n",
343 					d->d_name, errno);
344 #endif DEBUG
345 			errno = 0;
346 			wn--;
347 			continue;
348 		}
349 		wlist[wn].w_name = newstr(d->d_name);
350 
351 		/* extract useful information */
352 		while (fgets(lbuf, sizeof lbuf, cf) != NULL)
353 		{
354 			if (lbuf[0] == 'P')
355 			{
356 				(void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri);
357 				break;
358 			}
359 		}
360 		(void) fclose(cf);
361 	}
362 	(void) closedir(f);
363 	wn++;
364 
365 	/*
366 	**  Sort the work directory.
367 	*/
368 
369 	qsort(wlist, min(wn, WLSIZE), sizeof *wlist, workcmpf);
370 
371 	/*
372 	**  Convert the work list into canonical form.
373 	**	Should be turning it into a list of envelopes here perhaps.
374 	*/
375 
376 	wp = &WorkQ;
377 	for (i = min(wn, WLSIZE); --i >= 0; )
378 	{
379 		w = (WORK *) xalloc(sizeof *w);
380 		w->w_name = wlist[i].w_name;
381 		w->w_pri = wlist[i].w_pri;
382 		w->w_next = NULL;
383 		*wp = w;
384 		wp = &w->w_next;
385 	}
386 
387 # ifdef DEBUG
388 	if (tTd(40, 1))
389 	{
390 		for (w = WorkQ; w != NULL; w = w->w_next)
391 			printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
392 	}
393 # endif DEBUG
394 
395 	return (wn);
396 }
397 /*
398 **  WORKCMPF -- compare function for ordering work.
399 **
400 **	Parameters:
401 **		a -- the first argument.
402 **		b -- the second argument.
403 **
404 **	Returns:
405 **		1 if a < b
406 **		0 if a == b
407 **		-1 if a > b
408 **
409 **	Side Effects:
410 **		none.
411 */
412 
413 workcmpf(a, b)
414 	register WORK *a;
415 	register WORK *b;
416 {
417 	if (a->w_pri == b->w_pri)
418 		return (0);
419 	else if (a->w_pri > b->w_pri)
420 		return (-1);
421 	else
422 		return (1);
423 }
424 /*
425 **  DOWORK -- do a work request.
426 **
427 **	Parameters:
428 **		w -- the work request to be satisfied.
429 **
430 **	Returns:
431 **		none.
432 **
433 **	Side Effects:
434 **		The work request is satisfied if possible.
435 */
436 
437 dowork(w)
438 	register WORK *w;
439 {
440 	register int i;
441 
442 # ifdef DEBUG
443 	if (tTd(40, 1))
444 		printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);
445 # endif DEBUG
446 
447 	/*
448 	**  Fork for work.
449 	*/
450 
451 	i = fork();
452 	if (i < 0)
453 	{
454 		syserr("dowork: cannot fork");
455 		return;
456 	}
457 
458 	if (i == 0)
459 	{
460 		/*
461 		**  CHILD
462 		**	Lock the control file to avoid duplicate deliveries.
463 		**		Then run the file as though we had just read it.
464 		**	We save an idea of the temporary name so we
465 		**		can recover on interrupt.
466 		*/
467 
468 		/* set basic modes, etc. */
469 		(void) alarm(0);
470 		closexscript(CurEnv);
471 		CurEnv->e_flags &= ~EF_FATALERRS;
472 		QueueRun = TRUE;
473 		ErrorMode = EM_MAIL;
474 		CurEnv->e_id = &w->w_name[2];
475 # ifdef LOG
476 		if (LogLevel > 11)
477 			syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id,
478 			       getpid());
479 # endif LOG
480 
481 		/* don't use the headers from sendmail.cf... */
482 		CurEnv->e_header = NULL;
483 		(void) chompheader("from: $q", TRUE);
484 
485 		/* create the link to the control file during processing */
486 		if (link(w->w_name, queuename(CurEnv, 'l')) < 0)
487 		{
488 			/* being processed by another queuer */
489 # ifdef LOG
490 			if (LogLevel > 4)
491 				syslog(LOG_DEBUG, "%s: locked", CurEnv->e_id);
492 # endif LOG
493 			exit(EX_OK);
494 		}
495 
496 		/* do basic system initialization */
497 		initsys();
498 
499 		/* read the queue control file */
500 		readqf(CurEnv, TRUE);
501 		CurEnv->e_flags |= EF_INQUEUE;
502 		eatheader(CurEnv);
503 
504 		/* do the delivery */
505 		if (!bitset(EF_FATALERRS, CurEnv->e_flags))
506 			sendall(CurEnv, SM_DELIVER);
507 
508 		/* finish up and exit */
509 		finis();
510 	}
511 
512 	/*
513 	**  Parent -- pick up results.
514 	*/
515 
516 	errno = 0;
517 	(void) waitfor(i);
518 }
519 /*
520 **  READQF -- read queue file and set up environment.
521 **
522 **	Parameters:
523 **		e -- the envelope of the job to run.
524 **		full -- if set, read in all information.  Otherwise just
525 **			read in info needed for a queue print.
526 **
527 **	Returns:
528 **		none.
529 **
530 **	Side Effects:
531 **		cf is read and created as the current job, as though
532 **		we had been invoked by argument.
533 */
534 
535 readqf(e, full)
536 	register ENVELOPE *e;
537 	bool full;
538 {
539 	register FILE *f;
540 	char buf[MAXFIELD];
541 	extern char *fgetfolded();
542 	register char *p;
543 
544 	/*
545 	**  Open the file created by queueup.
546 	*/
547 
548 	p = queuename(e, 'q');
549 	f = fopen(p, "r");
550 	if (f == NULL)
551 	{
552 		syserr("readqf: no control file %s", p);
553 		return;
554 	}
555 	FileName = p;
556 	LineNumber = 0;
557 
558 	/*
559 	**  Read and process the file.
560 	*/
561 
562 	if (Verbose && full)
563 		printf("\nRunning %s\n", e->e_id);
564 	while (fgetfolded(buf, sizeof buf, f) != NULL)
565 	{
566 		switch (buf[0])
567 		{
568 		  case 'R':		/* specify recipient */
569 			sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue);
570 			break;
571 
572 		  case 'H':		/* header */
573 			if (full)
574 				(void) chompheader(&buf[1], FALSE);
575 			break;
576 
577 		  case 'M':		/* message */
578 			e->e_message = newstr(&buf[1]);
579 			break;
580 
581 		  case 'S':		/* sender */
582 			setsender(newstr(&buf[1]));
583 			break;
584 
585 		  case 'D':		/* data file name */
586 			if (!full)
587 				break;
588 			e->e_df = newstr(&buf[1]);
589 			e->e_dfp = fopen(e->e_df, "r");
590 			if (e->e_dfp == NULL)
591 				syserr("readqf: cannot open %s", e->e_df);
592 			break;
593 
594 		  case 'T':		/* init time */
595 			(void) sscanf(&buf[1], "%ld", &e->e_ctime);
596 			break;
597 
598 		  case 'P':		/* message priority */
599 			(void) sscanf(&buf[1], "%ld", &e->e_msgpriority);
600 
601 			/* make sure that big things get sent eventually */
602 			e->e_msgpriority -= WKTIMEFACT;
603 			break;
604 
605 		  default:
606 			syserr("readqf(%s): bad line \"%s\"", e->e_id, buf);
607 			break;
608 		}
609 	}
610 
611 	FileName = NULL;
612 }
613 /*
614 **  PRINTQUEUE -- print out a representation of the mail queue
615 **
616 **	Parameters:
617 **		none.
618 **
619 **	Returns:
620 **		none.
621 **
622 **	Side Effects:
623 **		Prints a listing of the mail queue on the standard output.
624 */
625 
626 printqueue()
627 {
628 	register WORK *w;
629 	FILE *f;
630 	int nrequests;
631 	char buf[MAXLINE];
632 
633 	/*
634 	**  Read and order the queue.
635 	*/
636 
637 	nrequests = orderq();
638 
639 	/*
640 	**  Print the work list that we have read.
641 	*/
642 
643 	/* first see if there is anything */
644 	if (nrequests <= 0)
645 	{
646 		printf("Mail queue is empty\n");
647 		return;
648 	}
649 
650 	printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
651 	if (nrequests > WLSIZE)
652 		printf(", only %d printed", WLSIZE);
653 	printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
654 	for (w = WorkQ; w != NULL; w = w->w_next)
655 	{
656 		struct stat st;
657 		auto time_t submittime = 0;
658 		long dfsize = -1;
659 		char lf[20];
660 		char message[MAXLINE];
661 
662 		printf("%7s", w->w_name + 2);
663 		strcpy(lf, w->w_name);
664 		lf[0] = 'l';
665 		if (stat(lf, &st) >= 0)
666 			printf("*");
667 		else
668 			printf(" ");
669 		errno = 0;
670 		f = fopen(w->w_name, "r");
671 		if (f == NULL)
672 		{
673 			printf(" (finished)\n");
674 			errno = 0;
675 			continue;
676 		}
677 		message[0] = '\0';
678 		while (fgets(buf, sizeof buf, f) != NULL)
679 		{
680 			fixcrlf(buf, TRUE);
681 			switch (buf[0])
682 			{
683 			  case 'M':	/* error message */
684 				strcpy(message, &buf[1]);
685 				break;
686 
687 			  case 'S':	/* sender name */
688 				if (message[0] != '\0')
689 				{
690 					(void) strcat(buf, " (");
691 					(void) strcat(buf, message);
692 					(void) strcat(buf, ")");
693 				}
694 				printf("%8d %.16s %.40s", dfsize,
695 					ctime(&submittime), &buf[1]);
696 				break;
697 
698 			  case 'R':	/* recipient name */
699 				printf("\n\t\t\t\t  %.40s", &buf[1]);
700 				break;
701 
702 			  case 'T':	/* creation time */
703 				sscanf(&buf[1], "%ld", &submittime);
704 				break;
705 
706 			  case 'D':	/* data file name */
707 				if (stat(&buf[1], &st) >= 0)
708 					dfsize = st.st_size;
709 				break;
710 			}
711 		}
712 		if (submittime == (time_t) 0)
713 			printf(" (no control file)");
714 		printf("\n");
715 		fclose(f);
716 	}
717 }
718 
719 # endif QUEUE
720