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