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.92 (Berkeley) 06/19/94"; 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 <netdb.h> 18 # include <pwd.h> 19 20 /* 21 ** CONF.C -- Sendmail Configuration Tables. 22 ** 23 ** Defines the configuration of this installation. 24 ** 25 ** Configuration Variables: 26 ** HdrInfo -- a table describing well-known header fields. 27 ** Each entry has the field name and some flags, 28 ** which are described in sendmail.h. 29 ** 30 ** Notes: 31 ** I have tried to put almost all the reasonable 32 ** configuration information into the configuration 33 ** file read at runtime. My intent is that anything 34 ** here is a function of the version of UNIX you 35 ** are running, or is really static -- for example 36 ** the headers are a superset of widely used 37 ** protocols. If you find yourself playing with 38 ** this file too much, you may be making a mistake! 39 */ 40 41 42 43 44 /* 45 ** Header info table 46 ** Final (null) entry contains the flags used for any other field. 47 ** 48 ** Not all of these are actually handled specially by sendmail 49 ** at this time. They are included as placeholders, to let 50 ** you know that "someday" I intend to have sendmail do 51 ** something with them. 52 */ 53 54 struct hdrinfo HdrInfo[] = 55 { 56 /* originator fields, most to least significant */ 57 "resent-sender", H_FROM|H_RESENT, 58 "resent-from", H_FROM|H_RESENT, 59 "resent-reply-to", H_FROM|H_RESENT, 60 "sender", H_FROM, 61 "from", H_FROM, 62 "reply-to", H_FROM, 63 "full-name", H_ACHECK, 64 "return-receipt-to", H_FROM|H_RECEIPTTO, 65 "errors-to", H_FROM|H_ERRORSTO, 66 67 /* destination fields */ 68 "to", H_RCPT, 69 "resent-to", H_RCPT|H_RESENT, 70 "cc", H_RCPT, 71 "resent-cc", H_RCPT|H_RESENT, 72 "bcc", H_RCPT|H_ACHECK, 73 "resent-bcc", H_RCPT|H_ACHECK|H_RESENT, 74 "apparently-to", H_RCPT, 75 76 /* message identification and control */ 77 "message-id", 0, 78 "resent-message-id", H_RESENT, 79 "message", H_EOH, 80 "text", H_EOH, 81 82 /* date fields */ 83 "date", 0, 84 "resent-date", H_RESENT, 85 86 /* trace fields */ 87 "received", H_TRACE|H_FORCE, 88 "x400-received", H_TRACE|H_FORCE, 89 "via", H_TRACE|H_FORCE, 90 "mail-from", H_TRACE|H_FORCE, 91 92 /* miscellaneous fields */ 93 "comments", H_FORCE, 94 "return-path", H_FORCE|H_ACHECK, 95 96 NULL, 0, 97 }; 98 99 100 101 /* 102 ** Location of system files/databases/etc. 103 */ 104 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 "noreceipts", PRIV_NORECEIPTS, 125 "goaway", PRIV_GOAWAY, 126 NULL, 0, 127 }; 128 129 130 131 /* 132 ** Miscellaneous stuff. 133 */ 134 135 int DtableSize = 50; /* max open files; reset in 4.2bsd */ 136 137 138 /* 139 ** Following should be config parameters (and probably will be in 140 ** future releases). In the meantime, setting these is considered 141 ** unsupported, and is intentionally undocumented. 142 */ 143 144 #ifdef BROKENSMTPPEERS 145 bool BrokenSmtpPeers = TRUE; /* set if you have broken SMTP peers */ 146 #else 147 bool BrokenSmtpPeers = FALSE; /* set if you have broken SMTP peers */ 148 #endif 149 #ifdef NOLOOPBACKCHECK 150 bool CheckLoopBack = FALSE; /* set to check HELO loopback */ 151 #else 152 bool CheckLoopBack = TRUE; /* set to check HELO loopback */ 153 #endif 154 155 /* 156 ** SETDEFAULTS -- set default values 157 ** 158 ** Because of the way freezing is done, these must be initialized 159 ** using direct code. 160 ** 161 ** Parameters: 162 ** e -- the default envelope. 163 ** 164 ** Returns: 165 ** none. 166 ** 167 ** Side Effects: 168 ** Initializes a bunch of global variables to their 169 ** default values. 170 */ 171 172 #define DAYS * 24 * 60 * 60 173 174 setdefaults(e) 175 register ENVELOPE *e; 176 { 177 SpaceSub = ' '; /* option B */ 178 QueueLA = 8; /* option x */ 179 RefuseLA = 12; /* option X */ 180 WkRecipFact = 30000L; /* option y */ 181 WkClassFact = 1800L; /* option z */ 182 WkTimeFact = 90000L; /* option Z */ 183 QueueFactor = WkRecipFact * 20; /* option q */ 184 FileMode = (RealUid != geteuid()) ? 0644 : 0600; 185 /* option F */ 186 DefUid = 1; /* option u */ 187 DefGid = 1; /* option g */ 188 CheckpointInterval = 10; /* option C */ 189 MaxHopCount = 25; /* option h */ 190 e->e_sendmode = SM_FORK; /* option d */ 191 e->e_errormode = EM_PRINT; /* option e */ 192 SevenBit = FALSE; /* option 7 */ 193 MaxMciCache = 1; /* option k */ 194 MciCacheTimeout = 300; /* option K */ 195 LogLevel = 9; /* option L */ 196 settimeouts(NULL); /* option r */ 197 TimeOuts.to_q_return = 5 DAYS; /* option T */ 198 TimeOuts.to_q_warning = 0; /* option T */ 199 PrivacyFlags = 0; /* option p */ 200 setdefuser(); 201 setupmaps(); 202 setupmailers(); 203 } 204 205 206 /* 207 ** SETDEFUSER -- set/reset DefUser using DefUid (for initgroups()) 208 */ 209 210 setdefuser() 211 { 212 struct passwd *defpwent; 213 static char defuserbuf[40]; 214 215 DefUser = defuserbuf; 216 if ((defpwent = getpwuid(DefUid)) != NULL) 217 strcpy(defuserbuf, defpwent->pw_name); 218 else 219 strcpy(defuserbuf, "nobody"); 220 } 221 /* 222 ** HOST_MAP_INIT -- initialize host class structures 223 */ 224 225 bool 226 host_map_init(map, args) 227 MAP *map; 228 char *args; 229 { 230 register char *p = args; 231 232 for (;;) 233 { 234 while (isascii(*p) && isspace(*p)) 235 p++; 236 if (*p != '-') 237 break; 238 switch (*++p) 239 { 240 case 'a': 241 map->map_app = ++p; 242 break; 243 } 244 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 245 p++; 246 if (*p != '\0') 247 *p++ = '\0'; 248 } 249 if (map->map_app != NULL) 250 map->map_app = newstr(map->map_app); 251 return TRUE; 252 } 253 /* 254 ** SETUPMAILERS -- initialize default mailers 255 */ 256 257 setupmailers() 258 { 259 char buf[100]; 260 261 strcpy(buf, "prog, P=/bin/sh, F=lsD, A=sh -c $u"); 262 makemailer(buf); 263 264 strcpy(buf, "*file*, P=/dev/null, F=lsDFMPEu, A=FILE"); 265 makemailer(buf); 266 267 strcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE"); 268 makemailer(buf); 269 } 270 /* 271 ** SETUPMAPS -- set up map classes 272 */ 273 274 #define MAPDEF(name, ext, flags, parse, open, close, lookup, store) \ 275 { \ 276 extern bool parse __P((MAP *, char *)); \ 277 extern bool open __P((MAP *, int)); \ 278 extern void close __P((MAP *)); \ 279 extern char *lookup __P((MAP *, char *, char **, int *)); \ 280 extern void store __P((MAP *, char *, char *)); \ 281 s = stab(name, ST_MAPCLASS, ST_ENTER); \ 282 s->s_mapclass.map_cname = name; \ 283 s->s_mapclass.map_ext = ext; \ 284 s->s_mapclass.map_cflags = flags; \ 285 s->s_mapclass.map_parse = parse; \ 286 s->s_mapclass.map_open = open; \ 287 s->s_mapclass.map_close = close; \ 288 s->s_mapclass.map_lookup = lookup; \ 289 s->s_mapclass.map_store = store; \ 290 } 291 292 setupmaps() 293 { 294 register STAB *s; 295 296 #ifdef NEWDB 297 MAPDEF("hash", ".db", MCF_ALIASOK|MCF_REBUILDABLE, 298 map_parseargs, hash_map_open, db_map_close, 299 db_map_lookup, db_map_store); 300 MAPDEF("btree", ".db", MCF_ALIASOK|MCF_REBUILDABLE, 301 map_parseargs, bt_map_open, db_map_close, 302 db_map_lookup, db_map_store); 303 #endif 304 305 #ifdef NDBM 306 MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE, 307 map_parseargs, ndbm_map_open, ndbm_map_close, 308 ndbm_map_lookup, ndbm_map_store); 309 #endif 310 311 #ifdef NIS 312 MAPDEF("nis", NULL, MCF_ALIASOK, 313 map_parseargs, nis_map_open, nis_map_close, 314 nis_map_lookup, nis_map_store); 315 #endif 316 317 MAPDEF("stab", NULL, MCF_ALIASOK|MCF_ALIASONLY, 318 map_parseargs, stab_map_open, stab_map_close, 319 stab_map_lookup, stab_map_store); 320 321 MAPDEF("implicit", NULL, MCF_ALIASOK|MCF_ALIASONLY|MCF_REBUILDABLE, 322 map_parseargs, impl_map_open, impl_map_close, 323 impl_map_lookup, impl_map_store); 324 325 /* host DNS lookup */ 326 MAPDEF("host", NULL, 0, 327 host_map_init, null_map_open, null_map_close, 328 host_map_lookup, null_map_store); 329 330 /* dequote map */ 331 MAPDEF("dequote", NULL, 0, 332 dequote_init, null_map_open, null_map_close, 333 dequote_map, null_map_store); 334 335 #if 0 336 # ifdef USERDB 337 /* user database */ 338 MAPDEF("udb", ".db", 0, 339 udb_map_parse, null_map_open, null_map_close, 340 udb_map_lookup, null_map_store); 341 # endif 342 #endif 343 } 344 345 #undef MAPDEF 346 /* 347 ** USERNAME -- return the user id of the logged in user. 348 ** 349 ** Parameters: 350 ** none. 351 ** 352 ** Returns: 353 ** The login name of the logged in user. 354 ** 355 ** Side Effects: 356 ** none. 357 ** 358 ** Notes: 359 ** The return value is statically allocated. 360 */ 361 362 char * 363 username() 364 { 365 static char *myname = NULL; 366 extern char *getlogin(); 367 register struct passwd *pw; 368 369 /* cache the result */ 370 if (myname == NULL) 371 { 372 myname = getlogin(); 373 if (myname == NULL || myname[0] == '\0') 374 { 375 pw = getpwuid(RealUid); 376 if (pw != NULL) 377 myname = newstr(pw->pw_name); 378 } 379 else 380 { 381 uid_t uid = RealUid; 382 383 myname = newstr(myname); 384 if ((pw = getpwnam(myname)) == NULL || 385 (uid != 0 && uid != pw->pw_uid)) 386 { 387 pw = getpwuid(uid); 388 if (pw != NULL) 389 myname = newstr(pw->pw_name); 390 } 391 } 392 if (myname == NULL || myname[0] == '\0') 393 { 394 syserr("554 Who are you?"); 395 myname = "postmaster"; 396 } 397 } 398 399 return (myname); 400 } 401 /* 402 ** TTYPATH -- Get the path of the user's tty 403 ** 404 ** Returns the pathname of the user's tty. Returns NULL if 405 ** the user is not logged in or if s/he has write permission 406 ** denied. 407 ** 408 ** Parameters: 409 ** none 410 ** 411 ** Returns: 412 ** pathname of the user's tty. 413 ** NULL if not logged in or write permission denied. 414 ** 415 ** Side Effects: 416 ** none. 417 ** 418 ** WARNING: 419 ** Return value is in a local buffer. 420 ** 421 ** Called By: 422 ** savemail 423 */ 424 425 char * 426 ttypath() 427 { 428 struct stat stbuf; 429 register char *pathn; 430 extern char *ttyname(); 431 extern char *getlogin(); 432 433 /* compute the pathname of the controlling tty */ 434 if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL && 435 (pathn = ttyname(0)) == NULL) 436 { 437 errno = 0; 438 return (NULL); 439 } 440 441 /* see if we have write permission */ 442 if (stat(pathn, &stbuf) < 0 || !bitset(02, stbuf.st_mode)) 443 { 444 errno = 0; 445 return (NULL); 446 } 447 448 /* see if the user is logged in */ 449 if (getlogin() == NULL) 450 return (NULL); 451 452 /* looks good */ 453 return (pathn); 454 } 455 /* 456 ** CHECKCOMPAT -- check for From and To person compatible. 457 ** 458 ** This routine can be supplied on a per-installation basis 459 ** to determine whether a person is allowed to send a message. 460 ** This allows restriction of certain types of internet 461 ** forwarding or registration of users. 462 ** 463 ** If the hosts are found to be incompatible, an error 464 ** message should be given using "usrerr" and 0 should 465 ** be returned. 466 ** 467 ** 'NoReturn' can be set to suppress the return-to-sender 468 ** function; this should be done on huge messages. 469 ** 470 ** Parameters: 471 ** to -- the person being sent to. 472 ** 473 ** Returns: 474 ** an exit status 475 ** 476 ** Side Effects: 477 ** none (unless you include the usrerr stuff) 478 */ 479 480 checkcompat(to, e) 481 register ADDRESS *to; 482 register ENVELOPE *e; 483 { 484 # ifdef lint 485 if (to == NULL) 486 to++; 487 # endif /* lint */ 488 489 if (tTd(49, 1)) 490 printf("checkcompat(to=%s, from=%s)\n", 491 to->q_paddr, e->e_from.q_paddr); 492 493 # ifdef EXAMPLE_CODE 494 /* this code is intended as an example only */ 495 register STAB *s; 496 497 s = stab("arpa", ST_MAILER, ST_FIND); 498 if (s != NULL && e->e_from.q_mailer != LocalMailer && 499 to->q_mailer == s->s_mailer) 500 { 501 usrerr("553 No ARPA mail through this machine: see your system administration"); 502 /* NoReturn = TRUE; to supress return copy */ 503 return (EX_UNAVAILABLE); 504 } 505 # endif /* EXAMPLE_CODE */ 506 return (EX_OK); 507 } 508 /* 509 ** SETSIGNAL -- set a signal handler 510 ** 511 ** This is essentially old BSD "signal(3)". 512 */ 513 514 sigfunc_t 515 setsignal(sig, handler) 516 int sig; 517 sigfunc_t handler; 518 { 519 #if defined(SYS5SIGNALS) || defined(BSD4_3) || defined(_AUX_SOURCE) 520 return signal(sig, handler); 521 #else 522 struct sigaction n, o; 523 524 bzero(&n, sizeof n); 525 n.sa_handler = handler; 526 # ifdef SA_RESTART 527 n.sa_flags = SA_RESTART; 528 # endif 529 if (sigaction(sig, &n, &o) < 0) 530 return SIG_ERR; 531 return o.sa_handler; 532 #endif 533 } 534 /* 535 ** HOLDSIGS -- arrange to hold all signals 536 ** 537 ** Parameters: 538 ** none. 539 ** 540 ** Returns: 541 ** none. 542 ** 543 ** Side Effects: 544 ** Arranges that signals are held. 545 */ 546 547 holdsigs() 548 { 549 } 550 /* 551 ** RLSESIGS -- arrange to release all signals 552 ** 553 ** This undoes the effect of holdsigs. 554 ** 555 ** Parameters: 556 ** none. 557 ** 558 ** Returns: 559 ** none. 560 ** 561 ** Side Effects: 562 ** Arranges that signals are released. 563 */ 564 565 rlsesigs() 566 { 567 } 568 /* 569 ** INIT_MD -- do machine dependent initializations 570 ** 571 ** Systems that have global modes that should be set should do 572 ** them here rather than in main. 573 */ 574 575 #ifdef _AUX_SOURCE 576 # include <compat.h> 577 #endif 578 579 init_md(argc, argv) 580 int argc; 581 char **argv; 582 { 583 #ifdef _AUX_SOURCE 584 setcompat(getcompat() | COMPAT_BSDPROT); 585 #endif 586 } 587 /* 588 ** GETLA -- get the current load average 589 ** 590 ** This code stolen from la.c. 591 ** 592 ** Parameters: 593 ** none. 594 ** 595 ** Returns: 596 ** The current load average as an integer. 597 ** 598 ** Side Effects: 599 ** none. 600 */ 601 602 /* try to guess what style of load average we have */ 603 #define LA_ZERO 1 /* always return load average as zero */ 604 #define LA_INT 2 /* read kmem for avenrun; interpret as long */ 605 #define LA_FLOAT 3 /* read kmem for avenrun; interpret as float */ 606 #define LA_SUBR 4 /* call getloadavg */ 607 #define LA_MACH 5 /* MACH load averages (as on NeXT boxes) */ 608 #define LA_SHORT 6 /* read kmem for avenrun; interpret as short */ 609 #define LA_PROCSTR 7 /* read string ("1.17") from /proc/loadavg */ 610 611 /* do guesses based on general OS type */ 612 #ifndef LA_TYPE 613 # define LA_TYPE LA_ZERO 614 #endif 615 616 #if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT) 617 618 #include <nlist.h> 619 620 #ifndef LA_AVENRUN 621 # ifdef SYSTEM5 622 # define LA_AVENRUN "avenrun" 623 # else 624 # define LA_AVENRUN "_avenrun" 625 # endif 626 #endif 627 628 /* _PATH_UNIX should be defined in <paths.h> */ 629 #ifndef _PATH_UNIX 630 # if defined(SYSTEM5) 631 # define _PATH_UNIX "/unix" 632 # else 633 # define _PATH_UNIX "/vmunix" 634 # endif 635 #endif 636 637 struct nlist Nl[] = 638 { 639 { LA_AVENRUN }, 640 #define X_AVENRUN 0 641 { 0 }, 642 }; 643 644 #ifndef FSHIFT 645 # if defined(unixpc) 646 # define FSHIFT 5 647 # endif 648 649 # if defined(__alpha) || defined(IRIX) 650 # define FSHIFT 10 651 # endif 652 #endif 653 654 #ifndef FSHIFT 655 # define FSHIFT 8 656 #endif 657 658 #ifndef FSCALE 659 # define FSCALE (1 << FSHIFT) 660 #endif 661 662 getla() 663 { 664 static int kmem = -1; 665 #if LA_TYPE == LA_INT 666 long avenrun[3]; 667 #else 668 # if LA_TYPE == LA_SHORT 669 short avenrun[3]; 670 # else 671 double avenrun[3]; 672 # endif 673 #endif 674 extern off_t lseek(); 675 extern int errno; 676 677 if (kmem < 0) 678 { 679 kmem = open("/dev/kmem", 0, 0); 680 if (kmem < 0) 681 { 682 if (tTd(3, 1)) 683 printf("getla: open(/dev/kmem): %s\n", 684 errstring(errno)); 685 return (-1); 686 } 687 (void) fcntl(kmem, F_SETFD, 1); 688 if (nlist(_PATH_UNIX, Nl) < 0) 689 { 690 if (tTd(3, 1)) 691 printf("getla: nlist(%s): %s\n", _PATH_UNIX, 692 errstring(errno)); 693 return (-1); 694 } 695 if (Nl[X_AVENRUN].n_value == 0) 696 { 697 if (tTd(3, 1)) 698 printf("getla: nlist(%s, %s) ==> 0\n", 699 _PATH_UNIX, LA_AVENRUN); 700 return (-1); 701 } 702 #ifdef IRIX 703 Nl[X_AVENRUN].n_value &= 0x7fffffff; 704 #endif 705 } 706 if (tTd(3, 20)) 707 printf("getla: symbol address = %#x\n", Nl[X_AVENRUN].n_value); 708 if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, 0) == -1 || 709 read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun)) 710 { 711 /* thank you Ian */ 712 if (tTd(3, 1)) 713 printf("getla: lseek or read: %s\n", errstring(errno)); 714 return (-1); 715 } 716 #if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) 717 if (tTd(3, 5)) 718 { 719 printf("getla: avenrun = %d", avenrun[0]); 720 if (tTd(3, 15)) 721 printf(", %d, %d", avenrun[1], avenrun[2]); 722 printf("\n"); 723 } 724 if (tTd(3, 1)) 725 printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); 726 return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); 727 #else 728 if (tTd(3, 5)) 729 { 730 printf("getla: avenrun = %g", avenrun[0]); 731 if (tTd(3, 15)) 732 printf(", %g, %g", avenrun[1], avenrun[2]); 733 printf("\n"); 734 } 735 if (tTd(3, 1)) 736 printf("getla: %d\n", (int) (avenrun[0] +0.5)); 737 return ((int) (avenrun[0] + 0.5)); 738 #endif 739 } 740 741 #else 742 #if LA_TYPE == LA_SUBR 743 744 #ifdef DGUX 745 746 #include <sys/dg_sys_info.h> 747 748 int getla() 749 { 750 struct dg_sys_info_load_info load_info; 751 752 dg_sys_info((long *)&load_info, 753 DG_SYS_INFO_LOAD_INFO_TYPE, DG_SYS_INFO_LOAD_VERSION_0); 754 755 return((int) (load_info.one_minute + 0.5)); 756 } 757 758 #else 759 760 getla() 761 { 762 double avenrun[3]; 763 764 if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0) 765 { 766 if (tTd(3, 1)) 767 perror("getla: getloadavg failed:"); 768 return (-1); 769 } 770 if (tTd(3, 1)) 771 printf("getla: %d\n", (int) (avenrun[0] +0.5)); 772 return ((int) (avenrun[0] + 0.5)); 773 } 774 775 #endif /* DGUX */ 776 #else 777 #if LA_TYPE == LA_MACH 778 779 /* 780 ** This has been tested on NEXTSTEP release 2.1/3.X. 781 */ 782 783 #if defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0 784 # include <mach/mach.h> 785 #else 786 # include <mach.h> 787 #endif 788 789 getla() 790 { 791 processor_set_t default_set; 792 kern_return_t error; 793 unsigned int info_count; 794 struct processor_set_basic_info info; 795 host_t host; 796 797 error = processor_set_default(host_self(), &default_set); 798 if (error != KERN_SUCCESS) 799 return -1; 800 info_count = PROCESSOR_SET_BASIC_INFO_COUNT; 801 if (processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO, 802 &host, (processor_set_info_t)&info, 803 &info_count) != KERN_SUCCESS) 804 { 805 return -1; 806 } 807 return (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE; 808 } 809 810 811 #else 812 #if LA_TYPE == LA_PROCSTR 813 814 /* 815 ** Read /proc/loadavg for the load average. This is assumed to be 816 ** in a format like "0.15 0.12 0.06". 817 ** 818 ** Initially intended for Linux. This has been in the kernel 819 ** since at least 0.99.15. 820 */ 821 822 # ifndef _PATH_LOADAVG 823 # define _PATH_LOADAVG "/proc/loadavg" 824 # endif 825 826 int 827 getla() 828 { 829 double avenrun; 830 register int result; 831 FILE *fp; 832 833 fp = fopen(_PATH_LOADAVG, "r"); 834 if (fp == NULL) 835 { 836 if (tTd(3, 1)) 837 printf("getla: fopen(%s): %s\n", 838 _PATH_LOADAVG, errstring(errno)); 839 return -1; 840 } 841 result = fscanf(fp, "%lf", &avenrun); 842 fclose(fp); 843 if (result != 1) 844 { 845 if (tTd(3, 1)) 846 printf("getla: fscanf() = %d: %s\n", 847 result, errstring(errno)); 848 return -1; 849 } 850 851 if (tTd(3, 1)) 852 printf("getla(): %.2f\n", avenrun); 853 854 return ((int) (avenrun + 0.5)); 855 } 856 857 #else 858 859 getla() 860 { 861 if (tTd(3, 1)) 862 printf("getla: ZERO\n"); 863 return (0); 864 } 865 866 #endif 867 #endif 868 #endif 869 #endif 870 871 872 /* 873 * Copyright 1989 Massachusetts Institute of Technology 874 * 875 * Permission to use, copy, modify, distribute, and sell this software and its 876 * documentation for any purpose is hereby granted without fee, provided that 877 * the above copyright notice appear in all copies and that both that 878 * copyright notice and this permission notice appear in supporting 879 * documentation, and that the name of M.I.T. not be used in advertising or 880 * publicity pertaining to distribution of the software without specific, 881 * written prior permission. M.I.T. makes no representations about the 882 * suitability of this software for any purpose. It is provided "as is" 883 * without express or implied warranty. 884 * 885 * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL 886 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. 887 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 888 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 889 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 890 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 891 * 892 * Authors: Many and varied... 893 */ 894 895 /* Non Apollo stuff removed by Don Lewis 11/15/93 */ 896 #ifndef lint 897 static char rcsid[] = "@(#)$Id: getloadavg.c,v 1.16 1991/06/21 12:51:15 paul Exp $"; 898 #endif /* !lint */ 899 900 #ifdef apollo 901 # undef volatile 902 # include <apollo/base.h> 903 904 /* ARGSUSED */ 905 int getloadavg( call_data ) 906 caddr_t call_data; /* pointer to (double) return value */ 907 { 908 double *avenrun = (double *) call_data; 909 int i; 910 status_$t st; 911 long loadav[3]; 912 proc1_$get_loadav(loadav, &st); 913 *avenrun = loadav[0] / (double) (1 << 16); 914 return(0); 915 } 916 # endif /* apollo */ 917 /* 918 ** SHOULDQUEUE -- should this message be queued or sent? 919 ** 920 ** Compares the message cost to the load average to decide. 921 ** 922 ** Parameters: 923 ** pri -- the priority of the message in question. 924 ** ctime -- the message creation time. 925 ** 926 ** Returns: 927 ** TRUE -- if this message should be queued up for the 928 ** time being. 929 ** FALSE -- if the load is low enough to send this message. 930 ** 931 ** Side Effects: 932 ** none. 933 */ 934 935 bool 936 shouldqueue(pri, ctime) 937 long pri; 938 time_t ctime; 939 { 940 if (CurrentLA < QueueLA) 941 return (FALSE); 942 if (CurrentLA >= RefuseLA) 943 return (TRUE); 944 return (pri > (QueueFactor / (CurrentLA - QueueLA + 1))); 945 } 946 /* 947 ** REFUSECONNECTIONS -- decide if connections should be refused 948 ** 949 ** Parameters: 950 ** none. 951 ** 952 ** Returns: 953 ** TRUE if incoming SMTP connections should be refused 954 ** (for now). 955 ** FALSE if we should accept new work. 956 ** 957 ** Side Effects: 958 ** none. 959 */ 960 961 bool 962 refuseconnections() 963 { 964 #ifdef XLA 965 if (!xla_smtp_ok()) 966 return TRUE; 967 #endif 968 969 /* this is probably too simplistic */ 970 return (CurrentLA >= RefuseLA); 971 } 972 /* 973 ** SETPROCTITLE -- set process title for ps 974 ** 975 ** Parameters: 976 ** fmt -- a printf style format string. 977 ** a, b, c -- possible parameters to fmt. 978 ** 979 ** Returns: 980 ** none. 981 ** 982 ** Side Effects: 983 ** Clobbers argv of our main procedure so ps(1) will 984 ** display the title. 985 */ 986 987 #ifdef SETPROCTITLE 988 # ifdef HASSETPROCTITLE 989 *** ERROR *** Cannot have both SETPROCTITLE and HASSETPROCTITLE defined 990 # endif 991 # ifdef __hpux 992 # include <sys/pstat.h> 993 # endif 994 # ifdef BSD4_4 995 # include <machine/vmparam.h> 996 # include <sys/exec.h> 997 # ifdef __bsdi__ 998 # undef PS_STRINGS /* BSDI 1.0 doesn't do PS_STRINGS as we expect */ 999 # define PROCTITLEPAD '\0' 1000 # endif 1001 # ifdef PS_STRINGS 1002 # define SETPROC_STATIC static 1003 # endif 1004 # endif 1005 # ifndef SETPROC_STATIC 1006 # define SETPROC_STATIC 1007 # endif 1008 #endif 1009 1010 #ifndef PROCTITLEPAD 1011 # define PROCTITLEPAD ' ' 1012 #endif 1013 1014 #ifndef HASSETPROCTITLE 1015 1016 /*VARARGS1*/ 1017 #ifdef __STDC__ 1018 setproctitle(char *fmt, ...) 1019 #else 1020 setproctitle(fmt, va_alist) 1021 char *fmt; 1022 va_dcl 1023 #endif 1024 { 1025 # ifdef SETPROCTITLE 1026 register char *p; 1027 register int i; 1028 SETPROC_STATIC char buf[MAXLINE]; 1029 VA_LOCAL_DECL 1030 # ifdef __hpux 1031 union pstun pst; 1032 # endif 1033 extern char **Argv; 1034 extern char *LastArgv; 1035 1036 p = buf; 1037 1038 /* print sendmail: heading for grep */ 1039 (void) strcpy(p, "sendmail: "); 1040 p += strlen(p); 1041 1042 /* print the argument string */ 1043 VA_START(fmt); 1044 (void) vsprintf(p, fmt, ap); 1045 VA_END; 1046 1047 i = strlen(buf); 1048 1049 # ifdef __hpux 1050 pst.pst_command = buf; 1051 pstat(PSTAT_SETCMD, pst, i, 0, 0); 1052 # else 1053 # ifdef PS_STRINGS 1054 PS_STRINGS->ps_nargvstr = 1; 1055 PS_STRINGS->ps_argvstr = buf; 1056 # else 1057 if (i > LastArgv - Argv[0] - 2) 1058 { 1059 i = LastArgv - Argv[0] - 2; 1060 buf[i] = '\0'; 1061 } 1062 (void) strcpy(Argv[0], buf); 1063 p = &Argv[0][i]; 1064 while (p < LastArgv) 1065 *p++ = PROCTITLEPAD; 1066 # endif 1067 # endif 1068 # endif /* SETPROCTITLE */ 1069 } 1070 1071 #endif 1072 /* 1073 ** REAPCHILD -- pick up the body of my child, lest it become a zombie 1074 ** 1075 ** Parameters: 1076 ** none. 1077 ** 1078 ** Returns: 1079 ** none. 1080 ** 1081 ** Side Effects: 1082 ** Picks up extant zombies. 1083 */ 1084 1085 void 1086 reapchild() 1087 { 1088 int olderrno = errno; 1089 # ifdef HASWAITPID 1090 auto int status; 1091 int count; 1092 int pid; 1093 1094 count = 0; 1095 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) 1096 { 1097 if (count++ > 1000) 1098 { 1099 #ifdef LOG 1100 syslog(LOG_ALERT, "reapchild: waitpid loop: pid=%d, status=%x", 1101 pid, status); 1102 #endif 1103 break; 1104 } 1105 } 1106 # else 1107 # ifdef WNOHANG 1108 union wait status; 1109 1110 while (wait3(&status, WNOHANG, (struct rusage *) NULL) > 0) 1111 continue; 1112 # else /* WNOHANG */ 1113 auto int status; 1114 1115 while (wait(&status) > 0) 1116 continue; 1117 # endif /* WNOHANG */ 1118 # endif 1119 # ifdef SYS5SIGNALS 1120 (void) setsignal(SIGCHLD, reapchild); 1121 # endif 1122 errno = olderrno; 1123 } 1124 /* 1125 ** UNSETENV -- remove a variable from the environment 1126 ** 1127 ** Not needed on newer systems. 1128 ** 1129 ** Parameters: 1130 ** name -- the string name of the environment variable to be 1131 ** deleted from the current environment. 1132 ** 1133 ** Returns: 1134 ** none. 1135 ** 1136 ** Globals: 1137 ** environ -- a pointer to the current environment. 1138 ** 1139 ** Side Effects: 1140 ** Modifies environ. 1141 */ 1142 1143 #ifndef HASUNSETENV 1144 1145 void 1146 unsetenv(name) 1147 char *name; 1148 { 1149 extern char **environ; 1150 register char **pp; 1151 int len = strlen(name); 1152 1153 for (pp = environ; *pp != NULL; pp++) 1154 { 1155 if (strncmp(name, *pp, len) == 0 && 1156 ((*pp)[len] == '=' || (*pp)[len] == '\0')) 1157 break; 1158 } 1159 1160 for (; *pp != NULL; pp++) 1161 *pp = pp[1]; 1162 } 1163 1164 #endif 1165 /* 1166 ** GETDTABLESIZE -- return number of file descriptors 1167 ** 1168 ** Only on non-BSD systems 1169 ** 1170 ** Parameters: 1171 ** none 1172 ** 1173 ** Returns: 1174 ** size of file descriptor table 1175 ** 1176 ** Side Effects: 1177 ** none 1178 */ 1179 1180 #ifdef SOLARIS 1181 # include <sys/resource.h> 1182 #endif 1183 1184 int 1185 getdtsize() 1186 { 1187 #ifdef RLIMIT_NOFILE 1188 struct rlimit rl; 1189 1190 if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) 1191 return rl.rlim_cur; 1192 #endif 1193 1194 # ifdef HASGETDTABLESIZE 1195 return getdtablesize(); 1196 # else 1197 # ifdef _SC_OPEN_MAX 1198 return sysconf(_SC_OPEN_MAX); 1199 # else 1200 return NOFILE; 1201 # endif 1202 # endif 1203 } 1204 /* 1205 ** UNAME -- get the UUCP name of this system. 1206 */ 1207 1208 #ifndef HASUNAME 1209 1210 int 1211 uname(name) 1212 struct utsname *name; 1213 { 1214 FILE *file; 1215 char *n; 1216 1217 name->nodename[0] = '\0'; 1218 1219 /* try /etc/whoami -- one line with the node name */ 1220 if ((file = fopen("/etc/whoami", "r")) != NULL) 1221 { 1222 (void) fgets(name->nodename, NODE_LENGTH + 1, file); 1223 (void) fclose(file); 1224 n = strchr(name->nodename, '\n'); 1225 if (n != NULL) 1226 *n = '\0'; 1227 if (name->nodename[0] != '\0') 1228 return (0); 1229 } 1230 1231 /* try /usr/include/whoami.h -- has a #define somewhere */ 1232 if ((file = fopen("/usr/include/whoami.h", "r")) != NULL) 1233 { 1234 char buf[MAXLINE]; 1235 1236 while (fgets(buf, MAXLINE, file) != NULL) 1237 if (sscanf(buf, "#define sysname \"%*[^\"]\"", 1238 NODE_LENGTH, name->nodename) > 0) 1239 break; 1240 (void) fclose(file); 1241 if (name->nodename[0] != '\0') 1242 return (0); 1243 } 1244 1245 #ifdef TRUST_POPEN 1246 /* 1247 ** Popen is known to have security holes. 1248 */ 1249 1250 /* try uuname -l to return local name */ 1251 if ((file = popen("uuname -l", "r")) != NULL) 1252 { 1253 (void) fgets(name, NODE_LENGTH + 1, file); 1254 (void) pclose(file); 1255 n = strchr(name, '\n'); 1256 if (n != NULL) 1257 *n = '\0'; 1258 if (name->nodename[0] != '\0') 1259 return (0); 1260 } 1261 #endif 1262 1263 return (-1); 1264 } 1265 #endif /* HASUNAME */ 1266 /* 1267 ** INITGROUPS -- initialize groups 1268 ** 1269 ** Stub implementation for System V style systems 1270 */ 1271 1272 #ifndef HASINITGROUPS 1273 1274 initgroups(name, basegid) 1275 char *name; 1276 int basegid; 1277 { 1278 return 0; 1279 } 1280 1281 #endif 1282 /* 1283 ** SETSID -- set session id (for non-POSIX systems) 1284 */ 1285 1286 #ifndef HASSETSID 1287 1288 pid_t 1289 setsid __P ((void)) 1290 { 1291 #ifdef TIOCNOTTY 1292 int fd; 1293 1294 fd = open("/dev/tty", O_RDWR, 0); 1295 if (fd >= 0) 1296 { 1297 (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0); 1298 (void) close(fd); 1299 } 1300 #endif /* TIOCNOTTY */ 1301 # ifdef SYS5SETPGRP 1302 return setpgrp(); 1303 # else 1304 return setpgid(0, getpid()); 1305 # endif 1306 } 1307 1308 #endif 1309 /* 1310 ** FSYNC -- dummy fsync 1311 */ 1312 1313 #ifdef NEEDFSYNC 1314 1315 fsync(fd) 1316 int fd; 1317 { 1318 # ifdef O_SYNC 1319 return fcntl(fd, F_SETFL, O_SYNC); 1320 # else 1321 /* nothing we can do */ 1322 return 0; 1323 # endif 1324 } 1325 1326 #endif 1327 /* 1328 ** DGUX_INET_ADDR -- inet_addr for DG/UX 1329 ** 1330 ** Data General DG/UX version of inet_addr returns a struct in_addr 1331 ** instead of a long. This patches things. Only needed on versions 1332 ** prior to 5.4.3. 1333 */ 1334 1335 #ifdef DGUX_5_4_2 1336 1337 #undef inet_addr 1338 1339 long 1340 dgux_inet_addr(host) 1341 char *host; 1342 { 1343 struct in_addr haddr; 1344 1345 haddr = inet_addr(host); 1346 return haddr.s_addr; 1347 } 1348 1349 #endif 1350 /* 1351 ** GETOPT -- for old systems or systems with bogus implementations 1352 */ 1353 1354 #ifdef NEEDGETOPT 1355 1356 /* 1357 * Copyright (c) 1985 Regents of the University of California. 1358 * All rights reserved. The Berkeley software License Agreement 1359 * specifies the terms and conditions for redistribution. 1360 */ 1361 1362 1363 /* 1364 ** this version hacked to add `atend' flag to allow state machine 1365 ** to reset if invoked by the program to scan args for a 2nd time 1366 */ 1367 1368 #if defined(LIBC_SCCS) && !defined(lint) 1369 static char sccsid[] = "@(#)getopt.c 4.3 (Berkeley) 3/9/86"; 1370 #endif /* LIBC_SCCS and not lint */ 1371 1372 #include <stdio.h> 1373 1374 /* 1375 * get option letter from argument vector 1376 */ 1377 #ifdef _CONVEX_SOURCE 1378 extern int optind, opterr; 1379 #else 1380 int opterr = 1; /* if error message should be printed */ 1381 int optind = 1; /* index into parent argv vector */ 1382 #endif 1383 int optopt; /* character checked for validity */ 1384 char *optarg; /* argument associated with option */ 1385 1386 #define BADCH (int)'?' 1387 #define EMSG "" 1388 #define tell(s) if (opterr) {fputs(*nargv,stderr);fputs(s,stderr); \ 1389 fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);} 1390 1391 getopt(nargc,nargv,ostr) 1392 int nargc; 1393 char *const *nargv; 1394 const char *ostr; 1395 { 1396 static char *place = EMSG; /* option letter processing */ 1397 static char atend = 0; 1398 register char *oli; /* option letter list index */ 1399 1400 if (atend) { 1401 atend = 0; 1402 place = EMSG; 1403 } 1404 if(!*place) { /* update scanning pointer */ 1405 if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) { 1406 atend++; 1407 return(EOF); 1408 } 1409 if (*place == '-') { /* found "--" */ 1410 ++optind; 1411 atend++; 1412 return(EOF); 1413 } 1414 } /* option letter okay? */ 1415 if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr,optopt))) { 1416 if (!*place) ++optind; 1417 tell(": illegal option -- "); 1418 } 1419 if (*++oli != ':') { /* don't need argument */ 1420 optarg = NULL; 1421 if (!*place) ++optind; 1422 } 1423 else { /* need an argument */ 1424 if (*place) optarg = place; /* no white space */ 1425 else if (nargc <= ++optind) { /* no arg */ 1426 place = EMSG; 1427 tell(": option requires an argument -- "); 1428 } 1429 else optarg = nargv[optind]; /* white space */ 1430 place = EMSG; 1431 ++optind; 1432 } 1433 return(optopt); /* dump back option letter */ 1434 } 1435 1436 #endif 1437 /* 1438 ** VFPRINTF, VSPRINTF -- for old 4.3 BSD systems missing a real version 1439 */ 1440 1441 #ifdef NEEDVPRINTF 1442 1443 #define MAXARG 16 1444 1445 vfprintf(fp, fmt, ap) 1446 FILE * fp; 1447 char * fmt; 1448 char ** ap; 1449 { 1450 char * bp[MAXARG]; 1451 int i = 0; 1452 1453 while (*ap && i < MAXARG) 1454 bp[i++] = *ap++; 1455 fprintf(fp, fmt, bp[0], bp[1], bp[2], bp[3], 1456 bp[4], bp[5], bp[6], bp[7], 1457 bp[8], bp[9], bp[10], bp[11], 1458 bp[12], bp[13], bp[14], bp[15]); 1459 } 1460 1461 vsprintf(s, fmt, ap) 1462 char * s; 1463 char * fmt; 1464 char ** ap; 1465 { 1466 char * bp[MAXARG]; 1467 int i = 0; 1468 1469 while (*ap && i < MAXARG) 1470 bp[i++] = *ap++; 1471 sprintf(s, fmt, bp[0], bp[1], bp[2], bp[3], 1472 bp[4], bp[5], bp[6], bp[7], 1473 bp[8], bp[9], bp[10], bp[11], 1474 bp[12], bp[13], bp[14], bp[15]); 1475 } 1476 1477 #endif 1478 /* 1479 ** USERSHELLOK -- tell if a user's shell is ok for unrestricted use 1480 ** 1481 ** Parameters: 1482 ** shell -- the user's shell from /etc/passwd 1483 ** 1484 ** Returns: 1485 ** TRUE -- if it is ok to use this for unrestricted access. 1486 ** FALSE -- if the shell is restricted. 1487 */ 1488 1489 #if !HASGETUSERSHELL 1490 1491 # ifndef _PATH_SHELLS 1492 # define _PATH_SHELLS "/etc/shells" 1493 # endif 1494 1495 char *DefaultUserShells[] = 1496 { 1497 "/bin/sh", 1498 "/usr/bin/sh", 1499 "/bin/csh", 1500 "/usr/bin/csh", 1501 #ifdef __hpux 1502 "/bin/rsh", 1503 "/bin/ksh", 1504 "/bin/rksh", 1505 "/bin/pam", 1506 "/usr/bin/keysh", 1507 "/bin/posix/sh", 1508 #endif 1509 NULL 1510 }; 1511 1512 #endif 1513 1514 #define WILDCARD_SHELL "/SENDMAIL/ANY/SHELL/" 1515 1516 bool 1517 usershellok(shell) 1518 char *shell; 1519 { 1520 #if HASGETUSERSHELL 1521 register char *p; 1522 extern char *getusershell(); 1523 1524 setusershell(); 1525 while ((p = getusershell()) != NULL) 1526 if (strcmp(p, shell) == 0 || strcmp(p, WILDCARD_SHELL) == 0) 1527 break; 1528 endusershell(); 1529 return p != NULL; 1530 #else 1531 register FILE *shellf; 1532 char buf[MAXLINE]; 1533 1534 shellf = fopen(_PATH_SHELLS, "r"); 1535 if (shellf == NULL) 1536 { 1537 /* no /etc/shells; see if it is one of the std shells */ 1538 char **d; 1539 1540 for (d = DefaultUserShells; *d != NULL; d++) 1541 { 1542 if (strcmp(shell, *d) == 0) 1543 return TRUE; 1544 } 1545 return FALSE; 1546 } 1547 1548 while (fgets(buf, sizeof buf, shellf) != NULL) 1549 { 1550 register char *p, *q; 1551 1552 p = buf; 1553 while (*p != '\0' && *p != '#' && *p != '/') 1554 p++; 1555 if (*p == '#' || *p == '\0') 1556 continue; 1557 q = p; 1558 while (*p != '\0' && *p != '#' && !isspace(*p)) 1559 p++; 1560 *p = '\0'; 1561 if (strcmp(shell, q) == 0 || strcmp(WILDCARD_SHELL, q) == 0) 1562 { 1563 fclose(shellf); 1564 return TRUE; 1565 } 1566 } 1567 fclose(shellf); 1568 return FALSE; 1569 #endif 1570 } 1571 /* 1572 ** FREESPACE -- see how much free space is on the queue filesystem 1573 ** 1574 ** Only implemented if you have statfs. 1575 ** 1576 ** Parameters: 1577 ** dir -- the directory in question. 1578 ** bsize -- a variable into which the filesystem 1579 ** block size is stored. 1580 ** 1581 ** Returns: 1582 ** The number of bytes free on the queue filesystem. 1583 ** -1 if the statfs call fails. 1584 ** 1585 ** Side effects: 1586 ** Puts the filesystem block size into bsize. 1587 */ 1588 1589 /* statfs types */ 1590 #define SFS_NONE 0 /* no statfs implementation */ 1591 #define SFS_USTAT 1 /* use ustat */ 1592 #define SFS_4ARGS 2 /* use four-argument statfs call */ 1593 #define SFS_VFS 3 /* use <sys/vfs.h> implementation */ 1594 #define SFS_MOUNT 4 /* use <sys/mount.h> implementation */ 1595 #define SFS_STATFS 5 /* use <sys/statfs.h> implementation */ 1596 #define SFS_STATVFS 6 /* use <sys/statvfs.h> implementation */ 1597 1598 #ifndef SFS_TYPE 1599 # define SFS_TYPE SFS_NONE 1600 #endif 1601 1602 #if SFS_TYPE == SFS_USTAT 1603 # include <ustat.h> 1604 #endif 1605 #if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS 1606 # include <sys/statfs.h> 1607 #endif 1608 #if SFS_TYPE == SFS_VFS 1609 # include <sys/vfs.h> 1610 #endif 1611 #if SFS_TYPE == SFS_MOUNT 1612 # include <sys/mount.h> 1613 #endif 1614 #if SFS_TYPE == SFS_STATVFS 1615 # include <sys/statvfs.h> 1616 #endif 1617 1618 long 1619 freespace(dir, bsize) 1620 char *dir; 1621 long *bsize; 1622 { 1623 #if SFS_TYPE != SFS_NONE 1624 # if SFS_TYPE == SFS_USTAT 1625 struct ustat fs; 1626 struct stat statbuf; 1627 # define FSBLOCKSIZE DEV_BSIZE 1628 # define f_bavail f_tfree 1629 # else 1630 # if defined(ultrix) 1631 struct fs_data fs; 1632 # define f_bavail fd_bfreen 1633 # define FSBLOCKSIZE fs.fd_bsize 1634 # else 1635 # if SFS_TYPE == SFS_STATVFS 1636 struct statvfs fs; 1637 # define FSBLOCKSIZE fs.f_bsize 1638 # else 1639 struct statfs fs; 1640 # define FSBLOCKSIZE fs.f_bsize 1641 # if defined(_SCO_unix_) || defined(IRIX) || defined(apollo) 1642 # define f_bavail f_bfree 1643 # endif 1644 # endif 1645 # endif 1646 # endif 1647 extern int errno; 1648 1649 # if SFS_TYPE == SFS_USTAT 1650 if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0) 1651 # else 1652 # if SFS_TYPE == SFS_4ARGS 1653 if (statfs(dir, &fs, sizeof fs, 0) == 0) 1654 # else 1655 # if SFS_TYPE == SFS_STATVFS 1656 if (statvfs(dir, &fs) == 0) 1657 # else 1658 # if defined(ultrix) 1659 if (statfs(dir, &fs) > 0) 1660 # else 1661 if (statfs(dir, &fs) == 0) 1662 # endif 1663 # endif 1664 # endif 1665 # endif 1666 { 1667 if (bsize != NULL) 1668 *bsize = FSBLOCKSIZE; 1669 return (fs.f_bavail); 1670 } 1671 #endif 1672 return (-1); 1673 } 1674 /* 1675 ** ENOUGHSPACE -- check to see if there is enough free space on the queue fs 1676 ** 1677 ** Only implemented if you have statfs. 1678 ** 1679 ** Parameters: 1680 ** msize -- the size to check against. If zero, we don't yet 1681 ** know how big the message will be, so just check for 1682 ** a "reasonable" amount. 1683 ** 1684 ** Returns: 1685 ** TRUE if there is enough space. 1686 ** FALSE otherwise. 1687 */ 1688 1689 bool 1690 enoughspace(msize) 1691 long msize; 1692 { 1693 long bfree, bsize; 1694 1695 if (MinBlocksFree <= 0 && msize <= 0) 1696 { 1697 if (tTd(4, 80)) 1698 printf("enoughspace: no threshold\n"); 1699 return TRUE; 1700 } 1701 1702 if ((bfree = freespace(QueueDir, &bsize)) >= 0) 1703 { 1704 if (tTd(4, 80)) 1705 printf("enoughspace: bavail=%ld, need=%ld\n", 1706 bfree, msize); 1707 1708 /* convert msize to block count */ 1709 msize = msize / bsize + 1; 1710 if (MinBlocksFree >= 0) 1711 msize += MinBlocksFree; 1712 1713 if (bfree < msize) 1714 { 1715 #ifdef LOG 1716 if (LogLevel > 0) 1717 syslog(LOG_ALERT, 1718 "%s: low on space (have %ld, %s needs %ld in %s)", 1719 CurEnv->e_id, bfree, 1720 CurHostName, msize, QueueDir); 1721 #endif 1722 return FALSE; 1723 } 1724 } 1725 else if (tTd(4, 80)) 1726 printf("enoughspace failure: min=%ld, need=%ld: %s\n", 1727 MinBlocksFree, msize, errstring(errno)); 1728 return TRUE; 1729 } 1730 /* 1731 ** TRANSIENTERROR -- tell if an error code indicates a transient failure 1732 ** 1733 ** This looks at an errno value and tells if this is likely to 1734 ** go away if retried later. 1735 ** 1736 ** Parameters: 1737 ** err -- the errno code to classify. 1738 ** 1739 ** Returns: 1740 ** TRUE if this is probably transient. 1741 ** FALSE otherwise. 1742 */ 1743 1744 bool 1745 transienterror(err) 1746 int err; 1747 { 1748 switch (err) 1749 { 1750 case EIO: /* I/O error */ 1751 case ENXIO: /* Device not configured */ 1752 case EAGAIN: /* Resource temporarily unavailable */ 1753 case ENOMEM: /* Cannot allocate memory */ 1754 case ENODEV: /* Operation not supported by device */ 1755 case ENFILE: /* Too many open files in system */ 1756 case EMFILE: /* Too many open files */ 1757 case ENOSPC: /* No space left on device */ 1758 #ifdef ETIMEDOUT 1759 case ETIMEDOUT: /* Connection timed out */ 1760 #endif 1761 #ifdef ESTALE 1762 case ESTALE: /* Stale NFS file handle */ 1763 #endif 1764 #ifdef ENETDOWN 1765 case ENETDOWN: /* Network is down */ 1766 #endif 1767 #ifdef ENETUNREACH 1768 case ENETUNREACH: /* Network is unreachable */ 1769 #endif 1770 #ifdef ENETRESET 1771 case ENETRESET: /* Network dropped connection on reset */ 1772 #endif 1773 #ifdef ECONNABORTED 1774 case ECONNABORTED: /* Software caused connection abort */ 1775 #endif 1776 #ifdef ECONNRESET 1777 case ECONNRESET: /* Connection reset by peer */ 1778 #endif 1779 #ifdef ENOBUFS 1780 case ENOBUFS: /* No buffer space available */ 1781 #endif 1782 #ifdef ESHUTDOWN 1783 case ESHUTDOWN: /* Can't send after socket shutdown */ 1784 #endif 1785 #ifdef ECONNREFUSED 1786 case ECONNREFUSED: /* Connection refused */ 1787 #endif 1788 #ifdef EHOSTDOWN 1789 case EHOSTDOWN: /* Host is down */ 1790 #endif 1791 #ifdef EHOSTUNREACH 1792 case EHOSTUNREACH: /* No route to host */ 1793 #endif 1794 #ifdef EDQUOT 1795 case EDQUOT: /* Disc quota exceeded */ 1796 #endif 1797 #ifdef EPROCLIM 1798 case EPROCLIM: /* Too many processes */ 1799 #endif 1800 #ifdef EUSERS 1801 case EUSERS: /* Too many users */ 1802 #endif 1803 #ifdef EDEADLK 1804 case EDEADLK: /* Resource deadlock avoided */ 1805 #endif 1806 #ifdef EISCONN 1807 case EISCONN: /* Socket already connected */ 1808 #endif 1809 #ifdef EINPROGRESS 1810 case EINPROGRESS: /* Operation now in progress */ 1811 #endif 1812 #ifdef EALREADY 1813 case EALREADY: /* Operation already in progress */ 1814 #endif 1815 #ifdef EADDRINUSE 1816 case EADDRINUSE: /* Address already in use */ 1817 #endif 1818 #ifdef EADDRNOTAVAIL 1819 case EADDRNOTAVAIL: /* Can't assign requested address */ 1820 #endif 1821 #ifdef ETXTBSY 1822 case ETXTBSY: /* (Apollo) file locked */ 1823 #endif 1824 #if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR)) 1825 case ENOSR: /* Out of streams resources */ 1826 #endif 1827 return TRUE; 1828 } 1829 1830 /* nope, must be permanent */ 1831 return FALSE; 1832 } 1833 /* 1834 ** LOCKFILE -- lock a file using flock or (shudder) fcntl locking 1835 ** 1836 ** Parameters: 1837 ** fd -- the file descriptor of the file. 1838 ** filename -- the file name (for error messages). 1839 ** ext -- the filename extension. 1840 ** type -- type of the lock. Bits can be: 1841 ** LOCK_EX -- exclusive lock. 1842 ** LOCK_NB -- non-blocking. 1843 ** 1844 ** Returns: 1845 ** TRUE if the lock was acquired. 1846 ** FALSE otherwise. 1847 */ 1848 1849 bool 1850 lockfile(fd, filename, ext, type) 1851 int fd; 1852 char *filename; 1853 char *ext; 1854 int type; 1855 { 1856 # if !HASFLOCK 1857 int action; 1858 struct flock lfd; 1859 1860 if (ext == NULL) 1861 ext = ""; 1862 1863 bzero(&lfd, sizeof lfd); 1864 if (bitset(LOCK_UN, type)) 1865 lfd.l_type = F_UNLCK; 1866 else if (bitset(LOCK_EX, type)) 1867 lfd.l_type = F_WRLCK; 1868 else 1869 lfd.l_type = F_RDLCK; 1870 1871 if (bitset(LOCK_NB, type)) 1872 action = F_SETLK; 1873 else 1874 action = F_SETLKW; 1875 1876 if (tTd(55, 60)) 1877 printf("lockfile(%s%s, action=%d, type=%d): ", 1878 filename, ext, action, lfd.l_type); 1879 1880 if (fcntl(fd, action, &lfd) >= 0) 1881 { 1882 if (tTd(55, 60)) 1883 printf("SUCCESS\n"); 1884 return TRUE; 1885 } 1886 1887 if (tTd(55, 60)) 1888 printf("(%s) ", errstring(errno)); 1889 1890 /* 1891 ** On SunOS, if you are testing using -oQ/tmp/mqueue or 1892 ** -oA/tmp/aliases or anything like that, and /tmp is mounted 1893 ** as type "tmp" (that is, served from swap space), the 1894 ** previous fcntl will fail with "Invalid argument" errors. 1895 ** Since this is fairly common during testing, we will assume 1896 ** that this indicates that the lock is successfully grabbed. 1897 */ 1898 1899 if (errno == EINVAL) 1900 { 1901 if (tTd(55, 60)) 1902 printf("SUCCESS\n"); 1903 return TRUE; 1904 } 1905 1906 if (!bitset(LOCK_NB, type) || (errno != EACCES && errno != EAGAIN)) 1907 { 1908 int omode = -1; 1909 # ifdef F_GETFL 1910 int oerrno = errno; 1911 1912 (void) fcntl(fd, F_GETFL, &omode); 1913 errno = oerrno; 1914 # endif 1915 syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", 1916 filename, ext, fd, type, omode, geteuid()); 1917 } 1918 # else 1919 if (ext == NULL) 1920 ext = ""; 1921 1922 if (tTd(55, 60)) 1923 printf("lockfile(%s%s, type=%o): ", filename, ext, type); 1924 1925 if (flock(fd, type) >= 0) 1926 { 1927 if (tTd(55, 60)) 1928 printf("SUCCESS\n"); 1929 return TRUE; 1930 } 1931 1932 if (tTd(55, 60)) 1933 printf("(%s) ", errstring(errno)); 1934 1935 if (!bitset(LOCK_NB, type) || errno != EWOULDBLOCK) 1936 { 1937 int omode = -1; 1938 # ifdef F_GETFL 1939 int oerrno = errno; 1940 1941 (void) fcntl(fd, F_GETFL, &omode); 1942 errno = oerrno; 1943 # endif 1944 syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", 1945 filename, ext, fd, type, omode, geteuid()); 1946 } 1947 # endif 1948 if (tTd(55, 60)) 1949 printf("FAIL\n"); 1950 return FALSE; 1951 } 1952 /* 1953 ** CHOWNSAFE -- tell if chown is "safe" (executable only by root) 1954 ** 1955 ** Parameters: 1956 ** fd -- the file descriptor to check. 1957 ** 1958 ** Returns: 1959 ** TRUE -- if only root can chown the file to an arbitrary 1960 ** user. 1961 ** FALSE -- if an arbitrary user can give away a file. 1962 */ 1963 1964 bool 1965 chownsafe(fd) 1966 int fd; 1967 { 1968 #ifdef __hpux 1969 char *s; 1970 int tfd; 1971 uid_t o_uid, o_euid; 1972 gid_t o_gid, o_egid; 1973 bool rval; 1974 struct stat stbuf; 1975 1976 o_uid = getuid(); 1977 o_euid = geteuid(); 1978 o_gid = getgid(); 1979 o_egid = getegid(); 1980 fstat(fd, &stbuf); 1981 setresuid(stbuf.st_uid, stbuf.st_uid, -1); 1982 setresgid(stbuf.st_gid, stbuf.st_gid, -1); 1983 s = tmpnam(NULL); 1984 tfd = open(s, O_RDONLY|O_CREAT, 0600); 1985 rval = fchown(tfd, DefUid, DefGid) != 0; 1986 close(tfd); 1987 unlink(s); 1988 setreuid(o_uid, o_euid); 1989 setresgid(o_gid, o_egid, -1); 1990 return rval; 1991 #else 1992 # ifdef _POSIX_CHOWN_RESTRICTED 1993 # if _POSIX_CHOWN_RESTRICTED == -1 1994 return FALSE; 1995 # else 1996 return TRUE; 1997 # endif 1998 # else 1999 # ifdef _PC_CHOWN_RESTRICTED 2000 return fpathconf(fd, _PC_CHOWN_RESTRICTED) > 0; 2001 # else 2002 # ifdef BSD 2003 return TRUE; 2004 # else 2005 return FALSE; 2006 # endif 2007 # endif 2008 # endif 2009 #endif 2010 } 2011 /* 2012 ** RESETLIMITS -- reset system controlled resource limits 2013 ** 2014 ** This is to avoid denial-of-service attacks 2015 ** 2016 ** Parameters: 2017 ** none 2018 ** 2019 ** Returns: 2020 ** none 2021 */ 2022 2023 #if HASSETRLIMIT 2024 # include <sys/resource.h> 2025 #endif 2026 2027 resetlimits() 2028 { 2029 #if HASSETRLIMIT 2030 struct rlimit lim; 2031 2032 lim.rlim_cur = lim.rlim_max = RLIM_INFINITY; 2033 (void) setrlimit(RLIMIT_CPU, &lim); 2034 (void) setrlimit(RLIMIT_FSIZE, &lim); 2035 #else 2036 # if HASULIMIT 2037 (void) ulimit(2, 0x3fffff); 2038 # endif 2039 #endif 2040 } 2041 /* 2042 ** GETCFNAME -- return the name of the .cf file. 2043 ** 2044 ** Some systems (e.g., NeXT) determine this dynamically. 2045 */ 2046 2047 char * 2048 getcfname() 2049 { 2050 if (ConfFile != NULL) 2051 return ConfFile; 2052 #ifdef NETINFO 2053 { 2054 extern char *ni_propval(); 2055 char *cflocation; 2056 2057 cflocation = ni_propval("/locations/sendmail", "sendmail.cf"); 2058 if (cflocation != NULL) 2059 return cflocation; 2060 } 2061 #endif 2062 return _PATH_SENDMAILCF; 2063 } 2064 /* 2065 ** SETVENDOR -- process vendor code from V configuration line 2066 ** 2067 ** Parameters: 2068 ** vendor -- string representation of vendor. 2069 ** 2070 ** Returns: 2071 ** TRUE -- if ok. 2072 ** FALSE -- if vendor code could not be processed. 2073 ** 2074 ** Side Effects: 2075 ** It is reasonable to set mode flags here to tweak 2076 ** processing in other parts of the code if necessary. 2077 ** For example, if you are a vendor that uses $%y to 2078 ** indicate YP lookups, you could enable that here. 2079 */ 2080 2081 bool 2082 setvendor(vendor) 2083 char *vendor; 2084 { 2085 if (strcasecmp(vendor, "Berkeley") == 0) 2086 return TRUE; 2087 2088 /* add vendor extensions here */ 2089 2090 return FALSE; 2091 } 2092 /* 2093 ** STRTOL -- convert string to long integer 2094 ** 2095 ** For systems that don't have it in the C library. 2096 ** 2097 ** This is taken verbatim from the 4.4-Lite C library. 2098 */ 2099 2100 #ifdef NEEDSTRTOL 2101 2102 #if defined(LIBC_SCCS) && !defined(lint) 2103 static char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93"; 2104 #endif /* LIBC_SCCS and not lint */ 2105 2106 #include <limits.h> 2107 2108 /* 2109 * Convert a string to a long integer. 2110 * 2111 * Ignores `locale' stuff. Assumes that the upper and lower case 2112 * alphabets and digits are each contiguous. 2113 */ 2114 2115 long 2116 strtol(nptr, endptr, base) 2117 const char *nptr; 2118 char **endptr; 2119 register int base; 2120 { 2121 register const char *s = nptr; 2122 register unsigned long acc; 2123 register int c; 2124 register unsigned long cutoff; 2125 register int neg = 0, any, cutlim; 2126 2127 /* 2128 * Skip white space and pick up leading +/- sign if any. 2129 * If base is 0, allow 0x for hex and 0 for octal, else 2130 * assume decimal; if base is already 16, allow 0x. 2131 */ 2132 do { 2133 c = *s++; 2134 } while (isspace(c)); 2135 if (c == '-') { 2136 neg = 1; 2137 c = *s++; 2138 } else if (c == '+') 2139 c = *s++; 2140 if ((base == 0 || base == 16) && 2141 c == '0' && (*s == 'x' || *s == 'X')) { 2142 c = s[1]; 2143 s += 2; 2144 base = 16; 2145 } 2146 if (base == 0) 2147 base = c == '0' ? 8 : 10; 2148 2149 /* 2150 * Compute the cutoff value between legal numbers and illegal 2151 * numbers. That is the largest legal value, divided by the 2152 * base. An input number that is greater than this value, if 2153 * followed by a legal input character, is too big. One that 2154 * is equal to this value may be valid or not; the limit 2155 * between valid and invalid numbers is then based on the last 2156 * digit. For instance, if the range for longs is 2157 * [-2147483648..2147483647] and the input base is 10, 2158 * cutoff will be set to 214748364 and cutlim to either 2159 * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated 2160 * a value > 214748364, or equal but the next digit is > 7 (or 8), 2161 * the number is too big, and we will return a range error. 2162 * 2163 * Set any if any `digits' consumed; make it negative to indicate 2164 * overflow. 2165 */ 2166 cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX; 2167 cutlim = cutoff % (unsigned long)base; 2168 cutoff /= (unsigned long)base; 2169 for (acc = 0, any = 0;; c = *s++) { 2170 if (isdigit(c)) 2171 c -= '0'; 2172 else if (isalpha(c)) 2173 c -= isupper(c) ? 'A' - 10 : 'a' - 10; 2174 else 2175 break; 2176 if (c >= base) 2177 break; 2178 if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim) 2179 any = -1; 2180 else { 2181 any = 1; 2182 acc *= base; 2183 acc += c; 2184 } 2185 } 2186 if (any < 0) { 2187 acc = neg ? LONG_MIN : LONG_MAX; 2188 errno = ERANGE; 2189 } else if (neg) 2190 acc = -acc; 2191 if (endptr != 0) 2192 *endptr = (char *)(any ? s - 1 : nptr); 2193 return (acc); 2194 } 2195 2196 #endif 2197 /* 2198 ** SOLARIS_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX 2199 ** 2200 ** Solaris versions prior through 2.3 don't properly deliver a 2201 ** canonical h_name field. This tries to work around it. 2202 */ 2203 2204 #ifdef SOLARIS 2205 2206 struct hostent * 2207 solaris_gethostbyname(name) 2208 const char *name; 2209 { 2210 # ifdef SOLARIS_2_3 2211 static struct hostent hp; 2212 static char buf[1000]; 2213 extern struct hostent *_switch_gethostbyname_r(); 2214 2215 return _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno); 2216 # else 2217 extern struct hostent *__switch_gethostbyname(); 2218 2219 return __switch_gethostbyname(name); 2220 # endif 2221 } 2222 2223 struct hostent * 2224 solaris_gethostbyaddr(addr, len, type) 2225 const char *addr; 2226 int len; 2227 int type; 2228 { 2229 # ifdef SOLARIS_2_3 2230 static struct hostent hp; 2231 static char buf[1000]; 2232 extern struct hostent *_switch_gethostbyaddr_r(); 2233 2234 return _switch_gethostbyaddr_r(addr, len, type, &hp, buf, sizeof(buf), &h_errno); 2235 # else 2236 extern struct hostent *__switch_gethostbyaddr(); 2237 2238 return __switch_gethostbyaddr(addr, len, type); 2239 # endif 2240 } 2241 2242 #endif 2243 /* 2244 ** NI_PROPVAL -- netinfo property value lookup routine 2245 ** 2246 ** Parameters: 2247 ** directory -- the Netinfo directory name. 2248 ** propname -- the Netinfo property name. 2249 ** 2250 ** Returns: 2251 ** NULL -- if: 2252 ** 1. the directory is not found 2253 ** 2. the property name is not found 2254 ** 3. the property contains multiple values 2255 ** 4. some error occured 2256 ** else -- the location of the config file. 2257 ** 2258 ** Notes: 2259 ** Caller should free the return value of ni_proval 2260 */ 2261 2262 #ifdef NETINFO 2263 2264 # include <netinfo/ni.h> 2265 2266 # define LOCAL_NETINFO_DOMAIN "." 2267 # define PARENT_NETINFO_DOMAIN ".." 2268 # define MAX_NI_LEVELS 256 2269 2270 char * 2271 ni_propval(directory, propname) 2272 char *directory; 2273 char *propname; 2274 { 2275 char *propval = NULL; 2276 int i; 2277 void *ni = NULL; 2278 void *lastni = NULL; 2279 ni_status nis; 2280 ni_id nid; 2281 ni_namelist ninl; 2282 2283 /* 2284 ** If the passed directory and property name are found 2285 ** in one of netinfo domains we need to search (starting 2286 ** from the local domain moving all the way back to the 2287 ** root domain) set propval to the property's value 2288 ** and return it. 2289 */ 2290 2291 for (i = 0; i < MAX_NI_LEVELS; ++i) 2292 { 2293 if (i == 0) 2294 { 2295 nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni); 2296 } 2297 else 2298 { 2299 if (lastni != NULL) 2300 ni_free(lastni); 2301 lastni = ni; 2302 nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni); 2303 } 2304 2305 /* 2306 ** Don't bother if we didn't get a handle on a 2307 ** proper domain. This is not necessarily an error. 2308 ** We would get a positive ni_status if, for instance 2309 ** we never found the directory or property and tried 2310 ** to open the parent of the root domain! 2311 */ 2312 2313 if (nis != 0) 2314 break; 2315 2316 /* 2317 ** Find the path to the server information. 2318 */ 2319 2320 if (ni_pathsearch(ni, &nid, directory) != 0) 2321 continue; 2322 2323 /* 2324 ** Find "host" information. 2325 */ 2326 2327 if (ni_lookupprop(ni, &nid, propname, &ninl) != 0) 2328 continue; 2329 2330 /* 2331 ** If there's only one name in 2332 ** the list, assume we've got 2333 ** what we want. 2334 */ 2335 2336 if (ninl.ni_namelist_len == 1) 2337 { 2338 propval = ni_name_dup(ninl.ni_namelist_val[0]); 2339 break; 2340 } 2341 } 2342 2343 /* 2344 ** Clean up. 2345 */ 2346 2347 if (ni != NULL) 2348 ni_free(ni); 2349 if (lastni != NULL && ni != lastni) 2350 ni_free(lastni); 2351 2352 return propval; 2353 } 2354 2355 #endif /* NETINFO */ 2356 /* 2357 ** HARD_SYSLOG -- call syslog repeatedly until it works 2358 ** 2359 ** Needed on HP-UX, which apparently doesn't guarantee that 2360 ** syslog succeeds during interrupt handlers. 2361 */ 2362 2363 #ifdef __hpux 2364 2365 # define MAXSYSLOGTRIES 100 2366 # undef syslog 2367 2368 # ifdef __STDC__ 2369 hard_syslog(int pri, char *msg, ...) 2370 # else 2371 hard_syslog(pri, msg, va_alist) 2372 int pri; 2373 char *msg; 2374 va_dcl 2375 # endif 2376 { 2377 int i; 2378 char buf[SYSLOG_BUFSIZE * 2]; 2379 VA_LOCAL_DECL; 2380 2381 VA_START(msg); 2382 vsprintf(buf, msg, ap); 2383 VA_END; 2384 2385 for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, "%s", buf) < 0; ) 2386 continue; 2387 } 2388 2389 #endif 2390