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