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