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