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