1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)conf.c 8.35 (Berkeley) 09/19/93"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 # include "pathnames.h" 15 # include <sys/ioctl.h> 16 # include <sys/param.h> 17 # include <pwd.h> 18 19 /* 20 ** CONF.C -- Sendmail Configuration Tables. 21 ** 22 ** Defines the configuration of this installation. 23 ** 24 ** Configuration Variables: 25 ** HdrInfo -- a table describing well-known header fields. 26 ** Each entry has the field name and some flags, 27 ** which are described in sendmail.h. 28 ** 29 ** Notes: 30 ** I have tried to put almost all the reasonable 31 ** configuration information into the configuration 32 ** file read at runtime. My intent is that anything 33 ** here is a function of the version of UNIX you 34 ** are running, or is really static -- for example 35 ** the headers are a superset of widely used 36 ** protocols. If you find yourself playing with 37 ** this file too much, you may be making a mistake! 38 */ 39 40 41 42 43 /* 44 ** Header info table 45 ** Final (null) entry contains the flags used for any other field. 46 ** 47 ** Not all of these are actually handled specially by sendmail 48 ** at this time. They are included as placeholders, to let 49 ** you know that "someday" I intend to have sendmail do 50 ** something with them. 51 */ 52 53 struct hdrinfo HdrInfo[] = 54 { 55 /* originator fields, most to least significant */ 56 "resent-sender", H_FROM|H_RESENT, 57 "resent-from", H_FROM|H_RESENT, 58 "resent-reply-to", H_FROM|H_RESENT, 59 "sender", H_FROM, 60 "from", H_FROM, 61 "reply-to", H_FROM, 62 "full-name", H_ACHECK, 63 "return-receipt-to", H_FROM /* |H_RECEIPTTO */, 64 "errors-to", H_FROM|H_ERRORSTO, 65 66 /* destination fields */ 67 "to", H_RCPT, 68 "resent-to", H_RCPT|H_RESENT, 69 "cc", H_RCPT, 70 "resent-cc", H_RCPT|H_RESENT, 71 "bcc", H_RCPT|H_ACHECK, 72 "resent-bcc", H_RCPT|H_ACHECK|H_RESENT, 73 "apparently-to", H_RCPT, 74 75 /* message identification and control */ 76 "message-id", 0, 77 "resent-message-id", H_RESENT, 78 "message", H_EOH, 79 "text", H_EOH, 80 81 /* date fields */ 82 "date", 0, 83 "resent-date", H_RESENT, 84 85 /* trace fields */ 86 "received", H_TRACE|H_FORCE, 87 "x400-received", H_TRACE|H_FORCE, 88 "via", H_TRACE|H_FORCE, 89 "mail-from", H_TRACE|H_FORCE, 90 91 /* miscellaneous fields */ 92 "comments", H_FORCE, 93 "return-path", H_FORCE|H_ACHECK, 94 95 NULL, 0, 96 }; 97 98 99 100 /* 101 ** Location of system files/databases/etc. 102 */ 103 104 char *ConfFile = _PATH_SENDMAILCF; /* runtime configuration */ 105 char *PidFile = _PATH_SENDMAILPID; /* stores daemon proc id */ 106 107 108 109 /* 110 ** Privacy values 111 */ 112 113 struct prival PrivacyValues[] = 114 { 115 "public", PRIV_PUBLIC, 116 "needmailhelo", PRIV_NEEDMAILHELO, 117 "needexpnhelo", PRIV_NEEDEXPNHELO, 118 "needvrfyhelo", PRIV_NEEDVRFYHELO, 119 "noexpn", PRIV_NOEXPN, 120 "novrfy", PRIV_NOVRFY, 121 "restrictmailq", PRIV_RESTRICTMAILQ, 122 "restrictqrun", PRIV_RESTRICTQRUN, 123 "authwarnings", PRIV_AUTHWARNINGS, 124 "goaway", PRIV_GOAWAY, 125 NULL, 0, 126 }; 127 128 129 130 /* 131 ** Miscellaneous stuff. 132 */ 133 134 int DtableSize = 50; /* max open files; reset in 4.2bsd */ 135 /* 136 ** SETDEFAULTS -- set default values 137 ** 138 ** Because of the way freezing is done, these must be initialized 139 ** using direct code. 140 ** 141 ** Parameters: 142 ** e -- the default envelope. 143 ** 144 ** Returns: 145 ** none. 146 ** 147 ** Side Effects: 148 ** Initializes a bunch of global variables to their 149 ** default values. 150 */ 151 152 #define DAYS * 24 * 60 * 60 153 154 setdefaults(e) 155 register ENVELOPE *e; 156 { 157 SpaceSub = ' '; /* option B */ 158 QueueLA = 8; /* option x */ 159 RefuseLA = 12; /* option X */ 160 WkRecipFact = 30000L; /* option y */ 161 WkClassFact = 1800L; /* option z */ 162 WkTimeFact = 90000L; /* option Z */ 163 QueueFactor = WkRecipFact * 20; /* option q */ 164 FileMode = (RealUid != geteuid()) ? 0644 : 0600; 165 /* option F */ 166 DefUid = 1; /* option u */ 167 DefGid = 1; /* option g */ 168 CheckpointInterval = 10; /* option C */ 169 MaxHopCount = 25; /* option h */ 170 e->e_sendmode = SM_FORK; /* option d */ 171 e->e_errormode = EM_PRINT; /* option e */ 172 SevenBit = FALSE; /* option 7 */ 173 MaxMciCache = 1; /* option k */ 174 MciCacheTimeout = 300; /* option K */ 175 LogLevel = 9; /* option L */ 176 settimeouts(NULL); /* option r */ 177 TimeOuts.to_q_return = 5 DAYS; /* option T */ 178 TimeOuts.to_q_warning = 0; /* option T */ 179 PrivacyFlags = 0; /* option p */ 180 setdefuser(); 181 setupmaps(); 182 setupmailers(); 183 } 184 185 186 /* 187 ** SETDEFUSER -- set/reset DefUser using DefUid (for initgroups()) 188 */ 189 190 setdefuser() 191 { 192 struct passwd *defpwent; 193 static char defuserbuf[40]; 194 195 DefUser = defuserbuf; 196 if ((defpwent = getpwuid(DefUid)) != NULL) 197 strcpy(defuserbuf, defpwent->pw_name); 198 else 199 strcpy(defuserbuf, "nobody"); 200 } 201 /* 202 ** HOST_MAP_INIT -- initialize host class structures 203 */ 204 205 bool 206 host_map_init(map, args) 207 MAP *map; 208 char *args; 209 { 210 register char *p = args; 211 212 for (;;) 213 { 214 while (isascii(*p) && isspace(*p)) 215 p++; 216 if (*p != '-') 217 break; 218 switch (*++p) 219 { 220 case 'a': 221 map->map_app = ++p; 222 break; 223 } 224 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 225 p++; 226 if (*p != '\0') 227 *p++ = '\0'; 228 } 229 if (map->map_app != NULL) 230 map->map_app = newstr(map->map_app); 231 return TRUE; 232 } 233 /* 234 ** SETUPMAILERS -- initialize default mailers 235 */ 236 237 setupmailers() 238 { 239 char buf[100]; 240 241 strcpy(buf, "prog, P=/bin/sh, F=lsD, A=sh -c $u"); 242 makemailer(buf); 243 244 strcpy(buf, "*file*, P=/dev/null, F=lsDFMPEu, A=FILE"); 245 makemailer(buf); 246 247 strcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE"); 248 makemailer(buf); 249 } 250 /* 251 ** SETUPMAPS -- set up map classes 252 */ 253 254 #define MAPDEF(name, ext, flags, parse, open, close, lookup, store) \ 255 { \ 256 extern bool parse __P((MAP *, char *)); \ 257 extern bool open __P((MAP *, int)); \ 258 extern void close __P((MAP *)); \ 259 extern char *lookup __P((MAP *, char *, char **, int *)); \ 260 extern void store __P((MAP *, char *, char *)); \ 261 s = stab(name, ST_MAPCLASS, ST_ENTER); \ 262 s->s_mapclass.map_cname = name; \ 263 s->s_mapclass.map_ext = ext; \ 264 s->s_mapclass.map_cflags = flags; \ 265 s->s_mapclass.map_parse = parse; \ 266 s->s_mapclass.map_open = open; \ 267 s->s_mapclass.map_close = close; \ 268 s->s_mapclass.map_lookup = lookup; \ 269 s->s_mapclass.map_store = store; \ 270 } 271 272 setupmaps() 273 { 274 register STAB *s; 275 276 #ifdef NEWDB 277 MAPDEF("hash", ".db", MCF_ALIASOK|MCF_REBUILDABLE, 278 map_parseargs, hash_map_open, db_map_close, 279 db_map_lookup, db_map_store); 280 MAPDEF("btree", ".db", MCF_ALIASOK|MCF_REBUILDABLE, 281 map_parseargs, bt_map_open, db_map_close, 282 db_map_lookup, db_map_store); 283 #endif 284 285 #ifdef NDBM 286 MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE, 287 map_parseargs, ndbm_map_open, ndbm_map_close, 288 ndbm_map_lookup, ndbm_map_store); 289 #endif 290 291 #ifdef NIS 292 MAPDEF("nis", NULL, MCF_ALIASOK, 293 map_parseargs, nis_map_open, nis_map_close, 294 nis_map_lookup, nis_map_store); 295 #endif 296 297 MAPDEF("stab", NULL, MCF_ALIASOK|MCF_ALIASONLY, 298 map_parseargs, stab_map_open, stab_map_close, 299 stab_map_lookup, stab_map_store); 300 301 MAPDEF("implicit", NULL, MCF_ALIASOK|MCF_ALIASONLY|MCF_REBUILDABLE, 302 map_parseargs, impl_map_open, impl_map_close, 303 impl_map_lookup, impl_map_store); 304 305 /* host DNS lookup */ 306 MAPDEF("host", NULL, 0, 307 host_map_init, null_map_open, null_map_close, 308 host_map_lookup, null_map_store); 309 310 /* dequote map */ 311 MAPDEF("dequote", NULL, 0, 312 dequote_init, null_map_open, null_map_close, 313 dequote_map, null_map_store); 314 315 #if 0 316 # ifdef USERDB 317 /* user database */ 318 MAPDEF("udb", ".db", 0, 319 udb_map_parse, null_map_open, null_map_close, 320 udb_map_lookup, null_map_store); 321 # endif 322 #endif 323 } 324 325 #undef MAPDEF 326 /* 327 ** USERNAME -- return the user id of the logged in user. 328 ** 329 ** Parameters: 330 ** none. 331 ** 332 ** Returns: 333 ** The login name of the logged in user. 334 ** 335 ** Side Effects: 336 ** none. 337 ** 338 ** Notes: 339 ** The return value is statically allocated. 340 */ 341 342 char * 343 username() 344 { 345 static char *myname = NULL; 346 extern char *getlogin(); 347 register struct passwd *pw; 348 349 /* cache the result */ 350 if (myname == NULL) 351 { 352 myname = getlogin(); 353 if (myname == NULL || myname[0] == '\0') 354 { 355 pw = getpwuid(RealUid); 356 if (pw != NULL) 357 myname = newstr(pw->pw_name); 358 } 359 else 360 { 361 uid_t uid = RealUid; 362 363 myname = newstr(myname); 364 if ((pw = getpwnam(myname)) == NULL || 365 (uid != 0 && uid != pw->pw_uid)) 366 { 367 pw = getpwuid(uid); 368 if (pw != NULL) 369 myname = newstr(pw->pw_name); 370 } 371 } 372 if (myname == NULL || myname[0] == '\0') 373 { 374 syserr("554 Who are you?"); 375 myname = "postmaster"; 376 } 377 } 378 379 return (myname); 380 } 381 /* 382 ** TTYPATH -- Get the path of the user's tty 383 ** 384 ** Returns the pathname of the user's tty. Returns NULL if 385 ** the user is not logged in or if s/he has write permission 386 ** denied. 387 ** 388 ** Parameters: 389 ** none 390 ** 391 ** Returns: 392 ** pathname of the user's tty. 393 ** NULL if not logged in or write permission denied. 394 ** 395 ** Side Effects: 396 ** none. 397 ** 398 ** WARNING: 399 ** Return value is in a local buffer. 400 ** 401 ** Called By: 402 ** savemail 403 */ 404 405 char * 406 ttypath() 407 { 408 struct stat stbuf; 409 register char *pathn; 410 extern char *ttyname(); 411 extern char *getlogin(); 412 413 /* compute the pathname of the controlling tty */ 414 if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL && 415 (pathn = ttyname(0)) == NULL) 416 { 417 errno = 0; 418 return (NULL); 419 } 420 421 /* see if we have write permission */ 422 if (stat(pathn, &stbuf) < 0 || !bitset(02, stbuf.st_mode)) 423 { 424 errno = 0; 425 return (NULL); 426 } 427 428 /* see if the user is logged in */ 429 if (getlogin() == NULL) 430 return (NULL); 431 432 /* looks good */ 433 return (pathn); 434 } 435 /* 436 ** CHECKCOMPAT -- check for From and To person compatible. 437 ** 438 ** This routine can be supplied on a per-installation basis 439 ** to determine whether a person is allowed to send a message. 440 ** This allows restriction of certain types of internet 441 ** forwarding or registration of users. 442 ** 443 ** If the hosts are found to be incompatible, an error 444 ** message should be given using "usrerr" and 0 should 445 ** be returned. 446 ** 447 ** 'NoReturn' can be set to suppress the return-to-sender 448 ** function; this should be done on huge messages. 449 ** 450 ** Parameters: 451 ** to -- the person being sent to. 452 ** 453 ** Returns: 454 ** an exit status 455 ** 456 ** Side Effects: 457 ** none (unless you include the usrerr stuff) 458 */ 459 460 checkcompat(to, e) 461 register ADDRESS *to; 462 register ENVELOPE *e; 463 { 464 # ifdef lint 465 if (to == NULL) 466 to++; 467 # endif /* lint */ 468 469 if (tTd(49, 1)) 470 printf("checkcompat(to=%s, from=%s)\n", 471 to->q_paddr, e->e_from.q_paddr); 472 473 # ifdef EXAMPLE_CODE 474 /* this code is intended as an example only */ 475 register STAB *s; 476 477 s = stab("arpa", ST_MAILER, ST_FIND); 478 if (s != NULL && e->e_from.q_mailer != LocalMailer && 479 to->q_mailer == s->s_mailer) 480 { 481 usrerr("553 No ARPA mail through this machine: see your system administration"); 482 /* NoReturn = TRUE; to supress return copy */ 483 return (EX_UNAVAILABLE); 484 } 485 # endif /* EXAMPLE_CODE */ 486 return (EX_OK); 487 } 488 /* 489 ** SETSIGNAL -- set a signal handler 490 ** 491 ** This is essentially old BSD "signal(3)". 492 */ 493 494 setsig_t 495 setsignal(sig, handler) 496 int sig; 497 setsig_t handler; 498 { 499 #if defined(SYS5SIGNALS) || defined(BSD4_3) || defined(_AUX_SOURCE) 500 return signal(sig, handler); 501 #else 502 struct sigaction n, o; 503 504 bzero(&n, sizeof n); 505 n.sa_handler = handler; 506 if (sigaction(sig, &n, &o) < 0) 507 return SIG_ERR; 508 return o.sa_handler; 509 #endif 510 } 511 /* 512 ** HOLDSIGS -- arrange to hold all signals 513 ** 514 ** Parameters: 515 ** none. 516 ** 517 ** Returns: 518 ** none. 519 ** 520 ** Side Effects: 521 ** Arranges that signals are held. 522 */ 523 524 holdsigs() 525 { 526 } 527 /* 528 ** RLSESIGS -- arrange to release all signals 529 ** 530 ** This undoes the effect of holdsigs. 531 ** 532 ** Parameters: 533 ** none. 534 ** 535 ** Returns: 536 ** none. 537 ** 538 ** Side Effects: 539 ** Arranges that signals are released. 540 */ 541 542 rlsesigs() 543 { 544 } 545 /* 546 ** GETLA -- get the current load average 547 ** 548 ** This code stolen from la.c. 549 ** 550 ** Parameters: 551 ** none. 552 ** 553 ** Returns: 554 ** The current load average as an integer. 555 ** 556 ** Side Effects: 557 ** none. 558 */ 559 560 /* try to guess what style of load average we have */ 561 #define LA_ZERO 1 /* always return load average as zero */ 562 #define LA_INT 2 /* read kmem for avenrun; interpret as long */ 563 #define LA_FLOAT 3 /* read kmem for avenrun; interpret as float */ 564 #define LA_SUBR 4 /* call getloadavg */ 565 #define LA_MACH 5 /* MACH load averages (as on NeXT boxes) */ 566 #define LA_SHORT 6 /* read kmem for avenrun; interpret as short */ 567 568 /* do guesses based on general OS type */ 569 #ifndef LA_TYPE 570 # define LA_TYPE LA_ZERO 571 #endif 572 573 #if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT) 574 575 #include <nlist.h> 576 577 #ifndef LA_AVENRUN 578 # ifdef SYSTEM5 579 # define LA_AVENRUN "avenrun" 580 # else 581 # define LA_AVENRUN "_avenrun" 582 # endif 583 #endif 584 585 /* _PATH_UNIX should be defined in <paths.h> */ 586 #ifndef _PATH_UNIX 587 # if defined(SYSTEM5) 588 # define _PATH_UNIX "/unix" 589 # else 590 # define _PATH_UNIX "/vmunix" 591 # endif 592 #endif 593 594 struct nlist Nl[] = 595 { 596 { LA_AVENRUN }, 597 #define X_AVENRUN 0 598 { 0 }, 599 }; 600 601 #ifndef FSHIFT 602 # if defined(unixpc) 603 # define FSHIFT 5 604 # endif 605 606 # if defined(__alpha) 607 # define FSHIFT 10 608 # endif 609 610 # if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) 611 # define FSHIFT 8 612 # endif 613 #endif 614 615 #if ((LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT)) && !defined(FSCALE) 616 # define FSCALE (1 << FSHIFT) 617 #endif 618 619 getla() 620 { 621 static int kmem = -1; 622 #if LA_TYPE == LA_INT 623 long avenrun[3]; 624 #else 625 # if LA_TYPE == LA_SHORT 626 short avenrun[3]; 627 # else 628 double avenrun[3]; 629 # endif 630 #endif 631 extern off_t lseek(); 632 extern int errno; 633 634 if (kmem < 0) 635 { 636 kmem = open("/dev/kmem", 0, 0); 637 if (kmem < 0) 638 { 639 if (tTd(3, 1)) 640 printf("getla: open(/dev/kmem): %s\n", 641 errstring(errno)); 642 return (-1); 643 } 644 (void) fcntl(kmem, F_SETFD, 1); 645 if (nlist(_PATH_UNIX, Nl) < 0) 646 { 647 if (tTd(3, 1)) 648 printf("getla: nlist(%s): %s\n", _PATH_UNIX, 649 errstring(errno)); 650 return (-1); 651 } 652 if (Nl[X_AVENRUN].n_value == 0) 653 { 654 if (tTd(3, 1)) 655 printf("getla: nlist(%s, %s) ==> 0\n", 656 _PATH_UNIX, LA_AVENRUN); 657 return (-1); 658 } 659 } 660 if (tTd(3, 20)) 661 printf("getla: symbol address = %#x\n", Nl[X_AVENRUN].n_value); 662 if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, 0) == -1 || 663 read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun)) 664 { 665 /* thank you Ian */ 666 if (tTd(3, 1)) 667 printf("getla: lseek or read: %s\n", errstring(errno)); 668 return (-1); 669 } 670 #if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) 671 if (tTd(3, 5)) 672 { 673 printf("getla: avenrun = %d", avenrun[0]); 674 if (tTd(3, 15)) 675 printf(", %d, %d", avenrun[1], avenrun[2]); 676 printf("\n"); 677 } 678 if (tTd(3, 1)) 679 printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); 680 return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); 681 #else 682 if (tTd(3, 5)) 683 { 684 printf("getla: avenrun = %g", avenrun[0]); 685 if (tTd(3, 15)) 686 printf(", %g, %g", avenrun[1], avenrun[2]); 687 printf("\n"); 688 } 689 if (tTd(3, 1)) 690 printf("getla: %d\n", (int) (avenrun[0] +0.5)); 691 return ((int) (avenrun[0] + 0.5)); 692 #endif 693 } 694 695 #else 696 #if LA_TYPE == LA_SUBR 697 698 getla() 699 { 700 double avenrun[3]; 701 702 if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0) 703 { 704 if (tTd(3, 1)) 705 perror("getla: getloadavg failed:"); 706 return (-1); 707 } 708 if (tTd(3, 1)) 709 printf("getla: %d\n", (int) (avenrun[0] +0.5)); 710 return ((int) (avenrun[0] + 0.5)); 711 } 712 713 #else 714 #if LA_TYPE == LA_MACH 715 716 /* 717 ** This has been tested on NeXT release 2.1. 718 */ 719 720 #include <mach.h> 721 722 getla() 723 { 724 processor_set_t default_set; 725 kern_return_t error; 726 unsigned int info_count; 727 struct processor_set_basic_info info; 728 host_t host; 729 730 error = processor_set_default(host_self(), &default_set); 731 if (error != KERN_SUCCESS) 732 return -1; 733 info_count = PROCESSOR_SET_BASIC_INFO_COUNT; 734 if (processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO, 735 &host, (processor_set_info_t)&info, 736 &info_count) != KERN_SUCCESS) 737 { 738 return -1; 739 } 740 return (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE; 741 } 742 743 744 #else 745 746 getla() 747 { 748 if (tTd(3, 1)) 749 printf("getla: ZERO\n"); 750 return (0); 751 } 752 753 #endif 754 #endif 755 #endif 756 /* 757 ** SHOULDQUEUE -- should this message be queued or sent? 758 ** 759 ** Compares the message cost to the load average to decide. 760 ** 761 ** Parameters: 762 ** pri -- the priority of the message in question. 763 ** ctime -- the message creation time. 764 ** 765 ** Returns: 766 ** TRUE -- if this message should be queued up for the 767 ** time being. 768 ** FALSE -- if the load is low enough to send this message. 769 ** 770 ** Side Effects: 771 ** none. 772 */ 773 774 bool 775 shouldqueue(pri, ctime) 776 long pri; 777 time_t ctime; 778 { 779 if (CurrentLA < QueueLA) 780 return (FALSE); 781 if (CurrentLA >= RefuseLA) 782 return (TRUE); 783 return (pri > (QueueFactor / (CurrentLA - QueueLA + 1))); 784 } 785 /* 786 ** REFUSECONNECTIONS -- decide if connections should be refused 787 ** 788 ** Parameters: 789 ** none. 790 ** 791 ** Returns: 792 ** TRUE if incoming SMTP connections should be refused 793 ** (for now). 794 ** FALSE if we should accept new work. 795 ** 796 ** Side Effects: 797 ** none. 798 */ 799 800 bool 801 refuseconnections() 802 { 803 #ifdef XLA 804 if (!xla_smtp_ok()) 805 return TRUE; 806 #endif 807 808 /* this is probably too simplistic */ 809 return (CurrentLA >= RefuseLA); 810 } 811 /* 812 ** SETPROCTITLE -- set process title for ps 813 ** 814 ** Parameters: 815 ** fmt -- a printf style format string. 816 ** a, b, c -- possible parameters to fmt. 817 ** 818 ** Returns: 819 ** none. 820 ** 821 ** Side Effects: 822 ** Clobbers argv of our main procedure so ps(1) will 823 ** display the title. 824 */ 825 826 #ifdef SETPROCTITLE 827 # ifdef __hpux 828 # include <sys/pstat.h> 829 # endif 830 # ifdef BSD4_4 831 # include <machine/vmparam.h> 832 # include <sys/exec.h> 833 # ifdef __bsdi__ 834 # undef PS_STRINGS /* BSDI 1.0 doesn't do PS_STRINGS as we expect */ 835 # endif 836 # ifdef PS_STRINGS 837 # define SETPROC_STATIC static 838 # endif 839 # endif 840 # ifndef SETPROC_STATIC 841 # define SETPROC_STATIC 842 # endif 843 #endif 844 845 /*VARARGS1*/ 846 #ifdef __STDC__ 847 setproctitle(char *fmt, ...) 848 #else 849 setproctitle(fmt, va_alist) 850 char *fmt; 851 va_dcl 852 #endif 853 { 854 # ifdef SETPROCTITLE 855 register char *p; 856 register int i; 857 SETPROC_STATIC char buf[MAXLINE]; 858 VA_LOCAL_DECL 859 # ifdef __hpux 860 union pstun pst; 861 # endif 862 extern char **Argv; 863 extern char *LastArgv; 864 865 p = buf; 866 867 /* print sendmail: heading for grep */ 868 (void) strcpy(p, "sendmail: "); 869 p += strlen(p); 870 871 /* print the argument string */ 872 VA_START(fmt); 873 (void) vsprintf(p, fmt, ap); 874 VA_END; 875 876 i = strlen(buf); 877 878 # ifdef __hpux 879 pst.pst_command = buf; 880 pstat(PSTAT_SETCMD, pst, i, 0, 0); 881 # else 882 # ifdef PS_STRINGS 883 PS_STRINGS->ps_nargvstr = 1; 884 PS_STRINGS->ps_argvstr = buf; 885 # else 886 if (i > LastArgv - Argv[0] - 2) 887 { 888 i = LastArgv - Argv[0] - 2; 889 buf[i] = '\0'; 890 } 891 (void) strcpy(Argv[0], buf); 892 p = &Argv[0][i]; 893 while (p < LastArgv) 894 *p++ = ' '; 895 # endif 896 # endif 897 # endif /* SETPROCTITLE */ 898 } 899 /* 900 ** REAPCHILD -- pick up the body of my child, lest it become a zombie 901 ** 902 ** Parameters: 903 ** none. 904 ** 905 ** Returns: 906 ** none. 907 ** 908 ** Side Effects: 909 ** Picks up extant zombies. 910 */ 911 912 void 913 reapchild() 914 { 915 # ifdef HASWAITPID 916 auto int status; 917 int count; 918 int pid; 919 920 count = 0; 921 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) 922 { 923 if (count++ > 1000) 924 { 925 syslog(LOG_ALERT, "reapchild: waitpid loop: pid=%d, status=%x", 926 pid, status); 927 break; 928 } 929 } 930 # else 931 # ifdef WNOHANG 932 union wait status; 933 934 while (wait3(&status, WNOHANG, (struct rusage *) NULL) > 0) 935 continue; 936 # else /* WNOHANG */ 937 auto int status; 938 939 while (wait(&status) > 0) 940 continue; 941 # endif /* WNOHANG */ 942 # endif 943 # ifdef SYS5SIGNALS 944 (void) setsignal(SIGCHLD, reapchild); 945 # endif 946 } 947 /* 948 ** UNSETENV -- remove a variable from the environment 949 ** 950 ** Not needed on newer systems. 951 ** 952 ** Parameters: 953 ** name -- the string name of the environment variable to be 954 ** deleted from the current environment. 955 ** 956 ** Returns: 957 ** none. 958 ** 959 ** Globals: 960 ** environ -- a pointer to the current environment. 961 ** 962 ** Side Effects: 963 ** Modifies environ. 964 */ 965 966 #ifndef HASUNSETENV 967 968 void 969 unsetenv(name) 970 char *name; 971 { 972 extern char **environ; 973 register char **pp; 974 int len = strlen(name); 975 976 for (pp = environ; *pp != NULL; pp++) 977 { 978 if (strncmp(name, *pp, len) == 0 && 979 ((*pp)[len] == '=' || (*pp)[len] == '\0')) 980 break; 981 } 982 983 for (; *pp != NULL; pp++) 984 *pp = pp[1]; 985 } 986 987 #endif 988 /* 989 ** GETDTABLESIZE -- return number of file descriptors 990 ** 991 ** Only on non-BSD systems 992 ** 993 ** Parameters: 994 ** none 995 ** 996 ** Returns: 997 ** size of file descriptor table 998 ** 999 ** Side Effects: 1000 ** none 1001 */ 1002 1003 #ifdef SOLARIS 1004 # include <sys/resource.h> 1005 #endif 1006 1007 int 1008 getdtsize() 1009 { 1010 #ifdef RLIMIT_NOFILE 1011 struct rlimit rl; 1012 1013 if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) 1014 return rl.rlim_cur; 1015 #endif 1016 1017 # ifdef HASGETDTABLESIZE 1018 return getdtablesize(); 1019 # else 1020 # ifdef _SC_OPEN_MAX 1021 return sysconf(_SC_OPEN_MAX); 1022 # else 1023 return NOFILE; 1024 # endif 1025 # endif 1026 } 1027 /* 1028 ** UNAME -- get the UUCP name of this system. 1029 */ 1030 1031 #ifndef HASUNAME 1032 1033 int 1034 uname(name) 1035 struct utsname *name; 1036 { 1037 FILE *file; 1038 char *n; 1039 1040 name->nodename[0] = '\0'; 1041 1042 /* try /etc/whoami -- one line with the node name */ 1043 if ((file = fopen("/etc/whoami", "r")) != NULL) 1044 { 1045 (void) fgets(name->nodename, NODE_LENGTH + 1, file); 1046 (void) fclose(file); 1047 n = strchr(name->nodename, '\n'); 1048 if (n != NULL) 1049 *n = '\0'; 1050 if (name->nodename[0] != '\0') 1051 return (0); 1052 } 1053 1054 /* try /usr/include/whoami.h -- has a #define somewhere */ 1055 if ((file = fopen("/usr/include/whoami.h", "r")) != NULL) 1056 { 1057 char buf[MAXLINE]; 1058 1059 while (fgets(buf, MAXLINE, file) != NULL) 1060 if (sscanf(buf, "#define sysname \"%*[^\"]\"", 1061 NODE_LENGTH, name->nodename) > 0) 1062 break; 1063 (void) fclose(file); 1064 if (name->nodename[0] != '\0') 1065 return (0); 1066 } 1067 1068 #ifdef TRUST_POPEN 1069 /* 1070 ** Popen is known to have security holes. 1071 */ 1072 1073 /* try uuname -l to return local name */ 1074 if ((file = popen("uuname -l", "r")) != NULL) 1075 { 1076 (void) fgets(name, NODE_LENGTH + 1, file); 1077 (void) pclose(file); 1078 n = strchr(name, '\n'); 1079 if (n != NULL) 1080 *n = '\0'; 1081 if (name->nodename[0] != '\0') 1082 return (0); 1083 } 1084 #endif 1085 1086 return (-1); 1087 } 1088 #endif /* HASUNAME */ 1089 /* 1090 ** INITGROUPS -- initialize groups 1091 ** 1092 ** Stub implementation for System V style systems 1093 */ 1094 1095 #ifndef HASINITGROUPS 1096 1097 initgroups(name, basegid) 1098 char *name; 1099 int basegid; 1100 { 1101 return 0; 1102 } 1103 1104 #endif 1105 /* 1106 ** SETSID -- set session id (for non-POSIX systems) 1107 */ 1108 1109 #ifndef HASSETSID 1110 1111 pid_t 1112 setsid __P ((void)) 1113 { 1114 #ifdef TIOCNOTTY 1115 int fd; 1116 1117 fd = open("/dev/tty", 2); 1118 if (fd >= 0) 1119 { 1120 (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0); 1121 (void) close(fd); 1122 } 1123 #endif /* TIOCNOTTY */ 1124 # ifdef SYSTEM5 1125 return setpgrp(); 1126 # else 1127 return setpgid(0, getpid()); 1128 # endif 1129 } 1130 1131 #endif 1132 /* 1133 ** GETOPT -- for old systems or systems with bogus implementations 1134 */ 1135 1136 #ifdef NEEDGETOPT 1137 1138 /* 1139 * Copyright (c) 1985 Regents of the University of California. 1140 * All rights reserved. The Berkeley software License Agreement 1141 * specifies the terms and conditions for redistribution. 1142 */ 1143 1144 1145 /* 1146 ** this version hacked to add `atend' flag to allow state machine 1147 ** to reset if invoked by the program to scan args for a 2nd time 1148 */ 1149 1150 #if defined(LIBC_SCCS) && !defined(lint) 1151 static char sccsid[] = "@(#)getopt.c 4.3 (Berkeley) 3/9/86"; 1152 #endif /* LIBC_SCCS and not lint */ 1153 1154 #include <stdio.h> 1155 1156 /* 1157 * get option letter from argument vector 1158 */ 1159 int opterr = 1, /* if error message should be printed */ 1160 optind = 1, /* index into parent argv vector */ 1161 optopt; /* character checked for validity */ 1162 char *optarg; /* argument associated with option */ 1163 1164 #define BADCH (int)'?' 1165 #define EMSG "" 1166 #define tell(s) if (opterr) {fputs(*nargv,stderr);fputs(s,stderr); \ 1167 fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);} 1168 1169 getopt(nargc,nargv,ostr) 1170 int nargc; 1171 char **nargv, 1172 *ostr; 1173 { 1174 static char *place = EMSG; /* option letter processing */ 1175 static char atend = 0; 1176 register char *oli; /* option letter list index */ 1177 1178 if (atend) { 1179 atend = 0; 1180 place = EMSG; 1181 } 1182 if(!*place) { /* update scanning pointer */ 1183 if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) { 1184 atend++; 1185 return(EOF); 1186 } 1187 if (*place == '-') { /* found "--" */ 1188 ++optind; 1189 atend++; 1190 return(EOF); 1191 } 1192 } /* option letter okay? */ 1193 if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr,optopt))) { 1194 if (!*place) ++optind; 1195 tell(": illegal option -- "); 1196 } 1197 if (*++oli != ':') { /* don't need argument */ 1198 optarg = NULL; 1199 if (!*place) ++optind; 1200 } 1201 else { /* need an argument */ 1202 if (*place) optarg = place; /* no white space */ 1203 else if (nargc <= ++optind) { /* no arg */ 1204 place = EMSG; 1205 tell(": option requires an argument -- "); 1206 } 1207 else optarg = nargv[optind]; /* white space */ 1208 place = EMSG; 1209 ++optind; 1210 } 1211 return(optopt); /* dump back option letter */ 1212 } 1213 1214 #endif 1215 /* 1216 ** VFPRINTF, VSPRINTF -- for old 4.3 BSD systems missing a real version 1217 */ 1218 1219 #ifdef NEEDVPRINTF 1220 1221 #define MAXARG 16 1222 1223 vfprintf(fp, fmt, ap) 1224 FILE * fp; 1225 char * fmt; 1226 char ** ap; 1227 { 1228 char * bp[MAXARG]; 1229 int i = 0; 1230 1231 while (*ap && i < MAXARG) 1232 bp[i++] = *ap++; 1233 fprintf(fp, fmt, bp[0], bp[1], bp[2], bp[3], 1234 bp[4], bp[5], bp[6], bp[7], 1235 bp[8], bp[9], bp[10], bp[11], 1236 bp[12], bp[13], bp[14], bp[15]); 1237 } 1238 1239 vsprintf(s, fmt, ap) 1240 char * s; 1241 char * fmt; 1242 char ** ap; 1243 { 1244 char * bp[MAXARG]; 1245 int i = 0; 1246 1247 while (*ap && i < MAXARG) 1248 bp[i++] = *ap++; 1249 sprintf(s, fmt, bp[0], bp[1], bp[2], bp[3], 1250 bp[4], bp[5], bp[6], bp[7], 1251 bp[8], bp[9], bp[10], bp[11], 1252 bp[12], bp[13], bp[14], bp[15]); 1253 } 1254 1255 #endif 1256 /* 1257 ** FREESPACE -- see how much free space is on the queue filesystem 1258 ** 1259 ** Only implemented if you have statfs. 1260 ** 1261 ** Parameters: 1262 ** dir -- the directory in question. 1263 ** bsize -- a variable into which the filesystem 1264 ** block size is stored. 1265 ** 1266 ** Returns: 1267 ** The number of bytes free on the queue filesystem. 1268 ** -1 if the statfs call fails. 1269 ** 1270 ** Side effects: 1271 ** Puts the filesystem block size into bsize. 1272 */ 1273 1274 #ifdef HASSTATFS 1275 # undef HASUSTAT 1276 #endif 1277 1278 #if defined(HASUSTAT) 1279 # include <ustat.h> 1280 #endif 1281 1282 #ifdef HASSTATFS 1283 # if defined(IRIX) || defined(apollo) || defined(_SCO_unix_) 1284 # include <sys/statfs.h> 1285 # else 1286 # if (defined(sun) && !defined(BSD)) || defined(__hpux) || defined(_CONVEX_SOURCE) || defined(NeXT) || defined(_AUX_SOURCE) 1287 # include <sys/vfs.h> 1288 # else 1289 # include <sys/mount.h> 1290 # endif 1291 # endif 1292 #endif 1293 1294 long 1295 freespace(dir, bsize) 1296 char *dir; 1297 long *bsize; 1298 { 1299 #if defined(HASSTATFS) || defined(HASUSTAT) 1300 # if defined(HASUSTAT) 1301 struct ustat fs; 1302 struct stat statbuf; 1303 # define FSBLOCKSIZE DEV_BSIZE 1304 # define f_bavail f_tfree 1305 # else 1306 # if defined(ultrix) 1307 struct fs_data fs; 1308 # define f_bavail fd_bfreen 1309 # define FSBLOCKSIZE fs.fd_bsize 1310 # else 1311 struct statfs fs; 1312 # define FSBLOCKSIZE fs.f_bsize 1313 # if defined(_SCO_unix_) || defined(IRIX) 1314 # define f_bavail f_bfree 1315 # endif 1316 # endif 1317 # endif 1318 extern int errno; 1319 1320 # if defined(HASUSTAT) 1321 if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0) 1322 # else 1323 # if defined(IRIX) || defined(apollo) 1324 if (statfs(dir, &fs, sizeof fs, 0) == 0) 1325 # else 1326 # if defined(ultrix) 1327 if (statfs(dir, &fs) > 0) 1328 # else 1329 if (statfs(dir, &fs) == 0) 1330 # endif 1331 # endif 1332 # endif 1333 { 1334 if (bsize != NULL) 1335 *bsize = FSBLOCKSIZE; 1336 return (fs.f_bavail); 1337 } 1338 #endif 1339 return (-1); 1340 } 1341 /* 1342 ** ENOUGHSPACE -- check to see if there is enough free space on the queue fs 1343 ** 1344 ** Only implemented if you have statfs. 1345 ** 1346 ** Parameters: 1347 ** msize -- the size to check against. If zero, we don't yet 1348 ** know how big the message will be, so just check for 1349 ** a "reasonable" amount. 1350 ** 1351 ** Returns: 1352 ** TRUE if there is enough space. 1353 ** FALSE otherwise. 1354 */ 1355 1356 bool 1357 enoughspace(msize) 1358 long msize; 1359 { 1360 long bfree, bsize; 1361 1362 if (MinBlocksFree <= 0 && msize <= 0) 1363 { 1364 if (tTd(4, 80)) 1365 printf("enoughspace: no threshold\n"); 1366 return TRUE; 1367 } 1368 1369 if ((bfree = freespace(QueueDir, &bsize)) >= 0) 1370 { 1371 if (tTd(4, 80)) 1372 printf("enoughspace: bavail=%ld, need=%ld\n", 1373 bfree, msize); 1374 1375 /* convert msize to block count */ 1376 msize = msize / bsize + 1; 1377 if (MinBlocksFree >= 0) 1378 msize += MinBlocksFree; 1379 1380 if (bfree < msize) 1381 { 1382 #ifdef LOG 1383 if (LogLevel > 0) 1384 syslog(LOG_ALERT, 1385 "%s: low on space (have %ld, %s needs %ld in %s)", 1386 CurEnv->e_id, bfree, 1387 CurHostName, msize, QueueDir); 1388 #endif 1389 return FALSE; 1390 } 1391 } 1392 else if (tTd(4, 80)) 1393 printf("enoughspace failure: min=%ld, need=%ld: %s\n", 1394 MinBlocksFree, msize, errstring(errno)); 1395 return TRUE; 1396 } 1397 /* 1398 ** TRANSIENTERROR -- tell if an error code indicates a transient failure 1399 ** 1400 ** This looks at an errno value and tells if this is likely to 1401 ** go away if retried later. 1402 ** 1403 ** Parameters: 1404 ** err -- the errno code to classify. 1405 ** 1406 ** Returns: 1407 ** TRUE if this is probably transient. 1408 ** FALSE otherwise. 1409 */ 1410 1411 bool 1412 transienterror(err) 1413 int err; 1414 { 1415 switch (err) 1416 { 1417 case EIO: /* I/O error */ 1418 case ENXIO: /* Device not configured */ 1419 case EAGAIN: /* Resource temporarily unavailable */ 1420 case ENOMEM: /* Cannot allocate memory */ 1421 case ENODEV: /* Operation not supported by device */ 1422 case ENFILE: /* Too many open files in system */ 1423 case EMFILE: /* Too many open files */ 1424 case ENOSPC: /* No space left on device */ 1425 #ifdef ETIMEDOUT 1426 case ETIMEDOUT: /* Connection timed out */ 1427 #endif 1428 #ifdef ESTALE 1429 case ESTALE: /* Stale NFS file handle */ 1430 #endif 1431 #ifdef ENETDOWN 1432 case ENETDOWN: /* Network is down */ 1433 #endif 1434 #ifdef ENETUNREACH 1435 case ENETUNREACH: /* Network is unreachable */ 1436 #endif 1437 #ifdef ENETRESET 1438 case ENETRESET: /* Network dropped connection on reset */ 1439 #endif 1440 #ifdef ECONNABORTED 1441 case ECONNABORTED: /* Software caused connection abort */ 1442 #endif 1443 #ifdef ECONNRESET 1444 case ECONNRESET: /* Connection reset by peer */ 1445 #endif 1446 #ifdef ENOBUFS 1447 case ENOBUFS: /* No buffer space available */ 1448 #endif 1449 #ifdef ESHUTDOWN 1450 case ESHUTDOWN: /* Can't send after socket shutdown */ 1451 #endif 1452 #ifdef ECONNREFUSED 1453 case ECONNREFUSED: /* Connection refused */ 1454 #endif 1455 #ifdef EHOSTDOWN 1456 case EHOSTDOWN: /* Host is down */ 1457 #endif 1458 #ifdef EHOSTUNREACH 1459 case EHOSTUNREACH: /* No route to host */ 1460 #endif 1461 #ifdef EDQUOT 1462 case EDQUOT: /* Disc quota exceeded */ 1463 #endif 1464 #ifdef EPROCLIM 1465 case EPROCLIM: /* Too many processes */ 1466 #endif 1467 #ifdef EUSERS 1468 case EUSERS: /* Too many users */ 1469 #endif 1470 #ifdef EDEADLK 1471 case EDEADLK: /* Resource deadlock avoided */ 1472 #endif 1473 #ifdef EISCONN 1474 case EISCONN: /* Socket already connected */ 1475 #endif 1476 #ifdef EINPROGRESS 1477 case EINPROGRESS: /* Operation now in progress */ 1478 #endif 1479 #ifdef EALREADY 1480 case EALREADY: /* Operation already in progress */ 1481 #endif 1482 #ifdef EADDRINUSE 1483 case EADDRINUSE: /* Address already in use */ 1484 #endif 1485 #ifdef EADDRNOTAVAIL 1486 case EADDRNOTAVAIL: /* Can't assign requested address */ 1487 #endif 1488 #if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR)) 1489 case ENOSR: /* Out of streams resources */ 1490 #endif 1491 return TRUE; 1492 } 1493 1494 /* nope, must be permanent */ 1495 return FALSE; 1496 } 1497 /* 1498 ** LOCKFILE -- lock a file using flock or (shudder) fcntl locking 1499 ** 1500 ** Parameters: 1501 ** fd -- the file descriptor of the file. 1502 ** filename -- the file name (for error messages). 1503 ** ext -- the filename extension. 1504 ** type -- type of the lock. Bits can be: 1505 ** LOCK_EX -- exclusive lock. 1506 ** LOCK_NB -- non-blocking. 1507 ** 1508 ** Returns: 1509 ** TRUE if the lock was acquired. 1510 ** FALSE otherwise. 1511 */ 1512 1513 bool 1514 lockfile(fd, filename, ext, type) 1515 int fd; 1516 char *filename; 1517 char *ext; 1518 int type; 1519 { 1520 # ifndef HASFLOCK 1521 int action; 1522 struct flock lfd; 1523 1524 if (ext == NULL) 1525 ext = ""; 1526 1527 bzero(&lfd, sizeof lfd); 1528 if (bitset(LOCK_UN, type)) 1529 lfd.l_type = F_UNLCK; 1530 else if (bitset(LOCK_EX, type)) 1531 lfd.l_type = F_WRLCK; 1532 else 1533 lfd.l_type = F_RDLCK; 1534 1535 if (bitset(LOCK_NB, type)) 1536 action = F_SETLK; 1537 else 1538 action = F_SETLKW; 1539 1540 if (tTd(55, 60)) 1541 printf("lockfile(%s%s, action=%d, type=%d): ", 1542 filename, ext, action, lfd.l_type); 1543 1544 if (fcntl(fd, action, &lfd) >= 0) 1545 { 1546 if (tTd(55, 60)) 1547 printf("SUCCESS\n"); 1548 return TRUE; 1549 } 1550 1551 if (tTd(55, 60)) 1552 printf("(%s) ", errstring(errno)); 1553 1554 /* 1555 ** On SunOS, if you are testing using -oQ/tmp/mqueue or 1556 ** -oA/tmp/aliases or anything like that, and /tmp is mounted 1557 ** as type "tmp" (that is, served from swap space), the 1558 ** previous fcntl will fail with "Invalid argument" errors. 1559 ** Since this is fairly common during testing, we will assume 1560 ** that this indicates that the lock is successfully grabbed. 1561 */ 1562 1563 if (errno == EINVAL) 1564 { 1565 if (tTd(55, 60)) 1566 printf("SUCCESS\n"); 1567 return TRUE; 1568 } 1569 1570 if (!bitset(LOCK_NB, type) || (errno != EACCES && errno != EAGAIN)) 1571 { 1572 int omode = -1; 1573 # ifdef F_GETFL 1574 int oerrno = errno; 1575 1576 (void) fcntl(fd, F_GETFL, &omode); 1577 errno = oerrno; 1578 # endif 1579 syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", 1580 filename, ext, fd, type, omode, geteuid()); 1581 } 1582 # else 1583 if (ext == NULL) 1584 ext = ""; 1585 1586 if (tTd(55, 60)) 1587 printf("lockfile(%s%s, type=%o): ", filename, ext, type); 1588 1589 if (flock(fd, type) >= 0) 1590 { 1591 if (tTd(55, 60)) 1592 printf("SUCCESS\n"); 1593 return TRUE; 1594 } 1595 1596 if (tTd(55, 60)) 1597 printf("(%s) ", errstring(errno)); 1598 1599 if (!bitset(LOCK_NB, type) || errno != EWOULDBLOCK) 1600 { 1601 int omode = -1; 1602 # ifdef F_GETFL 1603 int oerrno = errno; 1604 1605 (void) fcntl(fd, F_GETFL, &omode); 1606 errno = oerrno; 1607 # endif 1608 syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", 1609 filename, ext, fd, type, omode, geteuid()); 1610 } 1611 # endif 1612 if (tTd(55, 60)) 1613 printf("FAIL\n"); 1614 return FALSE; 1615 } 1616