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