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