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