xutil.c revision 82794
1/* 2 * Copyright (c) 1997-2001 Erez Zadok 3 * Copyright (c) 1990 Jan-Simon Pendry 4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 5 * Copyright (c) 1990 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Jan-Simon Pendry at Imperial College, London. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgment: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * %W% (Berkeley) %G% 40 * 41 * $Id: xutil.c,v 1.11.2.6 2001/01/10 03:23:41 ezk Exp $ 42 * 43 */ 44 45#ifdef HAVE_CONFIG_H 46# include <config.h> 47#endif /* HAVE_CONFIG_H */ 48#include <am_defs.h> 49#include <amu.h> 50 51/* 52 * Logfp is the default logging device, and is initialized to stderr by 53 * default in dplog/plog below, and in 54 * amd/amfs_program.c:amfs_program_exec(). 55 */ 56FILE *logfp = NULL; 57 58static char *am_progname = "unknown"; /* "amd" */ 59static char am_hostname[MAXHOSTNAMELEN + 1] = "unknown"; /* Hostname */ 60pid_t am_mypid = -1; /* process ID */ 61serv_state amd_state; /* amd's state */ 62int foreground = 1; /* 1 == this is the top-level server */ 63#ifdef DEBUG 64int debug_flags = 0; 65#endif /* DEBUG */ 66 67#ifdef HAVE_SYSLOG 68int syslogging; 69#endif /* HAVE_SYSLOG */ 70int xlog_level = XLOG_ALL & ~XLOG_MAP & ~XLOG_STATS; 71int xlog_level_init = ~0; 72static int amd_program_number = AMQ_PROGRAM; 73 74time_t clock_valid = 0; 75time_t xclock_valid = 0; 76 77#ifdef DEBUG_MEM 78static int mem_bytes; 79static int orig_mem_bytes; 80#endif /* DEBUG_MEM */ 81 82/* forward definitions */ 83static void real_plog(int lvl, const char *fmt, va_list vargs) 84 __attribute__((__format__(__printf__, 2, 0))); 85/* for GCC format string auditing */ 86static const char *expand_error(const char *f, char *e, int maxlen) 87 __attribute__((__format_arg__(1))); 88 89#ifdef DEBUG 90/* 91 * List of debug options. 92 */ 93struct opt_tab dbg_opt[] = 94{ 95 {"all", D_ALL}, /* All */ 96 {"amq", D_AMQ}, /* Register for AMQ program */ 97 {"daemon", D_DAEMON}, /* Enter daemon mode */ 98 {"fork", D_FORK}, /* Fork server (nofork = don't fork) */ 99 {"full", D_FULL}, /* Program trace */ 100#ifdef HAVE_CLOCK_GETTIME 101 {"hrtime", D_HRTIME}, /* Print high resolution time stamps */ 102#endif /* HAVE_CLOCK_GETTIME */ 103 /* info service specific debugging (hesiod, nis, etc) */ 104 {"info", D_INFO}, 105# ifdef DEBUG_MEM 106 {"mem", D_MEM}, /* Trace memory allocations */ 107# endif /* DEBUG_MEM */ 108 {"mtab", D_MTAB}, /* Use local mtab file */ 109 {"readdir", D_READDIR}, /* check on browsable_dirs progress */ 110 {"str", D_STR}, /* Debug string munging */ 111 {"test", D_TEST}, /* Full debug - but no daemon */ 112 {"trace", D_TRACE}, /* Protocol trace */ 113 {"xdrtrace", D_XDRTRACE}, /* Trace xdr routines */ 114 {0, 0} 115}; 116#endif /* DEBUG */ 117 118/* 119 * List of log options 120 */ 121struct opt_tab xlog_opt[] = 122{ 123 {"all", XLOG_ALL}, /* All messages */ 124#ifdef DEBUG 125 {"debug", XLOG_DEBUG}, /* Debug messages */ 126#endif /* DEBUG */ /* DEBUG */ 127 {"error", XLOG_ERROR}, /* Non-fatal system errors */ 128 {"fatal", XLOG_FATAL}, /* Fatal errors */ 129 {"info", XLOG_INFO}, /* Information */ 130 {"map", XLOG_MAP}, /* Map errors */ 131 {"stats", XLOG_STATS}, /* Additional statistical information */ 132 {"user", XLOG_USER}, /* Non-fatal user errors */ 133 {"warn", XLOG_WARNING}, /* Warnings */ 134 {"warning", XLOG_WARNING}, /* Warnings */ 135 {0, 0} 136}; 137 138 139void 140am_set_progname(char *pn) 141{ 142 am_progname = pn; 143} 144 145 146const char * 147am_get_progname(void) 148{ 149 return am_progname; 150} 151 152 153void 154am_set_hostname(char *hn) 155{ 156 strncpy(am_hostname, hn, MAXHOSTNAMELEN); 157 am_hostname[MAXHOSTNAMELEN] = '\0'; 158} 159 160 161const char * 162am_get_hostname(void) 163{ 164 return am_hostname; 165} 166 167 168pid_t 169am_set_mypid(void) 170{ 171 am_mypid = getpid(); 172 return am_mypid; 173} 174 175 176voidp 177xmalloc(int len) 178{ 179 voidp p; 180 int retries = 600; 181 182 /* 183 * Avoid malloc's which return NULL for malloc(0) 184 */ 185 if (len == 0) 186 len = 1; 187 188 do { 189 p = (voidp) malloc((unsigned) len); 190 if (p) { 191#if defined(DEBUG) && defined(DEBUG_MEM) 192 amuDebug(D_MEM) 193 plog(XLOG_DEBUG, "Allocated size %d; block %#x", len, p); 194#endif /* defined(DEBUG) && defined(DEBUG_MEM) */ 195 return p; 196 } 197 if (retries > 0) { 198 plog(XLOG_ERROR, "Retrying memory allocation"); 199 sleep(1); 200 } 201 } while (--retries); 202 203 plog(XLOG_FATAL, "Out of memory"); 204 going_down(1); 205 206 abort(); 207 208 return 0; 209} 210 211 212/* like xmalloc, but zeros out the bytes */ 213voidp 214xzalloc(int len) 215{ 216 voidp p = xmalloc(len); 217 218 if (p) 219 memset(p, 0, len); 220 return p; 221} 222 223 224voidp 225xrealloc(voidp ptr, int len) 226{ 227#if defined(DEBUG) && defined(DEBUG_MEM) 228 amuDebug(D_MEM) plog(XLOG_DEBUG, "Reallocated size %d; block %#x", len, ptr); 229#endif /* defined(DEBUG) && defined(DEBUG_MEM) */ 230 231 if (len == 0) 232 len = 1; 233 234 if (ptr) 235 ptr = (voidp) realloc(ptr, (unsigned) len); 236 else 237 ptr = (voidp) xmalloc((unsigned) len); 238 239 if (!ptr) { 240 plog(XLOG_FATAL, "Out of memory in realloc"); 241 going_down(1); 242 abort(); 243 } 244 return ptr; 245} 246 247 248#if defined(DEBUG) && defined(DEBUG_MEM) 249void 250dxfree(char *file, int line, voidp ptr) 251{ 252 amuDebug(D_MEM) 253 plog(XLOG_DEBUG, "Free in %s:%d: block %#x", file, line, ptr); 254 /* this is the only place that must NOT use XFREE()!!! */ 255 free(ptr); 256 ptr = NULL; /* paranoid */ 257} 258#endif /* defined(DEBUG) && defined(DEBUG_MEM) */ 259 260 261#ifdef DEBUG_MEM 262static void 263checkup_mem(void) 264{ 265 struct mallinfo mi = mallinfo(); 266 u_long uordbytes = mi.uordblks * 4096; 267 268 if (mem_bytes != uordbytes) { 269 if (orig_mem_bytes == 0) 270 mem_bytes = orig_mem_bytes = uordbytes; 271 else { 272 fprintf(logfp, "%s[%ld]: ", am_get_progname(), (long) am_mypid); 273 if (mem_bytes < uordbytes) { 274 fprintf(logfp, "ALLOC: %ld bytes", uordbytes - mem_bytes); 275 } else { 276 fprintf(logfp, "FREE: %ld bytes", mem_bytes - uordbytes); 277 } 278 mem_bytes = uordbytes; 279 fprintf(logfp, ", making %d missing\n", mem_bytes - orig_mem_bytes); 280 } 281 } 282 malloc_verify(); 283} 284#endif /* DEBUG_MEM */ 285 286 287/* 288 * Take a log format string and expand occurrences of %m 289 * with the current error code taken from errno. Make sure 290 * 'e' never gets longer than maxlen characters. 291 */ 292static const char * 293expand_error(const char *f, char *e, int maxlen) 294{ 295#ifndef HAVE_STRERROR 296 /* 297 * XXX: we are assuming that if a system doesn't has strerror, 298 * then it has sys_nerr. If this assumption turns out to be wrong on 299 * some systems, we'll have to write a separate test to detect if 300 * a system has sys_nerr. -Erez 301 */ 302 extern int sys_nerr; 303#endif /* not HAVE_STRERROR */ 304 const char *p; 305 char *q; 306 int error = errno; 307 int len = 0; 308 309 for (p = f, q = e; (*q = *p) && len < maxlen; len++, q++, p++) { 310 if (p[0] == '%' && p[1] == 'm') { 311 const char *errstr; 312#ifdef HAVE_STRERROR 313 if (error < 0) 314#else /* not HAVE_STRERROR */ 315 if (error < 0 || error >= sys_nerr) 316#endif /* not HAVE_STRERROR */ 317 errstr = NULL; 318 else 319#ifdef HAVE_STRERROR 320 errstr = strerror(error); 321#else /* not HAVE_STRERROR */ 322 errstr = sys_errlist[error]; 323#endif /* not HAVE_STRERROR */ 324 if (errstr) 325 strcpy(q, errstr); 326 else 327 sprintf(q, "Error %d", error); 328 len += strlen(q) - 1; 329 q += strlen(q) - 1; 330 p++; 331 } 332 } 333 e[maxlen-1] = '\0'; /* null terminate, to be sure */ 334 return e; 335} 336 337 338/* 339 * Output the time of day and hostname to the logfile 340 */ 341static void 342show_time_host_and_name(int lvl) 343{ 344 static time_t last_t = 0; 345 static char *last_ctime = 0; 346 time_t t; 347#ifdef HAVE_CLOCK_GETTIME 348 struct timespec ts; 349#endif /* HAVE_CLOCK_GETTIME */ 350 char nsecs[11] = ""; /* '.' + 9 digits + '\0' */ 351 char *sev; 352 353#ifdef HAVE_CLOCK_GETTIME 354 /* 355 * Some systems (AIX 4.3) seem to implement clock_gettime() as stub 356 * returning ENOSYS. 357 */ 358 if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { 359 t = ts.tv_sec; 360#ifdef DEBUG 361 amuDebug(D_HRTIME) 362 sprintf(nsecs, ".%09ld", ts.tv_nsec); 363#endif /* DEBUG */ 364 } 365 else 366#endif /* HAVE_CLOCK_GETTIME */ 367 t = clocktime(); 368 369 if (t != last_t) { 370 last_ctime = ctime(&t); 371 last_t = t; 372 } 373 374 switch (lvl) { 375 case XLOG_FATAL: 376 sev = "fatal:"; 377 break; 378 case XLOG_ERROR: 379 sev = "error:"; 380 break; 381 case XLOG_USER: 382 sev = "user: "; 383 break; 384 case XLOG_WARNING: 385 sev = "warn: "; 386 break; 387 case XLOG_INFO: 388 sev = "info: "; 389 break; 390 case XLOG_DEBUG: 391 sev = "debug:"; 392 break; 393 case XLOG_MAP: 394 sev = "map: "; 395 break; 396 case XLOG_STATS: 397 sev = "stats:"; 398 break; 399 default: 400 sev = "hmm: "; 401 break; 402 } 403 fprintf(logfp, "%15.15s%s %s %s[%ld]/%s ", 404 last_ctime + 4, nsecs, am_get_hostname(), 405 am_get_progname(), 406 (long) am_mypid, 407 sev); 408} 409 410 411#ifdef DEBUG 412/* 413 * Switch on/off debug options 414 */ 415int 416debug_option(char *opt) 417{ 418 return cmdoption(opt, dbg_opt, &debug_flags); 419} 420 421 422void 423dplog(const char *fmt, ...) 424{ 425 va_list ap; 426 427 if (!logfp) 428 logfp = stderr; /* initialize before possible first use */ 429 430 va_start(ap, fmt); 431 real_plog(XLOG_DEBUG, fmt, ap); 432 va_end(ap); 433} 434#endif /* DEBUG */ 435 436 437void 438plog(int lvl, const char *fmt, ...) 439{ 440 va_list ap; 441 442 if (!logfp) 443 logfp = stderr; /* initialize before possible first use */ 444 445 va_start(ap, fmt); 446 real_plog(lvl, fmt, ap); 447 va_end(ap); 448} 449 450 451static void 452real_plog(int lvl, const char *fmt, va_list vargs) 453{ 454 char msg[1024]; 455 char efmt[1024]; 456 char *ptr = msg; 457 static char last_msg[1024]; 458 static int last_count = 0, last_lvl = 0; 459 460 if (!(xlog_level & lvl)) 461 return; 462 463#ifdef DEBUG_MEM 464 checkup_mem(); 465#endif /* DEBUG_MEM */ 466 467#ifdef HAVE_VSNPRINTF 468 /* 469 * XXX: ptr is 1024 bytes long, but we may write to ptr[strlen(ptr) + 2] 470 * (to add an '\n', see code below) so we have to limit the string copy 471 * to 1023 (including the '\0'). 472 */ 473 vsnprintf(ptr, 1023, expand_error(fmt, efmt, 1024), vargs); 474 msg[1022] = '\0'; /* null terminate, to be sure */ 475#else /* not HAVE_VSNPRINTF */ 476 /* 477 * XXX: ptr is 1024 bytes long. It is possible to write into it 478 * more than 1024 bytes, if efmt is already large, and vargs expand 479 * as well. This is not as safe as using vsnprintf(). 480 */ 481 vsprintf(ptr, expand_error(fmt, efmt, 1023), vargs); 482 msg[1023] = '\0'; /* null terminate, to be sure */ 483#endif /* not HAVE_VSNPRINTF */ 484 485 ptr += strlen(ptr); 486 if (ptr[-1] == '\n') 487 *--ptr = '\0'; 488 489#ifdef HAVE_SYSLOG 490 if (syslogging) { 491 switch (lvl) { /* from mike <mcooper@usc.edu> */ 492 case XLOG_FATAL: 493 lvl = LOG_CRIT; 494 break; 495 case XLOG_ERROR: 496 lvl = LOG_ERR; 497 break; 498 case XLOG_USER: 499 lvl = LOG_WARNING; 500 break; 501 case XLOG_WARNING: 502 lvl = LOG_WARNING; 503 break; 504 case XLOG_INFO: 505 lvl = LOG_INFO; 506 break; 507 case XLOG_DEBUG: 508 lvl = LOG_DEBUG; 509 break; 510 case XLOG_MAP: 511 lvl = LOG_DEBUG; 512 break; 513 case XLOG_STATS: 514 lvl = LOG_INFO; 515 break; 516 default: 517 lvl = LOG_ERR; 518 break; 519 } 520 syslog(lvl, "%s", msg); 521 return; 522 } 523#endif /* HAVE_SYSLOG */ 524 525 *ptr++ = '\n'; 526 *ptr = '\0'; 527 528 /* 529 * mimic syslog behavior: only write repeated strings if they differ 530 */ 531 switch (last_count) { 532 case 0: /* never printed at all */ 533 last_count = 1; 534 strncpy(last_msg, msg, 1024); 535 last_lvl = lvl; 536 show_time_host_and_name(lvl); /* mimic syslog header */ 537 fwrite(msg, ptr - msg, 1, logfp); 538 fflush(logfp); 539 break; 540 541 case 1: /* item printed once, if same, don't repeat */ 542 if (STREQ(last_msg, msg)) { 543 last_count++; 544 } else { /* last msg printed once, new one differs */ 545 /* last_count remains at 1 */ 546 strncpy(last_msg, msg, 1024); 547 last_lvl = lvl; 548 show_time_host_and_name(lvl); /* mimic syslog header */ 549 fwrite(msg, ptr - msg, 1, logfp); 550 fflush(logfp); 551 } 552 break; 553 554 case 100: 555 /* 556 * Don't allow repetitions longer than 100, so you can see when something 557 * cycles like crazy. 558 */ 559 show_time_host_and_name(last_lvl); 560 sprintf(last_msg, "last message repeated %d times\n", last_count); 561 fwrite(last_msg, strlen(last_msg), 1, logfp); 562 fflush(logfp); 563 last_count = 0; /* start from scratch */ 564 break; 565 566 default: /* item repeated multiple times */ 567 if (STREQ(last_msg, msg)) { 568 last_count++; 569 } else { /* last msg repeated+skipped, new one differs */ 570 show_time_host_and_name(last_lvl); 571 sprintf(last_msg, "last message repeated %d times\n", last_count); 572 fwrite(last_msg, strlen(last_msg), 1, logfp); 573 strncpy(last_msg, msg, 1024); 574 last_count = 1; 575 last_lvl = lvl; 576 show_time_host_and_name(lvl); /* mimic syslog header */ 577 fwrite(msg, ptr - msg, 1, logfp); 578 fflush(logfp); 579 } 580 break; 581 } 582 583} 584 585 586/* 587 * Display current debug options 588 */ 589void 590show_opts(int ch, struct opt_tab *opts) 591{ 592 int i; 593 int s = '{'; 594 595 fprintf(stderr, "\t[-%c {no}", ch); 596 for (i = 0; opts[i].opt; i++) { 597 fprintf(stderr, "%c%s", s, opts[i].opt); 598 s = ','; 599 } 600 fputs("}]\n", stderr); 601} 602 603 604int 605cmdoption(char *s, struct opt_tab *optb, int *flags) 606{ 607 char *p = s; 608 int errs = 0; 609 610 while (p && *p) { 611 int neg; 612 char *opt; 613 struct opt_tab *dp, *dpn = 0; 614 615 s = p; 616 p = strchr(p, ','); 617 if (p) 618 *p = '\0'; 619 620 /* check for "no" prefix to options */ 621 if (s[0] == 'n' && s[1] == 'o') { 622 opt = s + 2; 623 neg = 1; 624 } else { 625 opt = s; 626 neg = 0; 627 } 628 629 /* 630 * Scan the array of debug options to find the 631 * corresponding flag value. If it is found 632 * then set (or clear) the flag (depending on 633 * whether the option was prefixed with "no"). 634 */ 635 for (dp = optb; dp->opt; dp++) { 636 if (STREQ(opt, dp->opt)) 637 break; 638 if (opt != s && !dpn && STREQ(s, dp->opt)) 639 dpn = dp; 640 } 641 642 if (dp->opt || dpn) { 643 if (!dp->opt) { 644 dp = dpn; 645 neg = !neg; 646 } 647 if (neg) 648 *flags &= ~dp->flag; 649 else 650 *flags |= dp->flag; 651 } else { 652 /* 653 * This will log to stderr when parsing the command line 654 * since any -l option will not yet have taken effect. 655 */ 656 plog(XLOG_USER, "option \"%s\" not recognized", s); 657 errs++; 658 } 659 660 /* 661 * Put the comma back 662 */ 663 if (p) 664 *p++ = ','; 665 } 666 667 return errs; 668} 669 670 671/* 672 * Switch on/off logging options 673 */ 674int 675switch_option(char *opt) 676{ 677 int xl = xlog_level; 678 int rc = cmdoption(opt, xlog_opt, &xl); 679 680 if (rc) { 681 rc = EINVAL; 682 } else { 683 /* 684 * Keep track of initial log level, and 685 * don't allow options to be turned off. 686 */ 687 if (xlog_level_init == ~0) 688 xlog_level_init = xl; 689 else 690 xl |= xlog_level_init; 691 xlog_level = xl; 692 } 693 return rc; 694} 695 696#ifdef LOG_DAEMON 697/* 698 * get syslog facility to use. 699 * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc. 700 */ 701static int 702get_syslog_facility(const char *logfile) 703{ 704 char *facstr; 705 706 /* parse facility string */ 707 facstr = strchr(logfile, ':'); 708 if (!facstr) /* log file was "syslog" */ 709 return LOG_DAEMON; 710 facstr++; 711 if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */ 712 plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON"); 713 return LOG_DAEMON; 714 } 715 716#ifdef LOG_KERN 717 if (STREQ(facstr, "kern")) 718 return LOG_KERN; 719#endif /* not LOG_KERN */ 720#ifdef LOG_USER 721 if (STREQ(facstr, "user")) 722 return LOG_USER; 723#endif /* not LOG_USER */ 724#ifdef LOG_MAIL 725 if (STREQ(facstr, "mail")) 726 return LOG_MAIL; 727#endif /* not LOG_MAIL */ 728 729 if (STREQ(facstr, "daemon")) 730 return LOG_DAEMON; 731 732#ifdef LOG_AUTH 733 if (STREQ(facstr, "auth")) 734 return LOG_AUTH; 735#endif /* not LOG_AUTH */ 736#ifdef LOG_SYSLOG 737 if (STREQ(facstr, "syslog")) 738 return LOG_SYSLOG; 739#endif /* not LOG_SYSLOG */ 740#ifdef LOG_LPR 741 if (STREQ(facstr, "lpr")) 742 return LOG_LPR; 743#endif /* not LOG_LPR */ 744#ifdef LOG_NEWS 745 if (STREQ(facstr, "news")) 746 return LOG_NEWS; 747#endif /* not LOG_NEWS */ 748#ifdef LOG_UUCP 749 if (STREQ(facstr, "uucp")) 750 return LOG_UUCP; 751#endif /* not LOG_UUCP */ 752#ifdef LOG_CRON 753 if (STREQ(facstr, "cron")) 754 return LOG_CRON; 755#endif /* not LOG_CRON */ 756#ifdef LOG_LOCAL0 757 if (STREQ(facstr, "local0")) 758 return LOG_LOCAL0; 759#endif /* not LOG_LOCAL0 */ 760#ifdef LOG_LOCAL1 761 if (STREQ(facstr, "local1")) 762 return LOG_LOCAL1; 763#endif /* not LOG_LOCAL1 */ 764#ifdef LOG_LOCAL2 765 if (STREQ(facstr, "local2")) 766 return LOG_LOCAL2; 767#endif /* not LOG_LOCAL2 */ 768#ifdef LOG_LOCAL3 769 if (STREQ(facstr, "local3")) 770 return LOG_LOCAL3; 771#endif /* not LOG_LOCAL3 */ 772#ifdef LOG_LOCAL4 773 if (STREQ(facstr, "local4")) 774 return LOG_LOCAL4; 775#endif /* not LOG_LOCAL4 */ 776#ifdef LOG_LOCAL5 777 if (STREQ(facstr, "local5")) 778 return LOG_LOCAL5; 779#endif /* not LOG_LOCAL5 */ 780#ifdef LOG_LOCAL6 781 if (STREQ(facstr, "local6")) 782 return LOG_LOCAL6; 783#endif /* not LOG_LOCAL6 */ 784#ifdef LOG_LOCAL7 785 if (STREQ(facstr, "local7")) 786 return LOG_LOCAL7; 787#endif /* not LOG_LOCAL7 */ 788 789 /* didn't match anything else */ 790 plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr); 791 return LOG_DAEMON; 792} 793#endif /* not LOG_DAEMON */ 794 795 796/* 797 * Change current logfile 798 */ 799int 800switch_to_logfile(char *logfile, int old_umask) 801{ 802 FILE *new_logfp = stderr; 803 804 if (logfile) { 805#ifdef HAVE_SYSLOG 806 syslogging = 0; 807#endif /* HAVE_SYSLOG */ 808 809 if (STREQ(logfile, "/dev/stderr")) 810 new_logfp = stderr; 811 else if (NSTREQ(logfile, "syslog", strlen("syslog"))) { 812 813#ifdef HAVE_SYSLOG 814 syslogging = 1; 815 new_logfp = stderr; 816 openlog(am_get_progname(), 817 LOG_PID 818# ifdef LOG_CONS 819 | LOG_CONS 820# endif /* LOG_CONS */ 821# ifdef LOG_NOWAIT 822 | LOG_NOWAIT 823# endif /* LOG_NOWAIT */ 824# ifdef LOG_DAEMON 825 , get_syslog_facility(logfile) 826# endif /* LOG_DAEMON */ 827 ); 828#else /* not HAVE_SYSLOG */ 829 plog(XLOG_WARNING, "syslog option not supported, logging unchanged"); 830#endif /* not HAVE_SYSLOG */ 831 832 } else { 833 (void) umask(old_umask); 834 new_logfp = fopen(logfile, "a"); 835 umask(0); 836 } 837 } 838 839 /* 840 * If we couldn't open a new file, then continue using the old. 841 */ 842 if (!new_logfp && logfile) { 843 plog(XLOG_USER, "%s: Can't open logfile: %m", logfile); 844 return 1; 845 } 846 847 /* 848 * Close the previous file 849 */ 850 if (logfp && logfp != stderr) 851 (void) fclose(logfp); 852 logfp = new_logfp; 853 854 plog(XLOG_INFO, "switched to logfile \"%s\"", logfile); 855 return 0; 856} 857 858 859void 860unregister_amq(void) 861{ 862#ifdef DEBUG 863 amuDebug(D_AMQ) 864#endif /* DEBUG */ 865 /* find which instance of amd to unregister */ 866 pmap_unset(get_amd_program_number(), AMQ_VERSION); 867} 868 869 870void 871going_down(int rc) 872{ 873 if (foreground) { 874 if (amd_state != Start) { 875 if (amd_state != Done) 876 return; 877 unregister_amq(); 878 } 879 } 880 if (foreground) { 881 plog(XLOG_INFO, "Finishing with status %d", rc); 882 } else { 883#ifdef DEBUG 884 dlog("background process exiting with status %d", rc); 885#endif /* DEBUG */ 886 } 887 888 exit(rc); 889} 890 891 892/* return the rpc program number under which amd was used */ 893int 894get_amd_program_number(void) 895{ 896 return amd_program_number; 897} 898 899 900/* set the rpc program number used for amd */ 901void 902set_amd_program_number(int program) 903{ 904 amd_program_number = program; 905} 906 907 908/* 909 * Release the controlling tty of the process pid. 910 * 911 * Algorithm: try these in order, if available, until one of them 912 * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0). 913 * Do not use setpgid(): on some OSs it may release the controlling tty, 914 * even if the man page does not mention it, but on other OSs it does not. 915 * Also avoid setpgrp(): it works on some systems, and on others it is 916 * identical to setpgid(). 917 */ 918void 919amu_release_controlling_tty(void) 920{ 921#ifdef TIOCNOTTY 922 int fd; 923#endif /* TIOCNOTTY */ 924 int tempfd; 925 926#ifdef HAVE_SETSID 927 /* XXX: one day maybe use vhangup(2) */ 928 if (setsid() < 0) { 929 plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m"); 930 } else { 931 plog(XLOG_INFO, "released controlling tty using setsid()"); 932 return; 933 } 934#endif /* HAVE_SETSID */ 935 936 /* 937 * In daemon mode, leaving open file descriptors to terminals or pipes 938 * can be a really bad idea. 939 * Case in point: the redhat startup script calls us through their 'initlog' 940 * program, which exits as soon as the original amd process exits. If, at some 941 * point, a misbehaved library function decides to print something to the screen, 942 * we get a SIGPIPE and die. 943 * More precisely: NIS libc functions will attempt to print to stderr 944 * "YPBINDPROC_DOMAIN: Domain not bound" if ypbind is running but can't find 945 * a ypserver. 946 * 947 * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then 948 * reopen them as /dev/null. 949 * 950 * XXX We should also probably set the SIGPIPE handler to SIG_IGN. 951 */ 952 tempfd = open("/dev/null", O_RDWR); 953 fflush(stdin); close(0); dup2(tempfd, 0); 954 fflush(stdout); close(1); dup2(tempfd, 1); 955 fflush(stderr); close(2); dup2(tempfd, 2); 956 close(tempfd); 957 958#ifdef TIOCNOTTY 959 fd = open("/dev/tty", O_RDWR); 960 if (fd < 0) { 961 /* not an error if already no controlling tty */ 962 if (errno != ENXIO) 963 plog(XLOG_WARNING, "Could not open controlling tty: %m"); 964 } else { 965 if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY) 966 plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m"); 967 else 968 plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)"); 969 close(fd); 970 } 971 return; 972#endif /* not TIOCNOTTY */ 973 974 plog(XLOG_ERROR, "unable to release controlling tty"); 975} 976