1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms are permitted 7 * provided that the above copyright notice and this paragraph are 8 * duplicated in all such forms and that any documentation, 9 * advertising materials, and other materials related to such 10 * distribution and use acknowledge that the software was developed 11 * by the University of California, Berkeley. The name of the 12 * University may not be used to endorse or promote products derived 13 * from this software without specific prior written permission. 14 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 16 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 17 */ 18 19 # include "sendmail.h" 20 21 #ifndef lint 22 #ifdef QUEUE 23 static char sccsid[] = "@(#)queue.c 5.26 (Berkeley) 01/01/89 (with queueing)"; 24 #else 25 static char sccsid[] = "@(#)queue.c 5.26 (Berkeley) 01/01/89 (without queueing)"; 26 #endif 27 #endif /* not lint */ 28 29 # include <sys/stat.h> 30 # include <sys/dir.h> 31 # include <signal.h> 32 # include <errno.h> 33 34 # ifdef QUEUE 35 36 /* 37 ** Work queue. 38 */ 39 40 struct work 41 { 42 char *w_name; /* name of control file */ 43 long w_pri; /* priority of message, see below */ 44 time_t w_ctime; /* creation time of message */ 45 struct work *w_next; /* next in queue */ 46 }; 47 48 typedef struct work WORK; 49 50 WORK *WorkQ; /* queue of things to be done */ 51 /* 52 ** QUEUEUP -- queue a message up for future transmission. 53 ** 54 ** Parameters: 55 ** e -- the envelope to queue up. 56 ** queueall -- if TRUE, queue all addresses, rather than 57 ** just those with the QQUEUEUP flag set. 58 ** announce -- if TRUE, tell when you are queueing up. 59 ** 60 ** Returns: 61 ** none. 62 ** 63 ** Side Effects: 64 ** The current request are saved in a control file. 65 */ 66 67 queueup(e, queueall, announce) 68 register ENVELOPE *e; 69 bool queueall; 70 bool announce; 71 { 72 char *tf; 73 char *qf; 74 char buf[MAXLINE]; 75 register FILE *tfp; 76 register HDR *h; 77 register ADDRESS *q; 78 MAILER nullmailer; 79 80 /* 81 ** Create control file. 82 */ 83 84 tf = newstr(queuename(e, 't')); 85 tfp = fopen(tf, "w"); 86 if (tfp == NULL) 87 { 88 syserr("queueup: cannot create temp file %s", tf); 89 return; 90 } 91 (void) chmod(tf, FileMode); 92 93 if (tTd(40, 1)) 94 printf("queueing %s\n", e->e_id); 95 96 /* 97 ** If there is no data file yet, create one. 98 */ 99 100 if (e->e_df == NULL) 101 { 102 register FILE *dfp; 103 extern putbody(); 104 105 e->e_df = newstr(queuename(e, 'd')); 106 dfp = fopen(e->e_df, "w"); 107 if (dfp == NULL) 108 { 109 syserr("queueup: cannot create %s", e->e_df); 110 (void) fclose(tfp); 111 return; 112 } 113 (void) chmod(e->e_df, FileMode); 114 (*e->e_putbody)(dfp, ProgMailer, e); 115 (void) fclose(dfp); 116 e->e_putbody = putbody; 117 } 118 119 /* 120 ** Output future work requests. 121 ** Priority and creation time should be first, since 122 ** they are required by orderq. 123 */ 124 125 /* output message priority */ 126 fprintf(tfp, "P%ld\n", e->e_msgpriority); 127 128 /* output creation time */ 129 fprintf(tfp, "T%ld\n", e->e_ctime); 130 131 /* output name of data file */ 132 fprintf(tfp, "D%s\n", e->e_df); 133 134 /* message from envelope, if it exists */ 135 if (e->e_message != NULL) 136 fprintf(tfp, "M%s\n", e->e_message); 137 138 /* output name of sender */ 139 fprintf(tfp, "S%s\n", e->e_from.q_paddr); 140 141 /* output list of recipient addresses */ 142 for (q = e->e_sendqueue; q != NULL; q = q->q_next) 143 { 144 if (queueall ? !bitset(QDONTSEND, q->q_flags) : 145 bitset(QQUEUEUP, q->q_flags)) 146 { 147 fprintf(tfp, "R%s\n", q->q_paddr); 148 if (announce) 149 { 150 e->e_to = q->q_paddr; 151 message(Arpa_Info, "queued"); 152 if (LogLevel > 4) 153 logdelivery("queued"); 154 e->e_to = NULL; 155 } 156 if (tTd(40, 1)) 157 { 158 printf("queueing "); 159 printaddr(q, FALSE); 160 } 161 } 162 } 163 164 /* output list of error recipients */ 165 for (q = e->e_errorqueue; q != NULL; q = q->q_next) 166 { 167 if (!bitset(QDONTSEND, q->q_flags)) 168 fprintf(tfp, "E%s\n", q->q_paddr); 169 } 170 171 /* 172 ** Output headers for this message. 173 ** Expand macros completely here. Queue run will deal with 174 ** everything as absolute headers. 175 ** All headers that must be relative to the recipient 176 ** can be cracked later. 177 ** We set up a "null mailer" -- i.e., a mailer that will have 178 ** no effect on the addresses as they are output. 179 */ 180 181 bzero((char *) &nullmailer, sizeof nullmailer); 182 nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1; 183 nullmailer.m_eol = "\n"; 184 185 define('g', "\001f", e); 186 for (h = e->e_header; h != NULL; h = h->h_link) 187 { 188 extern bool bitzerop(); 189 190 /* don't output null headers */ 191 if (h->h_value == NULL || h->h_value[0] == '\0') 192 continue; 193 194 /* don't output resent headers on non-resent messages */ 195 if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 196 continue; 197 198 /* output this header */ 199 fprintf(tfp, "H"); 200 201 /* if conditional, output the set of conditions */ 202 if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) 203 { 204 int j; 205 206 (void) putc('?', tfp); 207 for (j = '\0'; j <= '\177'; j++) 208 if (bitnset(j, h->h_mflags)) 209 (void) putc(j, tfp); 210 (void) putc('?', tfp); 211 } 212 213 /* output the header: expand macros, convert addresses */ 214 if (bitset(H_DEFAULT, h->h_flags)) 215 { 216 (void) expand(h->h_value, buf, &buf[sizeof buf], e); 217 fprintf(tfp, "%s: %s\n", h->h_field, buf); 218 } 219 else if (bitset(H_FROM|H_RCPT, h->h_flags)) 220 { 221 commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags), 222 &nullmailer); 223 } 224 else 225 fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); 226 } 227 228 /* 229 ** Clean up. 230 */ 231 232 (void) fclose(tfp); 233 qf = queuename(e, 'q'); 234 if (tf != NULL) 235 { 236 (void) unlink(qf); 237 if (rename(tf, qf) < 0) 238 syserr("cannot unlink(%s, %s), df=%s", tf, qf, e->e_df); 239 errno = 0; 240 } 241 242 # ifdef LOG 243 /* save log info */ 244 if (LogLevel > 15) 245 syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df); 246 # endif LOG 247 } 248 /* 249 ** RUNQUEUE -- run the jobs in the queue. 250 ** 251 ** Gets the stuff out of the queue in some presumably logical 252 ** order and processes them. 253 ** 254 ** Parameters: 255 ** forkflag -- TRUE if the queue scanning should be done in 256 ** a child process. We double-fork so it is not our 257 ** child and we don't have to clean up after it. 258 ** 259 ** Returns: 260 ** none. 261 ** 262 ** Side Effects: 263 ** runs things in the mail queue. 264 */ 265 266 runqueue(forkflag) 267 bool forkflag; 268 { 269 extern bool shouldqueue(); 270 271 /* 272 ** If no work will ever be selected, don't even bother reading 273 ** the queue. 274 */ 275 276 if (shouldqueue(-100000000L)) 277 { 278 if (Verbose) 279 printf("Skipping queue run -- load average too high\n"); 280 281 if (forkflag) 282 return; 283 finis(); 284 } 285 286 /* 287 ** See if we want to go off and do other useful work. 288 */ 289 290 if (forkflag) 291 { 292 int pid; 293 294 pid = dofork(); 295 if (pid != 0) 296 { 297 extern reapchild(); 298 299 /* parent -- pick up intermediate zombie */ 300 #ifndef SIGCHLD 301 (void) waitfor(pid); 302 #else SIGCHLD 303 (void) signal(SIGCHLD, reapchild); 304 #endif SIGCHLD 305 if (QueueIntvl != 0) 306 (void) setevent(QueueIntvl, runqueue, TRUE); 307 return; 308 } 309 /* child -- double fork */ 310 #ifndef SIGCHLD 311 if (fork() != 0) 312 exit(EX_OK); 313 #else SIGCHLD 314 (void) signal(SIGCHLD, SIG_DFL); 315 #endif SIGCHLD 316 } 317 318 setproctitle("running queue"); 319 320 # ifdef LOG 321 if (LogLevel > 11) 322 syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid()); 323 # endif LOG 324 325 /* 326 ** Release any resources used by the daemon code. 327 */ 328 329 # ifdef DAEMON 330 clrdaemon(); 331 # endif DAEMON 332 333 /* 334 ** Make sure the alias database is open. 335 */ 336 337 initaliases(AliasFile, FALSE); 338 339 /* 340 ** Start making passes through the queue. 341 ** First, read and sort the entire queue. 342 ** Then, process the work in that order. 343 ** But if you take too long, start over. 344 */ 345 346 /* order the existing work requests */ 347 (void) orderq(FALSE); 348 349 /* process them once at a time */ 350 while (WorkQ != NULL) 351 { 352 WORK *w = WorkQ; 353 354 WorkQ = WorkQ->w_next; 355 dowork(w); 356 free(w->w_name); 357 free((char *) w); 358 } 359 360 /* exit without the usual cleanup */ 361 exit(ExitStat); 362 } 363 /* 364 ** ORDERQ -- order the work queue. 365 ** 366 ** Parameters: 367 ** doall -- if set, include everything in the queue (even 368 ** the jobs that cannot be run because the load 369 ** average is too high). Otherwise, exclude those 370 ** jobs. 371 ** 372 ** Returns: 373 ** The number of request in the queue (not necessarily 374 ** the number of requests in WorkQ however). 375 ** 376 ** Side Effects: 377 ** Sets WorkQ to the queue of available work, in order. 378 */ 379 380 # define NEED_P 001 381 # define NEED_T 002 382 383 orderq(doall) 384 bool doall; 385 { 386 register struct direct *d; 387 register WORK *w; 388 DIR *f; 389 register int i; 390 WORK wlist[QUEUESIZE+1]; 391 int wn = -1; 392 extern workcmpf(); 393 394 /* clear out old WorkQ */ 395 for (w = WorkQ; w != NULL; ) 396 { 397 register WORK *nw = w->w_next; 398 399 WorkQ = nw; 400 free(w->w_name); 401 free((char *) w); 402 w = nw; 403 } 404 405 /* open the queue directory */ 406 f = opendir("."); 407 if (f == NULL) 408 { 409 syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 410 return (0); 411 } 412 413 /* 414 ** Read the work directory. 415 */ 416 417 while ((d = readdir(f)) != NULL) 418 { 419 FILE *cf; 420 char lbuf[MAXNAME]; 421 422 /* is this an interesting entry? */ 423 if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 424 continue; 425 426 /* yes -- open control file (if not too many files) */ 427 if (++wn >= QUEUESIZE) 428 continue; 429 cf = fopen(d->d_name, "r"); 430 if (cf == NULL) 431 { 432 /* this may be some random person sending hir msgs */ 433 /* syserr("orderq: cannot open %s", cbuf); */ 434 if (tTd(41, 2)) 435 printf("orderq: cannot open %s (%d)\n", 436 d->d_name, errno); 437 errno = 0; 438 wn--; 439 continue; 440 } 441 w = &wlist[wn]; 442 w->w_name = newstr(d->d_name); 443 444 /* make sure jobs in creation don't clog queue */ 445 w->w_pri = 0x7fffffff; 446 w->w_ctime = 0; 447 448 /* extract useful information */ 449 i = NEED_P | NEED_T; 450 while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) 451 { 452 extern long atol(); 453 454 switch (lbuf[0]) 455 { 456 case 'P': 457 w->w_pri = atol(&lbuf[1]); 458 i &= ~NEED_P; 459 break; 460 461 case 'T': 462 w->w_ctime = atol(&lbuf[1]); 463 i &= ~NEED_T; 464 break; 465 } 466 } 467 (void) fclose(cf); 468 469 if (!doall && shouldqueue(w->w_pri)) 470 { 471 /* don't even bother sorting this job in */ 472 wn--; 473 } 474 } 475 (void) closedir(f); 476 wn++; 477 478 /* 479 ** Sort the work directory. 480 */ 481 482 qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf); 483 484 /* 485 ** Convert the work list into canonical form. 486 ** Should be turning it into a list of envelopes here perhaps. 487 */ 488 489 WorkQ = NULL; 490 for (i = min(wn, QUEUESIZE); --i >= 0; ) 491 { 492 w = (WORK *) xalloc(sizeof *w); 493 w->w_name = wlist[i].w_name; 494 w->w_pri = wlist[i].w_pri; 495 w->w_ctime = wlist[i].w_ctime; 496 w->w_next = WorkQ; 497 WorkQ = w; 498 } 499 500 if (tTd(40, 1)) 501 { 502 for (w = WorkQ; w != NULL; w = w->w_next) 503 printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 504 } 505 506 return (wn); 507 } 508 /* 509 ** WORKCMPF -- compare function for ordering work. 510 ** 511 ** Parameters: 512 ** a -- the first argument. 513 ** b -- the second argument. 514 ** 515 ** Returns: 516 ** -1 if a < b 517 ** 0 if a == b 518 ** +1 if a > b 519 ** 520 ** Side Effects: 521 ** none. 522 */ 523 524 workcmpf(a, b) 525 register WORK *a; 526 register WORK *b; 527 { 528 long pa = a->w_pri + a->w_ctime; 529 long pb = b->w_pri + b->w_ctime; 530 531 if (pa == pb) 532 return (0); 533 else if (pa > pb) 534 return (1); 535 else 536 return (-1); 537 } 538 /* 539 ** DOWORK -- do a work request. 540 ** 541 ** Parameters: 542 ** w -- the work request to be satisfied. 543 ** 544 ** Returns: 545 ** none. 546 ** 547 ** Side Effects: 548 ** The work request is satisfied if possible. 549 */ 550 551 dowork(w) 552 register WORK *w; 553 { 554 register int i; 555 extern bool shouldqueue(); 556 557 if (tTd(40, 1)) 558 printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 559 560 /* 561 ** Ignore jobs that are too expensive for the moment. 562 */ 563 564 if (shouldqueue(w->w_pri)) 565 { 566 if (Verbose) 567 printf("\nSkipping %s\n", w->w_name + 2); 568 return; 569 } 570 571 /* 572 ** Fork for work. 573 */ 574 575 if (ForkQueueRuns) 576 { 577 i = fork(); 578 if (i < 0) 579 { 580 syserr("dowork: cannot fork"); 581 return; 582 } 583 } 584 else 585 { 586 i = 0; 587 } 588 589 if (i == 0) 590 { 591 /* 592 ** CHILD 593 ** Lock the control file to avoid duplicate deliveries. 594 ** Then run the file as though we had just read it. 595 ** We save an idea of the temporary name so we 596 ** can recover on interrupt. 597 */ 598 599 /* set basic modes, etc. */ 600 (void) alarm(0); 601 clearenvelope(CurEnv, FALSE); 602 QueueRun = TRUE; 603 ErrorMode = EM_MAIL; 604 CurEnv->e_id = &w->w_name[2]; 605 # ifdef LOG 606 if (LogLevel > 11) 607 syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id, 608 getpid()); 609 # endif LOG 610 611 /* don't use the headers from sendmail.cf... */ 612 CurEnv->e_header = NULL; 613 614 /* lock the control file during processing */ 615 if (link(w->w_name, queuename(CurEnv, 'l')) < 0) 616 { 617 /* being processed by another queuer */ 618 # ifdef LOG 619 if (LogLevel > 4) 620 syslog(LOG_DEBUG, "%s: locked", CurEnv->e_id); 621 # endif LOG 622 if (ForkQueueRuns) 623 exit(EX_OK); 624 else 625 return; 626 } 627 628 /* do basic system initialization */ 629 initsys(); 630 631 /* read the queue control file */ 632 readqf(CurEnv, TRUE); 633 CurEnv->e_flags |= EF_INQUEUE; 634 eatheader(CurEnv); 635 636 /* do the delivery */ 637 if (!bitset(EF_FATALERRS, CurEnv->e_flags)) 638 sendall(CurEnv, SM_DELIVER); 639 640 /* finish up and exit */ 641 if (ForkQueueRuns) 642 finis(); 643 else 644 dropenvelope(CurEnv); 645 } 646 else 647 { 648 /* 649 ** Parent -- pick up results. 650 */ 651 652 errno = 0; 653 (void) waitfor(i); 654 } 655 } 656 /* 657 ** READQF -- read queue file and set up environment. 658 ** 659 ** Parameters: 660 ** e -- the envelope of the job to run. 661 ** full -- if set, read in all information. Otherwise just 662 ** read in info needed for a queue print. 663 ** 664 ** Returns: 665 ** none. 666 ** 667 ** Side Effects: 668 ** cf is read and created as the current job, as though 669 ** we had been invoked by argument. 670 */ 671 672 readqf(e, full) 673 register ENVELOPE *e; 674 bool full; 675 { 676 char *qf; 677 register FILE *qfp; 678 char buf[MAXFIELD]; 679 extern char *fgetfolded(); 680 extern long atol(); 681 682 /* 683 ** Read and process the file. 684 */ 685 686 qf = queuename(e, 'q'); 687 qfp = fopen(qf, "r"); 688 if (qfp == NULL) 689 { 690 syserr("readqf: no control file %s", qf); 691 return; 692 } 693 FileName = qf; 694 LineNumber = 0; 695 if (Verbose && full) 696 printf("\nRunning %s\n", e->e_id); 697 while (fgetfolded(buf, sizeof buf, qfp) != NULL) 698 { 699 if (tTd(40, 4)) 700 printf("+++++ %s\n", buf); 701 switch (buf[0]) 702 { 703 case 'R': /* specify recipient */ 704 sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue); 705 break; 706 707 case 'E': /* specify error recipient */ 708 sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_errorqueue); 709 break; 710 711 case 'H': /* header */ 712 if (full) 713 (void) chompheader(&buf[1], FALSE); 714 break; 715 716 case 'M': /* message */ 717 e->e_message = newstr(&buf[1]); 718 break; 719 720 case 'S': /* sender */ 721 setsender(newstr(&buf[1])); 722 break; 723 724 case 'D': /* data file name */ 725 if (!full) 726 break; 727 e->e_df = newstr(&buf[1]); 728 e->e_dfp = fopen(e->e_df, "r"); 729 if (e->e_dfp == NULL) 730 syserr("readqf: cannot open %s", e->e_df); 731 break; 732 733 case 'T': /* init time */ 734 e->e_ctime = atol(&buf[1]); 735 break; 736 737 case 'P': /* message priority */ 738 e->e_msgpriority = atol(&buf[1]) + WkTimeFact; 739 break; 740 741 case '\0': /* blank line; ignore */ 742 break; 743 744 default: 745 syserr("readqf(%s:%d): bad line \"%s\"", e->e_id, 746 LineNumber, buf); 747 break; 748 } 749 } 750 751 (void) fclose(qfp); 752 FileName = NULL; 753 754 /* 755 ** If we haven't read any lines, this queue file is empty. 756 ** Arrange to remove it without referencing any null pointers. 757 */ 758 759 if (LineNumber == 0) 760 { 761 errno = 0; 762 e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; 763 } 764 } 765 /* 766 ** PRINTQUEUE -- print out a representation of the mail queue 767 ** 768 ** Parameters: 769 ** none. 770 ** 771 ** Returns: 772 ** none. 773 ** 774 ** Side Effects: 775 ** Prints a listing of the mail queue on the standard output. 776 */ 777 778 printqueue() 779 { 780 register WORK *w; 781 FILE *f; 782 int nrequests; 783 char buf[MAXLINE]; 784 785 /* 786 ** Read and order the queue. 787 */ 788 789 nrequests = orderq(TRUE); 790 791 /* 792 ** Print the work list that we have read. 793 */ 794 795 /* first see if there is anything */ 796 if (nrequests <= 0) 797 { 798 printf("Mail queue is empty\n"); 799 return; 800 } 801 802 printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); 803 if (nrequests > QUEUESIZE) 804 printf(", only %d printed", QUEUESIZE); 805 if (Verbose) 806 printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); 807 else 808 printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); 809 for (w = WorkQ; w != NULL; w = w->w_next) 810 { 811 struct stat st; 812 auto time_t submittime = 0; 813 long dfsize = -1; 814 char lf[20]; 815 char message[MAXLINE]; 816 extern bool shouldqueue(); 817 818 f = fopen(w->w_name, "r"); 819 if (f == NULL) 820 { 821 errno = 0; 822 continue; 823 } 824 printf("%7s", w->w_name + 2); 825 (void) strcpy(lf, w->w_name); 826 lf[0] = 'l'; 827 if (stat(lf, &st) >= 0) 828 printf("*"); 829 else if (shouldqueue(w->w_pri)) 830 printf("X"); 831 else 832 printf(" "); 833 errno = 0; 834 835 message[0] = '\0'; 836 while (fgets(buf, sizeof buf, f) != NULL) 837 { 838 fixcrlf(buf, TRUE); 839 switch (buf[0]) 840 { 841 case 'M': /* error message */ 842 (void) strcpy(message, &buf[1]); 843 break; 844 845 case 'S': /* sender name */ 846 if (Verbose) 847 printf("%8ld %10ld %.12s %.38s", dfsize, 848 w->w_pri, ctime(&submittime) + 4, 849 &buf[1]); 850 else 851 printf("%8ld %.16s %.45s", dfsize, 852 ctime(&submittime), &buf[1]); 853 if (message[0] != '\0') 854 printf("\n\t\t (%.60s)", message); 855 break; 856 857 case 'R': /* recipient name */ 858 if (Verbose) 859 printf("\n\t\t\t\t\t %.38s", &buf[1]); 860 else 861 printf("\n\t\t\t\t %.45s", &buf[1]); 862 break; 863 864 case 'T': /* creation time */ 865 submittime = atol(&buf[1]); 866 break; 867 868 case 'D': /* data file name */ 869 if (stat(&buf[1], &st) >= 0) 870 dfsize = st.st_size; 871 break; 872 } 873 } 874 if (submittime == (time_t) 0) 875 printf(" (no control file)"); 876 printf("\n"); 877 (void) fclose(f); 878 } 879 } 880 881 # endif QUEUE 882 /* 883 ** QUEUENAME -- build a file name in the queue directory for this envelope. 884 ** 885 ** Assigns an id code if one does not already exist. 886 ** This code is very careful to avoid trashing existing files 887 ** under any circumstances. 888 ** We first create an nf file that is only used when 889 ** assigning an id. This file is always empty, so that 890 ** we can never accidently truncate an lf file. 891 ** 892 ** Parameters: 893 ** e -- envelope to build it in/from. 894 ** type -- the file type, used as the first character 895 ** of the file name. 896 ** 897 ** Returns: 898 ** a pointer to the new file name (in a static buffer). 899 ** 900 ** Side Effects: 901 ** Will create the lf and qf files if no id code is 902 ** already assigned. This will cause the envelope 903 ** to be modified. 904 */ 905 906 char * 907 queuename(e, type) 908 register ENVELOPE *e; 909 char type; 910 { 911 static char buf[MAXNAME]; 912 static int pid = -1; 913 char c1 = 'A'; 914 char c2 = 'A'; 915 916 if (e->e_id == NULL) 917 { 918 char qf[20]; 919 char nf[20]; 920 char lf[20]; 921 922 /* find a unique id */ 923 if (pid != getpid()) 924 { 925 /* new process -- start back at "AA" */ 926 pid = getpid(); 927 c1 = 'A'; 928 c2 = 'A' - 1; 929 } 930 (void) sprintf(qf, "qfAA%05d", pid); 931 (void) strcpy(lf, qf); 932 lf[0] = 'l'; 933 (void) strcpy(nf, qf); 934 nf[0] = 'n'; 935 936 while (c1 < '~' || c2 < 'Z') 937 { 938 int i; 939 940 if (c2 >= 'Z') 941 { 942 c1++; 943 c2 = 'A' - 1; 944 } 945 lf[2] = nf[2] = qf[2] = c1; 946 lf[3] = nf[3] = qf[3] = ++c2; 947 if (tTd(7, 20)) 948 printf("queuename: trying \"%s\"\n", nf); 949 950 # ifdef QUEUE 951 if (access(lf, 0) >= 0 || access(qf, 0) >= 0) 952 continue; 953 errno = 0; 954 i = creat(nf, FileMode); 955 if (i < 0) 956 { 957 (void) unlink(nf); /* kernel bug */ 958 if (errno == ENOSPC) { 959 syserr("queuename: Cannot create \"%s\" in \"%s\"", 960 nf, QueueDir); 961 exit(EX_UNAVAILABLE); 962 } 963 continue; 964 } 965 (void) close(i); 966 i = link(nf, lf); 967 (void) unlink(nf); 968 if (i < 0) 969 continue; 970 if (link(lf, qf) >= 0) 971 break; 972 (void) unlink(lf); 973 # else QUEUE 974 if (close(creat(qf, FileMode)) >= 0) 975 break; 976 # endif QUEUE 977 } 978 if (c1 >= '~' && c2 >= 'Z') 979 { 980 syserr("queuename: Cannot create \"%s\" in \"%s\"", 981 qf, QueueDir); 982 exit(EX_OSERR); 983 } 984 e->e_id = newstr(&qf[2]); 985 define('i', e->e_id, e); 986 if (tTd(7, 1)) 987 printf("queuename: assigned id %s, env=%x\n", e->e_id, e); 988 # ifdef LOG 989 if (LogLevel > 16) 990 syslog(LOG_DEBUG, "%s: assigned id", e->e_id); 991 # endif LOG 992 } 993 994 if (type == '\0') 995 return (NULL); 996 (void) sprintf(buf, "%cf%s", type, e->e_id); 997 if (tTd(7, 2)) 998 printf("queuename: %s\n", buf); 999 return (buf); 1000 } 1001 /* 1002 ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 1003 ** 1004 ** Parameters: 1005 ** e -- the envelope to unlock. 1006 ** 1007 ** Returns: 1008 ** none 1009 ** 1010 ** Side Effects: 1011 ** unlocks the queue for `e'. 1012 */ 1013 1014 unlockqueue(e) 1015 ENVELOPE *e; 1016 { 1017 /* remove the transcript */ 1018 # ifdef LOG 1019 if (LogLevel > 19) 1020 syslog(LOG_DEBUG, "%s: unlock", e->e_id); 1021 # endif LOG 1022 if (!tTd(51, 4)) 1023 xunlink(queuename(e, 'x')); 1024 1025 # ifdef QUEUE 1026 /* last but not least, remove the lock */ 1027 xunlink(queuename(e, 'l')); 1028 # endif QUEUE 1029 } 1030