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