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