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