1 # include "sendmail.h"
2 # include <sys/stat.h>
3 # include <sys/dir.h>
4 # include <signal.h>
5 # include <errno.h>
6 
7 static char	SccsId[] =	"@(#)queue.c	3.6	11/22/81";
8 
9 /*
10 **  QUEUEUP -- queue a message up for future transmission.
11 **
12 **	The queued message should already be in the correct place.
13 **	This routine just outputs the control file as appropriate.
14 **
15 **	Parameters:
16 **		df -- location of the data file.  The name will
17 **			be transformed into a control file name.
18 **
19 **	Returns:
20 **		none.
21 **
22 **	Side Effects:
23 **		The current request (only unsatisfied addresses)
24 **			are saved in a control file.
25 */
26 
27 queueup(df)
28 	char *df;
29 {
30 	char cf[MAXNAME];
31 	register FILE *f;
32 	register int i;
33 	register HDR *h;
34 	register char *p;
35 	register ADDRESS *q;
36 
37 	/*
38 	**  Create control file.
39 	*/
40 
41 	strcpy(cf, QueueDir);
42 	strcat(cf, "/cfXXXXXX");
43 	(void) mktemp(cf);
44 	f = fopen(cf, "w");
45 	if (f == NULL)
46 	{
47 		syserr("queueup: cannot create control file %s", cf);
48 		return;
49 	}
50 
51 # ifdef DEBUG
52 	if (Debug)
53 		printf("queued in %s\n", cf);
54 # endif DEBUG
55 
56 	/*
57 	**  Output future work requests.
58 	*/
59 
60 	/* output name of data file */
61 	fprintf(f, "D%s\n", df);
62 
63 	/* output name of sender */
64 	fprintf(f, "S%s\n", From.q_paddr);
65 
66 	/* output timeout */
67 	fprintf(f, "T%ld\n", TimeOut);
68 
69 	/* output message priority */
70 	fprintf(f, "P%ld\n", MsgPriority);
71 
72 	/* output list of recipient addresses */
73 	for (q = SendQueue; q != NULL; q = q->q_next)
74 	{
75 # ifdef DEBUG
76 		if (Debug > 0)
77 		{
78 			printf("queueing ");
79 			printaddr(q, FALSE);
80 		}
81 # endif DEBUG
82 		if (bitset(QQUEUEUP, q->q_flags))
83 			fprintf(f, "R%s\n", q->q_paddr);
84 	}
85 
86 	/* output headers for this message */
87 	for (h = Header; h != NULL; h = h->h_link)
88 	{
89 		if (h->h_value == NULL || h->h_value[0] == '\0')
90 			continue;
91 		fprintf(f, "H");
92 		if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags))
93 			mfdecode(h->h_mflags, f);
94 		fprintf(f, "%s: ", h->h_field);
95 		if (bitset(H_DEFAULT, h->h_flags))
96 		{
97 			char buf[MAXLINE];
98 
99 			(void) expand(h->h_value, buf, &buf[sizeof buf]);
100 			fprintf(f, "%s\n", buf);
101 		}
102 		else
103 			fprintf(f, "%s\n", h->h_value);
104 	}
105 
106 	/*
107 	**  Clean up.
108 	*/
109 
110 	(void) fclose(f);
111 }
112 /*
113 **  RUNQUEUE -- run the jobs in the queue.
114 **
115 **	Gets the stuff out of the queue in some presumably logical
116 **	order and processes them.
117 **
118 **	Parameters:
119 **		none.
120 **
121 **	Returns:
122 **		none.
123 **
124 **	Side Effects:
125 **		runs things in the mail queue.
126 */
127 
128 bool	ReorderQueue;		/* if set, reorder the send queue */
129 int	QueuePid;		/* pid of child running queue */
130 
131 runqueue(forkflag)
132 	bool forkflag;
133 {
134 	extern reordersig();
135 
136 	if (QueueIntvl != 0)
137 	{
138 		(void) signal(SIGALRM, reordersig);
139 		(void) alarm((unsigned) QueueIntvl);
140 	}
141 
142 	if (forkflag)
143 	{
144 		QueuePid = dofork();
145 		if (QueuePid > 0)
146 		{
147 			/* parent */
148 			return;
149 		}
150 		else
151 			(void) alarm((unsigned) 0);
152 	}
153 
154 	for (;;)
155 	{
156 		/*
157 		**  Order the existing work requests.
158 		*/
159 
160 		orderq();
161 
162 		if (WorkQ == NULL)
163 		{
164 			/* no work?  well, maybe later */
165 			if (QueueIntvl == 0)
166 				break;
167 			pause();
168 			continue;
169 		}
170 
171 		ReorderQueue = FALSE;
172 
173 		/*
174 		**  Process them once at a time.
175 		**	The queue could be reordered while we do this to take
176 		**	new requests into account.  If so, the existing job
177 		**	will be finished but the next thing taken off WorkQ
178 		**	may be something else.
179 		*/
180 
181 		while (WorkQ != NULL)
182 		{
183 			WORK *w = WorkQ;
184 
185 			WorkQ = WorkQ->w_next;
186 			dowork(w);
187 			free(w->w_name);
188 			free((char *) w);
189 			if (ReorderQueue)
190 				break;
191 		}
192 
193 		if (QueueIntvl == 0)
194 			break;
195 	}
196 }
197 /*
198 **  REORDERSIG -- catch the alarm signal and tell sendmail to reorder queue.
199 **
200 **	Parameters:
201 **		none.
202 **
203 **	Returns:
204 **		none.
205 **
206 **	Side Effects:
207 **		sets the "reorder work queue" flag.
208 */
209 
210 reordersig()
211 {
212 	if (QueuePid == 0)
213 	{
214 		/* we are in a child doing queueing */
215 		ReorderQueue = TRUE;
216 	}
217 	else
218 	{
219 		/* we are in a parent -- poke child or start new one */
220 		if (kill(QueuePid, SIGALRM) < 0)
221 		{
222 			/* no child -- get zombie & start new one */
223 			static int st;
224 
225 			(void) wait(&st);
226 			QueuePid = dofork();
227 			if (QueuePid == 0)
228 			{
229 				/* new child; run queue */
230 				runqueue(FALSE);
231 				finis();
232 			}
233 		}
234 	}
235 
236 	/*
237 	**  Arrange to get this signal again.
238 	*/
239 
240 	(void) alarm((unsigned) QueueIntvl);
241 }
242 /*
243 **  ORDERQ -- order the work queue.
244 **
245 **	Parameters:
246 **		none.
247 **
248 **	Returns:
249 **		none.
250 **
251 **	Side Effects:
252 **		Sets WorkQ to the queue of available work, in order.
253 */
254 
255 # define WLSIZE		120	/* max size of worklist per sort */
256 
257 orderq()
258 {
259 	struct direct d;
260 	register WORK *w;
261 	register WORK **wp;		/* parent of w */
262 	register FILE *f;
263 	register int i;
264 	struct stat st;
265 	WORK wlist[WLSIZE];
266 	int wn = 0;
267 	extern workcmpf();
268 	extern char *QueueDir;
269 
270 	/* clear out old WorkQ */
271 	for (w = WorkQ; w != NULL; )
272 	{
273 		register WORK *nw = w->w_next;
274 
275 		WorkQ = nw;
276 		free(w->w_name);
277 		free((char *) w);
278 		w = nw;
279 	}
280 
281 	/* open the queue directory */
282 	f = fopen(QueueDir, "r");
283 	if (f == NULL)
284 	{
285 		syserr("orderq: cannot open %s", QueueDir);
286 		return;
287 	}
288 
289 	/*
290 	**  Read the work directory.
291 	*/
292 
293 	while (wn < WLSIZE && fread((char *) &d, sizeof d, 1, f) == 1)
294 	{
295 		char cbuf[MAXNAME];
296 		char lbuf[MAXNAME];
297 		FILE *cf;
298 		register char *p;
299 
300 		/* is this an interesting entry? */
301 		if (d.d_ino == 0 || d.d_name[0] != 'c')
302 			continue;
303 
304 		/* yes -- find the control file location */
305 		strcpy(cbuf, QueueDir);
306 		strcat(cbuf, "/");
307 		p = &cbuf[strlen(cbuf)];
308 		strncpy(p, d.d_name, DIRSIZ);
309 		p[DIRSIZ] = '\0';
310 
311 		/* open control file */
312 		cf = fopen(cbuf, "r");
313 		if (cf == NULL)
314 		{
315 			syserr("orderq: cannot open %s", cbuf);
316 			continue;
317 		}
318 		wlist[wn].w_name = newstr(cbuf);
319 
320 		/* extract useful information */
321 		while (fgets(lbuf, sizeof lbuf, cf) != NULL)
322 		{
323 			fixcrlf(lbuf, TRUE);
324 
325 			switch (lbuf[0])
326 			{
327 			  case 'P':		/* message priority */
328 				(void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri);
329 				break;
330 			}
331 		}
332 		wn++;
333 		(void) fclose(cf);
334 	}
335 	(void) fclose(f);
336 
337 	/*
338 	**  Sort the work directory.
339 	*/
340 
341 	qsort(wlist, wn, sizeof *wlist, workcmpf);
342 
343 	/*
344 	**  Convert the work list into canonical form.
345 	*/
346 
347 	wp = &WorkQ;
348 	for (i = 0; i < wn; i++)
349 	{
350 		w = (WORK *) xalloc(sizeof *w);
351 		w->w_name = wlist[i].w_name;
352 		w->w_pri = wlist[i].w_pri;
353 		w->w_next = NULL;
354 		*wp = w;
355 		wp = &w->w_next;
356 	}
357 
358 # ifdef DEBUG
359 	if (Debug)
360 	{
361 		for (w = WorkQ; w != NULL; w = w->w_next)
362 			printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
363 	}
364 # endif DEBUG
365 }
366 /*
367 **	WORKCMPF -- compare function for ordering work.
368 **
369 **	Parameters:
370 **		a -- the first argument.
371 **		b -- the second argument.
372 **
373 **	Returns:
374 **		-1 if a < b
375 **		0 if a == b
376 **		1 if a > b
377 **
378 **	Side Effects:
379 **		none.
380 */
381 
382 # define PRIFACT	1800		/* bytes each priority point is worth */
383 
384 workcmpf(a, b)
385 	register WORK *a;
386 	register WORK *b;
387 {
388 	if (a->w_pri == b->w_pri)
389 		return (0);
390 	else if (a->w_pri > b->w_pri)
391 		return (1);
392 	else
393 		return (-1);
394 }
395 /*
396 **  DOWORK -- do a work request.
397 **
398 **	Parameters:
399 **		w -- the work request to be satisfied.
400 **
401 **	Returns:
402 **		none.
403 **
404 **	Side Effects:
405 **		The work request is satisfied if possible.
406 */
407 
408 dowork(w)
409 	register WORK *w;
410 {
411 	register int i;
412 	auto int xstat;
413 
414 # ifdef DEBUG
415 	if (Debug)
416 		printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);
417 # endif DEBUG
418 
419 	/*
420 	**  Fork for work.
421 	*/
422 
423 	i = fork();
424 	if (i < 0)
425 	{
426 		syserr("dowork: cannot fork");
427 		return;
428 	}
429 
430 	if (i == 0)
431 	{
432 		/*
433 		**  CHILD
434 		*/
435 
436 		QueueRun = TRUE;
437 		openxscrpt();
438 		initsys();
439 		readqf(w->w_name);
440 		sendall(FALSE);
441 # ifdef DEBUG
442 		if (Debug > 2)
443 			printf("CurTime=%ld, TimeOut=%ld\n", CurTime, TimeOut);
444 # endif DEBUG
445 		if (QueueUp && CurTime > TimeOut)
446 			timeout(w);
447 		(void) unlink(w->w_name);
448 		finis();
449 	}
450 
451 	/*
452 	**  Parent -- pick up results.
453 	*/
454 
455 	errno = 0;
456 	while ((i = wait(&xstat)) > 0 && errno != EINTR)
457 	{
458 		if (errno == EINTR)
459 		{
460 			errno = 0;
461 		}
462 	}
463 }
464 /*
465 **  READQF -- read queue file and set up environment.
466 **
467 **	Parameters:
468 **		cf -- name of queue control file.
469 **
470 **	Returns:
471 **		none.
472 **
473 **	Side Effects:
474 **		cf is read and created as the current job, as though
475 **		we had been invoked by argument.
476 */
477 
478 readqf(cf)
479 	char *cf;
480 {
481 	register FILE *f;
482 	char buf[MAXLINE];
483 
484 	/*
485 	**  Open the file created by queueup.
486 	*/
487 
488 	f = fopen(cf, "r");
489 	if (f == NULL)
490 	{
491 		syserr("readqf: no cf file %s", cf);
492 		return;
493 	}
494 
495 	/*
496 	**  Read and process the file.
497 	*/
498 
499 	if (Verbose)
500 		message(Arpa_Info, "Running %s (from %s)", cf, From.q_paddr);
501 
502 	while (fgets(buf, sizeof buf, f) != NULL)
503 	{
504 		fixcrlf(buf, TRUE);
505 
506 		switch (buf[0])
507 		{
508 		  case 'R':		/* specify recipient */
509 			sendto(&buf[1], 1, (ADDRESS *) NULL, &SendQueue);
510 			break;
511 
512 		  case 'H':		/* header */
513 			(void) chompheader(&buf[1], FALSE);
514 			break;
515 
516 		  case 'S':		/* sender */
517 			setsender(newstr(&buf[1]));
518 			break;
519 
520 		  case 'D':		/* data file name */
521 			InFileName = newstr(&buf[1]);
522 			TempFile = fopen(InFileName, "r");
523 			if (TempFile == NULL)
524 				syserr("readqf: cannot open %s", InFileName);
525 			break;
526 
527 		  case 'T':		/* timeout */
528 			(void) sscanf(&buf[1], "%ld", &TimeOut);
529 			break;
530 
531 		  case 'P':		/* message priority */
532 			(void) sscanf(&buf[1], "%ld", &MsgPriority);
533 
534 			/* make sure that big things get sent eventually */
535 			MsgPriority -= WKTIMEFACT;
536 			break;
537 
538 		  default:
539 			syserr("readqf(%s): bad line \"%s\"", cf, buf);
540 			break;
541 		}
542 	}
543 }
544 /*
545 **  TIMEOUT -- process timeout on queue file.
546 **
547 **	Parameters:
548 **		w -- pointer to work request that timed out.
549 **
550 **	Returns:
551 **		none.
552 **
553 **	Side Effects:
554 **		Returns a message to the sender saying that this
555 **		message has timed out.
556 */
557 
558 timeout(w)
559 	register WORK *w;
560 {
561 # ifdef DEBUG
562 	if (Debug > 0)
563 		printf("timeout(%s)\n", w->w_name);
564 # endif DEBUG
565 
566 	/* return message to sender */
567 	(void) returntosender("Cannot send mail for three days");
568 
569 	/* arrange to remove files from queue */
570 	QueueUp = FALSE;
571 }
572