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.52		11/24/82	(no queueing));
9 # else QUEUE
10 
11 SCCSID(@(#)queue.c	3.52		11/24/82);
12 
13 /*
14 **  QUEUEUP -- queue a message up for future transmission.
15 **
16 **	The queued message should already be in the correct place.
17 **	This routine just outputs the control file as appropriate.
18 **
19 **	Parameters:
20 **		e -- the envelope to queue up.
21 **		queueall -- if TRUE, queue all addresses, rather than
22 **			just those with the QQUEUEUP flag set.
23 **
24 **	Returns:
25 **		none.
26 **
27 **	Side Effects:
28 **		The current request (only unsatisfied addresses)
29 **			are saved in a control file.
30 */
31 
32 queueup(e, queueall)
33 	register ENVELOPE *e;
34 	bool queueall;
35 {
36 	char *tf;
37 	char *qf;
38 	char buf[MAXLINE];
39 	register FILE *tfp;
40 	register HDR *h;
41 	register ADDRESS *q;
42 	register int i;
43 
44 	/*
45 	**  Create control file.
46 	*/
47 
48 	tf = newstr(queuename(e, 't'));
49 	tfp = fopen(tf, "w");
50 	if (tfp == NULL)
51 	{
52 		syserr("queueup: cannot create temp file %s", tf);
53 		return;
54 	}
55 	(void) chmod(tf, FileMode);
56 
57 # ifdef DEBUG
58 	if (tTd(40, 1))
59 		printf("queueing in %s\n", tf);
60 # endif DEBUG
61 
62 	/*
63 	**  If there is no data file yet, create one.
64 	*/
65 
66 	if (e->e_df == NULL)
67 	{
68 		register FILE *dfp;
69 
70 		e->e_df = newstr(queuename(e, 'd'));
71 		dfp = fopen(e->e_df, "w");
72 		if (dfp == NULL)
73 		{
74 			syserr("queueup: cannot create %s", e->e_df);
75 			(void) fclose(tfp);
76 			return;
77 		}
78 		(void) chmod(e->e_df, FileMode);
79 		(*e->e_putbody)(dfp, Mailer[1], FALSE);
80 		(void) fclose(dfp);
81 	}
82 
83 	/*
84 	**  Output future work requests.
85 	*/
86 
87 	/* output name of data file */
88 	fprintf(tfp, "D%s\n", e->e_df);
89 
90 	/* output name of sender */
91 	fprintf(tfp, "S%s\n", e->e_from.q_paddr);
92 
93 	/* output creation time */
94 	fprintf(tfp, "T%ld\n", e->e_ctime);
95 
96 	/* output message priority */
97 	fprintf(tfp, "P%ld\n", e->e_msgpriority);
98 
99 	/* output message class */
100 	fprintf(tfp, "C%d\n", e->e_class);
101 
102 	/* output list of recipient addresses */
103 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
104 	{
105 # ifdef DEBUG
106 		if (tTd(40, 1))
107 		{
108 			printf("queueing ");
109 			printaddr(q, FALSE);
110 		}
111 # endif DEBUG
112 		if (queueall ? !bitset(QDONTSEND, q->q_flags) :
113 			       bitset(QQUEUEUP, q->q_flags))
114 		{
115 			fprintf(tfp, "R%s\n", q->q_paddr);
116 		}
117 	}
118 
119 	/* output headers for this message */
120 	define('g', "$f");
121 	for (h = e->e_header; h != NULL; h = h->h_link)
122 	{
123 		if (h->h_value == NULL || h->h_value[0] == '\0')
124 			continue;
125 		fprintf(tfp, "H");
126 		if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags))
127 			mfdecode(h->h_mflags, tfp);
128 		if (bitset(H_DEFAULT, h->h_flags))
129 		{
130 			(void) expand(h->h_value, buf, &buf[sizeof buf], e);
131 			fprintf(tfp, "%s: %s\n", h->h_field, buf);
132 		}
133 		else if (bitset(H_FROM|H_RCPT, h->h_flags))
134 			commaize(h, h->h_value, tfp,
135 				 bitset(EF_OLDSTYLE, e->e_flags), NULL);
136 		else
137 			fprintf(tfp, "%s: %s\n", h->h_field, h->h_value);
138 	}
139 
140 	/*
141 	**  Clean up.
142 	*/
143 
144 	(void) fclose(tfp);
145 	qf = queuename(e, 'q');
146 	(void) unlink(qf);
147 	if (link(tf, qf) < 0)
148 		syserr("cannot link(%s, %s), df=%s", tf, qf, e->e_df);
149 	else
150 		(void) unlink(tf);
151 
152 # ifdef LOG
153 	/* save log info */
154 	if (LogLevel > 15)
155 		syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
156 # endif LOG
157 }
158 /*
159 **  RUNQUEUE -- run the jobs in the queue.
160 **
161 **	Gets the stuff out of the queue in some presumably logical
162 **	order and processes them.
163 **
164 **	Parameters:
165 **		none.
166 **
167 **	Returns:
168 **		none.
169 **
170 **	Side Effects:
171 **		runs things in the mail queue.
172 */
173 
174 runqueue(forkflag)
175 	bool forkflag;
176 {
177 	register int i;
178 
179 	/*
180 	**  See if we want to go off and do other useful work.
181 	*/
182 
183 	if (forkflag)
184 	{
185 		int pid;
186 
187 		pid = dofork();
188 		if (pid != 0)
189 		{
190 			/* parent -- pick up intermediate zombie */
191 			do
192 			{
193 				auto int stat;
194 
195 				i = wait(&stat);
196 			} while (i >= 0 && i != pid);
197 			if (QueueIntvl != 0)
198 				setevent(QueueIntvl, runqueue, TRUE);
199 			return;
200 		}
201 		/* child -- double fork */
202 		if (fork() != 0)
203 			exit(EX_OK);
204 	}
205 # ifdef LOG
206 	if (LogLevel > 11)
207 		syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid());
208 # endif LOG
209 
210 	/*
211 	**  Start making passes through the queue.
212 	**	First, read and sort the entire queue.
213 	**	Then, process the work in that order.
214 	**		But if you take too long, start over.
215 	*/
216 
217 	/* order the existing work requests */
218 	orderq();
219 
220 	/* process them once at a time */
221 	while (WorkQ != NULL)
222 	{
223 		WORK *w = WorkQ;
224 
225 		WorkQ = WorkQ->w_next;
226 		dowork(w);
227 		free(w->w_name);
228 		free((char *) w);
229 	}
230 	finis();
231 }
232 /*
233 **  ORDERQ -- order the work queue.
234 **
235 **	Parameters:
236 **		none.
237 **
238 **	Returns:
239 **		none.
240 **
241 **	Side Effects:
242 **		Sets WorkQ to the queue of available work, in order.
243 */
244 
245 # define WLSIZE		120	/* max size of worklist per sort */
246 
247 orderq()
248 {
249 	register struct direct *d;
250 	register WORK *w;
251 	register WORK **wp;		/* parent of w */
252 	DIR *f;
253 	register int i;
254 	WORK wlist[WLSIZE];
255 	int wn = 0;
256 	extern workcmpf();
257 
258 	/* clear out old WorkQ */
259 	for (w = WorkQ; w != NULL; )
260 	{
261 		register WORK *nw = w->w_next;
262 
263 		WorkQ = nw;
264 		free(w->w_name);
265 		free((char *) w);
266 		w = nw;
267 	}
268 
269 	/* open the queue directory */
270 	f = opendir(".");
271 	if (f == NULL)
272 	{
273 		syserr("orderq: cannot open \"%s\" as \".\"", QueueDir);
274 		return;
275 	}
276 
277 	/*
278 	**  Read the work directory.
279 	*/
280 
281 	while (wn < WLSIZE && (d = readdir(f)) != NULL)
282 	{
283 		char lbuf[MAXNAME];
284 		FILE *cf;
285 
286 		/* is this an interesting entry? */
287 		if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
288 			continue;
289 
290 		/* yes -- open control file */
291 		cf = fopen(d->d_name, "r");
292 		if (cf == NULL)
293 		{
294 			/* this may be some random person sending hir msgs */
295 			/* syserr("orderq: cannot open %s", cbuf); */
296 			errno = 0;
297 			continue;
298 		}
299 		wlist[wn].w_name = newstr(d->d_name);
300 
301 		/* extract useful information */
302 		while (fgets(lbuf, sizeof lbuf, cf) != NULL)
303 		{
304 			fixcrlf(lbuf, TRUE);
305 
306 			switch (lbuf[0])
307 			{
308 			  case 'P':		/* message priority */
309 				(void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri);
310 				break;
311 			}
312 		}
313 		wn++;
314 		(void) fclose(cf);
315 	}
316 	(void) closedir(f);
317 
318 	/*
319 	**  Sort the work directory.
320 	*/
321 
322 	qsort(wlist, wn, sizeof *wlist, workcmpf);
323 
324 	/*
325 	**  Convert the work list into canonical form.
326 	*/
327 
328 	wp = &WorkQ;
329 	for (i = 0; i < wn; i++)
330 	{
331 		w = (WORK *) xalloc(sizeof *w);
332 		w->w_name = wlist[i].w_name;
333 		w->w_pri = wlist[i].w_pri;
334 		w->w_next = NULL;
335 		*wp = w;
336 		wp = &w->w_next;
337 	}
338 
339 # ifdef DEBUG
340 	if (tTd(40, 1))
341 	{
342 		for (w = WorkQ; w != NULL; w = w->w_next)
343 			printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
344 	}
345 # endif DEBUG
346 }
347 /*
348 **  WORKCMPF -- compare function for ordering work.
349 **
350 **	Parameters:
351 **		a -- the first argument.
352 **		b -- the second argument.
353 **
354 **	Returns:
355 **		-1 if a < b
356 **		0 if a == b
357 **		1 if a > b
358 **
359 **	Side Effects:
360 **		none.
361 */
362 
363 # define PRIFACT	1800		/* bytes each priority point is worth */
364 
365 workcmpf(a, b)
366 	register WORK *a;
367 	register WORK *b;
368 {
369 	if (a->w_pri == b->w_pri)
370 		return (0);
371 	else if (a->w_pri > b->w_pri)
372 		return (1);
373 	else
374 		return (-1);
375 }
376 /*
377 **  DOWORK -- do a work request.
378 **
379 **	Parameters:
380 **		w -- the work request to be satisfied.
381 **
382 **	Returns:
383 **		none.
384 **
385 **	Side Effects:
386 **		The work request is satisfied if possible.
387 */
388 
389 dowork(w)
390 	register WORK *w;
391 {
392 	register int i;
393 	auto int xstat;
394 
395 # ifdef DEBUG
396 	if (tTd(40, 1))
397 		printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);
398 # endif DEBUG
399 
400 	/*
401 	**  Fork for work.
402 	*/
403 
404 	i = fork();
405 	if (i < 0)
406 	{
407 		syserr("dowork: cannot fork");
408 		return;
409 	}
410 
411 	if (i == 0)
412 	{
413 		char buf[MAXNAME];
414 
415 		/*
416 		**  CHILD
417 		**	Lock the control file to avoid duplicate deliveries.
418 		**		Then run the file as though we had just read it.
419 		**	We save an idea of the temporary name so we
420 		**		can recover on interrupt.
421 		*/
422 
423 		/* set basic modes, etc. */
424 		(void) alarm(0);
425 		CurEnv->e_flags &= ~EF_FATALERRS;
426 		QueueRun = TRUE;
427 		MailBack = TRUE;
428 		CurEnv->e_id = &w->w_name[2];
429 # ifdef LOG
430 		if (LogLevel > 11)
431 			syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id,
432 			       getpid());
433 # endif LOG
434 
435 		/* don't use the headers from sendmail.cf... */
436 		CurEnv->e_header = NULL;
437 		chompheader("from: $q", TRUE);
438 
439 		/* create the link to the control file during processing */
440 		if (link(w->w_name, queuename(CurEnv, 'l')) < 0)
441 		{
442 			/* being processed by another queuer */
443 # ifdef LOG
444 			if (LogLevel > 4)
445 				syslog(LOG_DEBUG, "%s: locked", CurEnv->e_id);
446 # endif LOG
447 			exit(EX_OK);
448 		}
449 
450 		/* create ourselves a transcript file */
451 		openxscrpt();
452 
453 		/* do basic system initialization */
454 		initsys();
455 
456 		/* read the queue control file */
457 		readqf(queuename(CurEnv, 'q'));
458 		CurEnv->e_flags |= EF_INQUEUE;
459 		eatheader();
460 
461 		/* do the delivery */
462 		if (!bitset(EF_FATALERRS, CurEnv->e_flags))
463 			sendall(CurEnv, SM_DELIVER);
464 
465 		/* if still not sent, perhaps we should time out.... */
466 # ifdef DEBUG
467 		if (tTd(40, 3))
468 			printf("curtime=%ld, TimeOut=%ld\n", curtime(),
469 					     CurEnv->e_ctime + TimeOut);
470 # endif DEBUG
471 		if (curtime() > CurEnv->e_ctime + TimeOut)
472 			CurEnv->e_flags |= EF_TIMEOUT;
473 
474 		/* finish up and exit */
475 		finis();
476 	}
477 
478 	/*
479 	**  Parent -- pick up results.
480 	*/
481 
482 	errno = 0;
483 	while ((i = wait(&xstat)) > 0 && errno != EINTR)
484 	{
485 		if (errno == EINTR)
486 		{
487 			errno = 0;
488 		}
489 	}
490 }
491 /*
492 **  READQF -- read queue file and set up environment.
493 **
494 **	Parameters:
495 **		cf -- name of queue control file.
496 **
497 **	Returns:
498 **		none.
499 **
500 **	Side Effects:
501 **		cf is read and created as the current job, as though
502 **		we had been invoked by argument.
503 */
504 
505 readqf(cf)
506 	char *cf;
507 {
508 	register FILE *f;
509 	char buf[MAXFIELD];
510 	register char *p;
511 	register int i;
512 
513 	/*
514 	**  Open the file created by queueup.
515 	*/
516 
517 	f = fopen(cf, "r");
518 	if (f == NULL)
519 	{
520 		syserr("readqf: no cf file %s", cf);
521 		return;
522 	}
523 
524 	/*
525 	**  Read and process the file.
526 	*/
527 
528 	if (Verbose)
529 		printf("\nRunning %s\n", cf);
530 	while (fgetfolded(buf, sizeof buf, f) != NULL)
531 	{
532 		switch (buf[0])
533 		{
534 		  case 'R':		/* specify recipient */
535 			sendto(&buf[1], (ADDRESS *) NULL, &CurEnv->e_sendqueue);
536 			break;
537 
538 		  case 'H':		/* header */
539 			(void) chompheader(&buf[1], FALSE);
540 			break;
541 
542 		  case 'S':		/* sender */
543 			setsender(newstr(&buf[1]));
544 			break;
545 
546 		  case 'D':		/* data file name */
547 			CurEnv->e_df = newstr(&buf[1]);
548 			TempFile = fopen(CurEnv->e_df, "r");
549 			if (TempFile == NULL)
550 				syserr("readqf: cannot open %s", CurEnv->e_df);
551 			break;
552 
553 		  case 'T':		/* init time */
554 			(void) sscanf(&buf[1], "%ld", &CurEnv->e_ctime);
555 			break;
556 
557 		  case 'P':		/* message priority */
558 			(void) sscanf(&buf[1], "%ld", &CurEnv->e_msgpriority);
559 
560 			/* make sure that big things get sent eventually */
561 			CurEnv->e_msgpriority -= WKTIMEFACT;
562 			break;
563 
564 		  case 'C':		/* message class */
565 			(void) sscanf(&buf[1], "%hd", &CurEnv->e_class);
566 			break;
567 
568 		  case 'M':		/* define macro */
569 			define(buf[1], newstr(&buf[2]));
570 			break;
571 
572 		  default:
573 			syserr("readqf(%s): bad line \"%s\"", cf, buf);
574 			break;
575 		}
576 	}
577 }
578 /*
579 **  TIMEOUT -- process timeout on queue file.
580 **
581 **	Parameters:
582 **		e -- the envelope that timed out.
583 **
584 **	Returns:
585 **		none.
586 **
587 **	Side Effects:
588 **		Returns a message to the sender saying that this
589 **		message has timed out.
590 */
591 
592 timeout(e)
593 	register ENVELOPE *e;
594 {
595 	char buf[MAXLINE];
596 	extern char *pintvl();
597 
598 # ifdef DEBUG
599 	if (tTd(40, 3))
600 		printf("timeout(%s)\n", e->e_id);
601 # endif DEBUG
602 	e->e_to = NULL;
603 	message(Arpa_Info, "Message has timed out");
604 
605 	/* return message to sender */
606 	(void) sprintf(buf, "Cannot send mail for %s", pintvl(TimeOut, FALSE));
607 	(void) returntosender(buf, &e->e_from, TRUE);
608 
609 	/* arrange to remove files from queue */
610 	e->e_flags |= EF_CLRQUEUE;
611 }
612 
613 # endif QUEUE
614