Commit | Line | Data |
---|---|---|
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 |
8 | static char SccsId[] = "@(#)queue.c 3.7 %G% (no queueing)"; | |
9 | # else QUEUE | |
10 | ||
11 | static 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 | ||
31 | queueup(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 |
132 | bool ReorderQueue; /* if set, reorder the send queue */ |
133 | int QueuePid; /* pid of child running queue */ | |
aba51985 | 134 | |
2cf55cb7 EA |
135 | runqueue(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 | ||
214 | reordersig() | |
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 | ||
261 | orderq() | |
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 | ||
388 | workcmpf(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 | ||
412 | dowork(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 | ||
482 | readqf(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 | ||
563 | timeout(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 |