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