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