1/* 2 * Copyright (c) 1997-2014 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. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * 36 * File: am-utils/libamu/xutil.c 37 * 38 */ 39 40/* 41 * Miscellaneous Utilities: Logging, TTY, timers, signals, RPC, memory, etc. 42 */ 43 44#ifdef HAVE_CONFIG_H 45# include <config.h> 46#endif /* HAVE_CONFIG_H */ 47#include <am_defs.h> 48#include <amu.h> 49 50/* 51 * Logfp is the default logging device, and is initialized to stderr by 52 * default in dplog/plog below, and in 53 * amd/amfs_program.c:amfs_program_exec(). 54 */ 55FILE *logfp = NULL; 56 57static char *am_progname = "unknown"; /* "amd" */ 58static char am_hostname[MAXHOSTNAMELEN] = "unknown"; /* Hostname */ 59pid_t am_mypid = -1; /* process ID */ 60serv_state amd_state; /* amd's state */ 61int foreground = 1; /* 1 == this is the top-level server */ 62u_int debug_flags = D_CONTROL; /* set regardless if compiled with debugging */ 63 64#ifdef HAVE_SYSLOG 65int syslogging; 66#endif /* HAVE_SYSLOG */ 67static u_int xlog_level = XLOG_DEFAULT; 68static u_long amd_program_number = AMQ_PROGRAM; 69 70#ifdef DEBUG_MEM 71# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) 72static int mem_bytes; 73static int orig_mem_bytes; 74# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ 75#endif /* DEBUG_MEM */ 76 77/* forward definitions */ 78/* for GCC format string auditing */ 79static void real_plog(int lvl, const char *fmt, va_list vargs) 80 __attribute__((__format__(__printf__, 2, 0))); 81 82 83#ifdef DEBUG 84/* 85 * List of debug options. 86 */ 87struct opt_tab dbg_opt[] = 88{ 89 {"all", D_ALL}, /* All non-disruptive options */ 90 {"defaults", D_DEFAULT}, /* Default options */ 91 {"test", D_TEST}, /* Full debug - no daemon, no fork, no amq, local mtab */ 92 {"amq", D_AMQ}, /* Register for AMQ program */ 93 {"daemon", D_DAEMON}, /* Enter daemon mode */ 94 {"fork", D_FORK}, /* Fork server (hlfsd only) */ 95 {"full", D_FULL}, /* Program trace */ 96#ifdef HAVE_CLOCK_GETTIME 97 {"hrtime", D_HRTIME}, /* Print high resolution time stamps */ 98#endif /* HAVE_CLOCK_GETTIME */ 99 {"info", D_INFO}, /* info service specific debugging (hesiod, nis, etc) */ 100 {"mem", D_MEM}, /* Trace memory allocations */ 101 {"mtab", D_MTAB}, /* Use local mtab file */ 102 {"readdir", D_READDIR}, /* Check on browsable_dirs progress */ 103 {"str", D_STR}, /* Debug string munging */ 104 {"trace", D_TRACE}, /* Protocol trace */ 105 {"xdrtrace", D_XDRTRACE}, /* Trace xdr routines */ 106 {NULL, 0} 107}; 108#endif /* DEBUG */ 109 110/* 111 * List of log options 112 */ 113struct opt_tab xlog_opt[] = 114{ 115 {"all", XLOG_ALL}, /* All messages */ 116 {"defaults", XLOG_DEFAULT}, /* Default messages */ 117#ifdef DEBUG 118 {"debug", XLOG_DEBUG}, /* Debug messages */ 119#endif /* DEBUG */ /* DEBUG */ 120 {"error", XLOG_ERROR}, /* Non-fatal system errors */ 121 {"fatal", XLOG_FATAL}, /* Fatal errors */ 122 {"info", XLOG_INFO}, /* Information */ 123 {"map", XLOG_MAP}, /* Map errors */ 124 {"stats", XLOG_STATS}, /* Additional statistical information */ 125 {"user", XLOG_USER}, /* Non-fatal user errors */ 126 {"warn", XLOG_WARNING}, /* Warnings */ 127 {"warning", XLOG_WARNING}, /* Warnings */ 128 {NULL, 0} 129}; 130 131 132void 133am_set_progname(char *pn) 134{ 135 am_progname = pn; 136} 137 138 139const char * 140am_get_progname(void) 141{ 142 return am_progname; 143} 144 145 146void 147am_set_hostname(char *hn) 148{ 149 xstrlcpy(am_hostname, hn, sizeof(am_hostname)); 150} 151 152 153const char * 154am_get_hostname(void) 155{ 156 return am_hostname; 157} 158 159 160pid_t 161am_set_mypid(void) 162{ 163 am_mypid = getpid(); 164 return am_mypid; 165} 166 167 168long 169get_server_pid() 170{ 171 return (long) (foreground ? am_mypid : getppid()); 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 (amuDebug(D_MEM)) 191 plog(XLOG_DEBUG, "Allocated size %d; block %p", len, p); 192 return p; 193 } 194 if (retries > 0) { 195 plog(XLOG_ERROR, "Retrying memory allocation"); 196 sleep(1); 197 } 198 } while (--retries); 199 200 plog(XLOG_FATAL, "Out of memory"); 201 going_down(1); 202 203 abort(); 204 205 return 0; 206} 207 208 209/* like xmalloc, but zeros out the bytes */ 210voidp 211xzalloc(int len) 212{ 213 voidp p = xmalloc(len); 214 215 if (p) 216 memset(p, 0, len); 217 return p; 218} 219 220 221voidp 222xrealloc(voidp ptr, int len) 223{ 224 if (amuDebug(D_MEM)) 225 plog(XLOG_DEBUG, "Reallocated size %d; block %p", len, ptr); 226 227 if (len == 0) 228 len = 1; 229 230 if (ptr) 231 ptr = (voidp) realloc(ptr, (unsigned) len); 232 else 233 ptr = (voidp) xmalloc((unsigned) len); 234 235 if (!ptr) { 236 plog(XLOG_FATAL, "Out of memory in realloc"); 237 going_down(1); 238 abort(); 239 } 240 return ptr; 241} 242 243 244#ifdef DEBUG_MEM 245void 246dxfree(char *file, int line, voidp ptr) 247{ 248 if (amuDebug(D_MEM)) 249 plog(XLOG_DEBUG, "Free in %s:%d: block %p", file, line, ptr); 250 /* this is the only place that must NOT use XFREE()!!! */ 251 free(ptr); 252 ptr = NULL; /* paranoid */ 253} 254 255 256# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) 257static void 258checkup_mem(void) 259{ 260 struct mallinfo mi = mallinfo(); 261 u_long uordbytes = mi.uordblks * 4096; 262 263 if (mem_bytes != uordbytes) { 264 if (orig_mem_bytes == 0) 265 mem_bytes = orig_mem_bytes = uordbytes; 266 else { 267 fprintf(logfp, "%s[%ld]: ", am_get_progname(), (long) am_mypid); 268 if (mem_bytes < uordbytes) { 269 fprintf(logfp, "ALLOC: %ld bytes", uordbytes - mem_bytes); 270 } else { 271 fprintf(logfp, "FREE: %ld bytes", mem_bytes - uordbytes); 272 } 273 mem_bytes = uordbytes; 274 fprintf(logfp, ", making %d missing\n", mem_bytes - orig_mem_bytes); 275 } 276 } 277 malloc_verify(); 278} 279# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ 280#endif /* DEBUG_MEM */ 281 282 283/* 284 * Take a log format string and expand occurrences of %m 285 * with the current error code taken from errno. Make sure 286 * 'e' never gets longer than maxlen characters. 287 */ 288static const char * 289expand_error(const char *f, char *e, size_t maxlen) 290{ 291 const char *p; 292 char *q; 293 int error = errno; 294 size_t len = 0, l; 295 296 *e = '\0'; 297 for (p = f, q = e; len < maxlen && (*q = *p); len++, q++, p++) { 298 if (p[0] == '%' && p[1] == 'm') { 299 if (len >= maxlen) 300 break; 301 xstrlcpy(q, strerror(error), maxlen - len); 302 l = strlen(q); 303 if (l != 0) 304 l--; 305 len += l; 306 q += l; 307 p++; 308 } 309 } 310 e[maxlen - 1] = '\0'; /* null terminate, to be sure */ 311 return e; 312} 313 314 315/* 316 * Output the time of day and hostname to the logfile 317 */ 318static void 319show_time_host_and_name(int lvl) 320{ 321 static time_t last_t = 0; 322 static char *last_ctime = NULL; 323 time_t t; 324#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) 325 struct timespec ts; 326#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */ 327 char nsecs[11]; /* '.' + 9 digits + '\0' */ 328 char *sev; 329 330 nsecs[0] = '\0'; 331 332#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) 333 /* 334 * Some systems (AIX 4.3) seem to implement clock_gettime() as stub 335 * returning ENOSYS. 336 */ 337 if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { 338 t = ts.tv_sec; 339 if (amuDebug(D_HRTIME)) 340 xsnprintf(nsecs, sizeof(nsecs), ".%09ld", ts.tv_nsec); 341 } 342 else 343#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */ 344 t = clocktime(NULL); 345 346 if (t != last_t) { 347 last_ctime = ctime(&t); 348 last_t = t; 349 } 350 351 switch (lvl) { 352 case XLOG_FATAL: 353 sev = "fatal:"; 354 break; 355 case XLOG_ERROR: 356 sev = "error:"; 357 break; 358 case XLOG_USER: 359 sev = "user: "; 360 break; 361 case XLOG_WARNING: 362 sev = "warn: "; 363 break; 364 case XLOG_INFO: 365 sev = "info: "; 366 break; 367 case XLOG_DEBUG: 368 sev = "debug:"; 369 break; 370 case XLOG_MAP: 371 sev = "map: "; 372 break; 373 case XLOG_STATS: 374 sev = "stats:"; 375 break; 376 default: 377 sev = "hmm: "; 378 break; 379 } 380 fprintf(logfp, "%15.15s%s %s %s[%ld]/%s ", 381 last_ctime + 4, nsecs, am_get_hostname(), 382 am_get_progname(), 383 (long) am_mypid, 384 sev); 385} 386 387 388#ifdef DEBUG 389/* 390 * Switch on/off debug options 391 */ 392int 393debug_option(char *opt) 394{ 395 u_int dl = debug_flags; 396 static int initialized_debug_flags = 0; 397 int rc = cmdoption(opt, dbg_opt, &dl); 398 399 if (rc) /* if got any error, don't update debug flags */ 400 return EINVAL; 401 402 /* 403 * If we already initialized the debugging flags once (via amd.conf), then 404 * don't allow "immutable" flags to be changed again (via amq -D), because 405 * they could mess Amd's state and only make sense to be set once when Amd 406 * starts. 407 */ 408 if (initialized_debug_flags && 409 debug_flags != 0 && 410 (dl & D_IMMUTABLE) != (debug_flags & D_IMMUTABLE)) { 411 plog(XLOG_ERROR, "cannot change immutable debug flags"); 412 /* undo any attempted change to an immutable flag */ 413 dl = (dl & ~D_IMMUTABLE) | (debug_flags & D_IMMUTABLE); 414 } 415 initialized_debug_flags = 1; 416 debug_flags = dl; 417 418 return rc; 419} 420 421 422void 423dplog(const char *fmt, ...) 424{ 425#ifdef HAVE_SIGACTION 426 sigset_t old, chld; 427#else /* not HAVE_SIGACTION */ 428 int mask; 429#endif /* not HAVE_SIGACTION */ 430 va_list ap; 431 432#ifdef HAVE_SIGACTION 433 sigemptyset(&chld); 434 sigaddset(&chld, SIGCHLD); 435#else /* not HAVE_SIGACTION */ 436 mask = sigblock(sigmask(SIGCHLD)); 437#endif /* not HAVE_SIGACTION */ 438 439 sigprocmask(SIG_BLOCK, &chld, &old); 440 if (!logfp) 441 logfp = stderr; /* initialize before possible first use */ 442 443 va_start(ap, fmt); 444 real_plog(XLOG_DEBUG, fmt, ap); 445 va_end(ap); 446 447#ifdef HAVE_SIGACTION 448 sigprocmask(SIG_SETMASK, &old, NULL); 449#else /* not HAVE_SIGACTION */ 450 mask = sigblock(sigmask(SIGCHLD)); 451#endif /* not HAVE_SIGACTION */ 452} 453#endif /* DEBUG */ 454 455 456void 457plog(int lvl, const char *fmt, ...) 458{ 459#ifdef HAVE_SIGACTION 460 sigset_t old, chld; 461#else /* not HAVE_SIGACTION */ 462 int mask; 463#endif /* not HAVE_SIGACTION */ 464 va_list ap; 465 466#ifdef HAVE_SIGACTION 467 sigemptyset(&chld); 468 sigaddset(&chld, SIGCHLD); 469 sigprocmask(SIG_BLOCK, &chld, &old); 470#else /* not HAVE_SIGACTION */ 471 mask = sigblock(sigmask(SIGCHLD)); 472#endif /* not HAVE_SIGACTION */ 473 474 if (!logfp) 475 logfp = stderr; /* initialize before possible first use */ 476 477 va_start(ap, fmt); 478 real_plog(lvl, fmt, ap); 479 va_end(ap); 480 481#ifdef HAVE_SIGACTION 482 sigprocmask(SIG_SETMASK, &old, NULL); 483#else /* not HAVE_SIGACTION */ 484 sigsetmask(mask); 485#endif /* not HAVE_SIGACTION */ 486} 487 488 489static void 490real_plog(int lvl, const char *fmt, va_list vargs) 491{ 492 char msg[1024]; 493 char efmt[1024]; 494 char *ptr = msg; 495 static char last_msg[1024]; 496 static int last_count = 0, last_lvl = 0; 497 498 if (!(xlog_level & lvl)) 499 return; 500 501#ifdef DEBUG_MEM 502# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) 503 checkup_mem(); 504# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ 505#endif /* DEBUG_MEM */ 506 507 /* 508 * Note: xvsnprintf() may call plog() if a truncation happened, but the 509 * latter has some code to break out of an infinite loop. See comment in 510 * xsnprintf() below. 511 */ 512 xvsnprintf(ptr, 1023, expand_error(fmt, efmt, 1024), vargs); 513 514 ptr += strlen(ptr); 515 if (*(ptr-1) == '\n') 516 *--ptr = '\0'; 517 518#ifdef HAVE_SYSLOG 519 if (syslogging) { 520 switch (lvl) { /* from mike <mcooper@usc.edu> */ 521 case XLOG_FATAL: 522 lvl = LOG_CRIT; 523 break; 524 case XLOG_ERROR: 525 lvl = LOG_ERR; 526 break; 527 case XLOG_USER: 528 lvl = LOG_WARNING; 529 break; 530 case XLOG_WARNING: 531 lvl = LOG_WARNING; 532 break; 533 case XLOG_INFO: 534 lvl = LOG_INFO; 535 break; 536 case XLOG_DEBUG: 537 lvl = LOG_DEBUG; 538 break; 539 case XLOG_MAP: 540 lvl = LOG_DEBUG; 541 break; 542 case XLOG_STATS: 543 lvl = LOG_INFO; 544 break; 545 default: 546 lvl = LOG_ERR; 547 break; 548 } 549 syslog(lvl, "%s", msg); 550 return; 551 } 552#endif /* HAVE_SYSLOG */ 553 554 *ptr++ = '\n'; 555 *ptr = '\0'; 556 557 /* 558 * mimic syslog behavior: only write repeated strings if they differ 559 */ 560 switch (last_count) { 561 case 0: /* never printed at all */ 562 last_count = 1; 563 if (strlcpy(last_msg, msg, sizeof(last_msg)) >= sizeof(last_msg)) /* don't use xstrlcpy here (recursive!) */ 564 fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg); 565 last_lvl = lvl; 566 show_time_host_and_name(lvl); /* mimic syslog header */ 567 __IGNORE(fwrite(msg, ptr - msg, 1, logfp)); 568 fflush(logfp); 569 break; 570 571 case 1: /* item printed once, if same, don't repeat */ 572 if (STREQ(last_msg, msg)) { 573 last_count++; 574 } else { /* last msg printed once, new one differs */ 575 /* last_count remains at 1 */ 576 if (strlcpy(last_msg, msg, sizeof(last_msg)) >= sizeof(last_msg)) /* don't use xstrlcpy here (recursive!) */ 577 fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg); 578 last_lvl = lvl; 579 show_time_host_and_name(lvl); /* mimic syslog header */ 580 __IGNORE(fwrite(msg, ptr - msg, 1, logfp)); 581 fflush(logfp); 582 } 583 break; 584 585 case 100: 586 /* 587 * Don't allow repetitions longer than 100, so you can see when something 588 * cycles like crazy. 589 */ 590 show_time_host_and_name(last_lvl); 591 xsnprintf(last_msg, sizeof(last_msg), 592 "last message repeated %d times\n", last_count); 593 __IGNORE(fwrite(last_msg, strlen(last_msg), 1, logfp)); 594 fflush(logfp); 595 last_count = 0; /* start from scratch */ 596 break; 597 598 default: /* item repeated multiple times */ 599 if (STREQ(last_msg, msg)) { 600 last_count++; 601 } else { /* last msg repeated+skipped, new one differs */ 602 show_time_host_and_name(last_lvl); 603 xsnprintf(last_msg, sizeof(last_msg), 604 "last message repeated %d times\n", last_count); 605 __IGNORE(fwrite(last_msg, strlen(last_msg), 1, logfp)); 606 if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */ 607 fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg); 608 last_count = 1; 609 last_lvl = lvl; 610 show_time_host_and_name(lvl); /* mimic syslog header */ 611 __IGNORE(fwrite(msg, ptr - msg, 1, logfp)); 612 fflush(logfp); 613 } 614 break; 615 } 616 617} 618 619 620/* 621 * Display current debug options 622 */ 623void 624show_opts(int ch, struct opt_tab *opts) 625{ 626 int i; 627 int s = '{'; 628 629 fprintf(stderr, "\t[-%c {no}", ch); 630 for (i = 0; opts[i].opt; i++) { 631 fprintf(stderr, "%c%s", s, opts[i].opt); 632 s = ','; 633 } 634 fputs("}]\n", stderr); 635} 636 637 638int 639cmdoption(char *s, struct opt_tab *optb, u_int *flags) 640{ 641 char *p = s; 642 int errs = 0; 643 644 while (p && *p) { 645 int neg; 646 char *opt; 647 struct opt_tab *dp, *dpn = NULL; 648 649 s = p; 650 p = strchr(p, ','); 651 if (p) 652 *p = '\0'; 653 654 /* check for "no" prefix to options */ 655 if (s[0] == 'n' && s[1] == 'o') { 656 opt = s + 2; 657 neg = 1; 658 } else { 659 opt = s; 660 neg = 0; 661 } 662 663 /* 664 * Scan the array of debug options to find the 665 * corresponding flag value. If it is found 666 * then set (or clear) the flag (depending on 667 * whether the option was prefixed with "no"). 668 */ 669 for (dp = optb; dp->opt; dp++) { 670 if (STREQ(opt, dp->opt)) 671 break; 672 if (opt != s && !dpn && STREQ(s, dp->opt)) 673 dpn = dp; 674 } 675 676 if (dp->opt || dpn) { 677 if (!dp->opt) { 678 dp = dpn; 679 neg = !neg; 680 } 681 if (neg) 682 *flags &= ~dp->flag; 683 else 684 *flags |= dp->flag; 685 } else { 686 /* 687 * This will log to stderr when parsing the command line 688 * since any -l option will not yet have taken effect. 689 */ 690 plog(XLOG_ERROR, "option \"%s\" not recognized", s); 691 errs++; 692 } 693 694 /* 695 * Put the comma back 696 */ 697 if (p) 698 *p++ = ','; 699 } 700 701 return errs; 702} 703 704 705/* 706 * Switch on/off logging options 707 */ 708int 709switch_option(char *opt) 710{ 711 u_int xl = xlog_level; 712 int rc = cmdoption(opt, xlog_opt, &xl); 713 714 if (rc) /* if got any error, don't update flags */ 715 return EINVAL; 716 717 /* 718 * Don't allow "mandatory" flags to be turned off, because 719 * we must always be able to report on flag re/setting errors. 720 */ 721 if ((xl & XLOG_MANDATORY) != XLOG_MANDATORY) { 722 plog(XLOG_ERROR, "cannot turn off mandatory logging options"); 723 xl |= XLOG_MANDATORY; 724 } 725 if (xlog_level != xl) 726 xlog_level = xl; /* set new flags */ 727 return rc; 728} 729 730 731#ifdef LOG_DAEMON 732/* 733 * get syslog facility to use. 734 * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc. 735 */ 736static int 737get_syslog_facility(const char *logfile) 738{ 739 char *facstr; 740 741 /* parse facility string */ 742 facstr = strchr(logfile, ':'); 743 if (!facstr) /* log file was "syslog" */ 744 return LOG_DAEMON; 745 facstr++; 746 if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */ 747 plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON"); 748 return LOG_DAEMON; 749 } 750 751#ifdef LOG_KERN 752 if (STREQ(facstr, "kern")) 753 return LOG_KERN; 754#endif /* not LOG_KERN */ 755#ifdef LOG_USER 756 if (STREQ(facstr, "user")) 757 return LOG_USER; 758#endif /* not LOG_USER */ 759#ifdef LOG_MAIL 760 if (STREQ(facstr, "mail")) 761 return LOG_MAIL; 762#endif /* not LOG_MAIL */ 763 764 if (STREQ(facstr, "daemon")) 765 return LOG_DAEMON; 766 767#ifdef LOG_AUTH 768 if (STREQ(facstr, "auth")) 769 return LOG_AUTH; 770#endif /* not LOG_AUTH */ 771#ifdef LOG_SYSLOG 772 if (STREQ(facstr, "syslog")) 773 return LOG_SYSLOG; 774#endif /* not LOG_SYSLOG */ 775#ifdef LOG_LPR 776 if (STREQ(facstr, "lpr")) 777 return LOG_LPR; 778#endif /* not LOG_LPR */ 779#ifdef LOG_NEWS 780 if (STREQ(facstr, "news")) 781 return LOG_NEWS; 782#endif /* not LOG_NEWS */ 783#ifdef LOG_UUCP 784 if (STREQ(facstr, "uucp")) 785 return LOG_UUCP; 786#endif /* not LOG_UUCP */ 787#ifdef LOG_CRON 788 if (STREQ(facstr, "cron")) 789 return LOG_CRON; 790#endif /* not LOG_CRON */ 791#ifdef LOG_LOCAL0 792 if (STREQ(facstr, "local0")) 793 return LOG_LOCAL0; 794#endif /* not LOG_LOCAL0 */ 795#ifdef LOG_LOCAL1 796 if (STREQ(facstr, "local1")) 797 return LOG_LOCAL1; 798#endif /* not LOG_LOCAL1 */ 799#ifdef LOG_LOCAL2 800 if (STREQ(facstr, "local2")) 801 return LOG_LOCAL2; 802#endif /* not LOG_LOCAL2 */ 803#ifdef LOG_LOCAL3 804 if (STREQ(facstr, "local3")) 805 return LOG_LOCAL3; 806#endif /* not LOG_LOCAL3 */ 807#ifdef LOG_LOCAL4 808 if (STREQ(facstr, "local4")) 809 return LOG_LOCAL4; 810#endif /* not LOG_LOCAL4 */ 811#ifdef LOG_LOCAL5 812 if (STREQ(facstr, "local5")) 813 return LOG_LOCAL5; 814#endif /* not LOG_LOCAL5 */ 815#ifdef LOG_LOCAL6 816 if (STREQ(facstr, "local6")) 817 return LOG_LOCAL6; 818#endif /* not LOG_LOCAL6 */ 819#ifdef LOG_LOCAL7 820 if (STREQ(facstr, "local7")) 821 return LOG_LOCAL7; 822#endif /* not LOG_LOCAL7 */ 823 824 /* didn't match anything else */ 825 plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr); 826 return LOG_DAEMON; 827} 828#endif /* not LOG_DAEMON */ 829 830 831/* 832 * Change current logfile 833 */ 834int 835switch_to_logfile(char *logfile, int old_umask, int truncate_log) 836{ 837 FILE *new_logfp = stderr; 838 839 if (logfile) { 840#ifdef HAVE_SYSLOG 841 syslogging = 0; 842#endif /* HAVE_SYSLOG */ 843 844 if (STREQ(logfile, "/dev/stderr")) 845 new_logfp = stderr; 846 else if (NSTREQ(logfile, "syslog", strlen("syslog"))) { 847 848#ifdef HAVE_SYSLOG 849 syslogging = 1; 850 new_logfp = stderr; 851 openlog(am_get_progname(), 852 LOG_PID 853# ifdef LOG_NOWAIT 854 | LOG_NOWAIT 855# endif /* LOG_NOWAIT */ 856# ifdef LOG_DAEMON 857 , get_syslog_facility(logfile) 858# endif /* LOG_DAEMON */ 859 ); 860#else /* not HAVE_SYSLOG */ 861 plog(XLOG_WARNING, "syslog option not supported, logging unchanged"); 862#endif /* not HAVE_SYSLOG */ 863 864 } else { /* regular log file */ 865 (void) umask(old_umask); 866 if (truncate_log) 867 __IGNORE(truncate(logfile, 0)); 868 new_logfp = fopen(logfile, "a"); 869 umask(0); 870 } 871 } 872 873 /* 874 * If we couldn't open a new file, then continue using the old. 875 */ 876 if (!new_logfp && logfile) { 877 plog(XLOG_USER, "%s: Can't open logfile: %m", logfile); 878 return 1; 879 } 880 881 /* 882 * Close the previous file 883 */ 884 if (logfp && logfp != stderr) 885 (void) fclose(logfp); 886 logfp = new_logfp; 887 888 if (logfile) 889 plog(XLOG_INFO, "switched to logfile \"%s\"", logfile); 890 else 891 plog(XLOG_INFO, "no logfile defined; using stderr"); 892 893 return 0; 894} 895 896 897void 898unregister_amq(void) 899{ 900 901 if (amuDebug(D_AMQ)) { 902 /* find which instance of amd to unregister */ 903 u_long amd_prognum = get_amd_program_number(); 904 905 if (pmap_unset(amd_prognum, AMQ_VERSION) != 1) 906 dlog("failed to de-register Amd program %lu, version %lu", 907 amd_prognum, AMQ_VERSION); 908 } 909} 910 911 912void 913going_down(int rc) 914{ 915 if (foreground) { 916 if (amd_state != Start) { 917 if (amd_state != Done) 918 return; 919 unregister_amq(); 920 } 921 } 922 923#ifdef MOUNT_TABLE_ON_FILE 924 /* 925 * Call unlock_mntlist to free any important resources such as an on-disk 926 * lock file (/etc/mtab~). 927 */ 928 unlock_mntlist(); 929#endif /* MOUNT_TABLE_ON_FILE */ 930 931 if (foreground) { 932 plog(XLOG_INFO, "Finishing with status %d", rc); 933 } else { 934 dlog("background process exiting with status %d", rc); 935 } 936 /* bye bye... */ 937 exit(rc); 938} 939 940 941/* return the rpc program number under which amd was used */ 942u_long 943get_amd_program_number(void) 944{ 945 return amd_program_number; 946} 947 948 949/* set the rpc program number used for amd */ 950void 951set_amd_program_number(u_long program) 952{ 953 amd_program_number = program; 954} 955 956 957/* 958 * Release the controlling tty of the process pid. 959 * 960 * Algorithm: try these in order, if available, until one of them 961 * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0). 962 * Do not use setpgid(): on some OSs it may release the controlling tty, 963 * even if the man page does not mention it, but on other OSs it does not. 964 * Also avoid setpgrp(): it works on some systems, and on others it is 965 * identical to setpgid(). 966 */ 967void 968amu_release_controlling_tty(void) 969{ 970 int fd; 971 972 /* 973 * In daemon mode, leaving open file descriptors to terminals or pipes 974 * can be a really bad idea. 975 * Case in point: the redhat startup script calls us through their 'initlog' 976 * program, which exits as soon as the original amd process exits. If, 977 * at some point, a misbehaved library function decides to print something 978 * to the screen, we get a SIGPIPE and die. 979 * And guess what: NIS glibc functions will attempt to print to stderr 980 * "YPBINDPROC_DOMAIN: Domain not bound" if ypbind is running but can't find 981 * a ypserver. 982 * 983 * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then 984 * reopen them as /dev/null. 985 * 986 * XXX We should also probably set the SIGPIPE handler to SIG_IGN. 987 */ 988 fd = open("/dev/null", O_RDWR); 989 if (fd < 0) { 990 plog(XLOG_WARNING, "Could not open /dev/null for rw: %m"); 991 } else { 992 fflush(stdin); close(0); dup2(fd, 0); 993 fflush(stdout); close(1); dup2(fd, 1); 994 fflush(stderr); close(2); dup2(fd, 2); 995 close(fd); 996 } 997 998#ifdef HAVE_SETSID 999 /* XXX: one day maybe use vhangup(2) */ 1000 if (setsid() < 0) { 1001 plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m"); 1002 } else { 1003 plog(XLOG_INFO, "released controlling tty using setsid()"); 1004 return; 1005 } 1006#endif /* HAVE_SETSID */ 1007 1008#ifdef TIOCNOTTY 1009 fd = open("/dev/tty", O_RDWR); 1010 if (fd < 0) { 1011 /* not an error if already no controlling tty */ 1012 if (errno != ENXIO) 1013 plog(XLOG_WARNING, "Could not open controlling tty: %m"); 1014 } else { 1015 if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY) 1016 plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m"); 1017 else 1018 plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)"); 1019 close(fd); 1020 } 1021 return; 1022#else 1023 plog(XLOG_ERROR, "unable to release controlling tty"); 1024#endif /* not TIOCNOTTY */ 1025} 1026 1027 1028/* setup a single signal handler */ 1029void 1030setup_sighandler(int signum, void (*handler)(int)) 1031{ 1032#ifdef HAVE_SIGACTION 1033 struct sigaction sa; 1034 memset(&sa, 0, sizeof(sa)); 1035 sa.sa_flags = 0; /* unnecessary */ 1036 sa.sa_handler = handler; 1037 sigemptyset(&(sa.sa_mask)); /* probably unnecessary too */ 1038 sigaddset(&(sa.sa_mask), signum); 1039 sigaction(signum, &sa, NULL); 1040#else /* not HAVE_SIGACTION */ 1041 (void) signal(signum, handler); 1042#endif /* not HAVE_SIGACTION */ 1043} 1044 1045 1046/* 1047 * Return current time in seconds. If passed a non-null argyument, then 1048 * fill it in with the current time in seconds and microseconds (useful 1049 * for mtime updates). 1050 */ 1051time_t 1052clocktime(nfstime *nt) 1053{ 1054 static struct timeval now; /* keep last time, as default */ 1055 1056 if (gettimeofday(&now, NULL) < 0) { 1057 plog(XLOG_ERROR, "clocktime: gettimeofday: %m"); 1058 /* hack: force time to have incremented by at least 1 second */ 1059 now.tv_sec++; 1060 } 1061 /* copy seconds and microseconds. may demote a long to an int */ 1062 if (nt) { 1063 nt->nt_seconds = (u_int) now.tv_sec; 1064 nt->nt_useconds = (u_int) now.tv_usec; 1065 } 1066 return (time_t) now.tv_sec; 1067} 1068 1069 1070/* 1071 * Make all the directories in the path. 1072 */ 1073int 1074mkdirs(char *path, int mode) 1075{ 1076 /* 1077 * take a copy in case path is in readonly store 1078 */ 1079 char *p2 = xstrdup(path); 1080 char *sp = p2; 1081 struct stat stb; 1082 int error_so_far = 0; 1083 1084 /* 1085 * Skip through the string make the directories. 1086 * Mostly ignore errors - the result is tested at the end. 1087 * 1088 * This assumes we are root so that we can do mkdir in a 1089 * mode 555 directory... 1090 */ 1091 while ((sp = strchr(sp + 1, '/'))) { 1092 *sp = '\0'; 1093 if (mkdir(p2, mode) < 0) { 1094 error_so_far = errno; 1095 } else { 1096 dlog("mkdir(%s)", p2); 1097 } 1098 *sp = '/'; 1099 } 1100 1101 if (mkdir(p2, mode) < 0) { 1102 error_so_far = errno; 1103 } else { 1104 dlog("mkdir(%s)", p2); 1105 } 1106 1107 XFREE(p2); 1108 1109 return stat(path, &stb) == 0 && 1110 (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far; 1111} 1112 1113 1114/* 1115 * Remove as many directories in the path as possible. 1116 * Give up if the directory doesn't appear to have 1117 * been created by Amd (not mode dr-x) or an rmdir 1118 * fails for any reason. 1119 */ 1120void 1121rmdirs(char *dir) 1122{ 1123 char *xdp = xstrdup(dir); 1124 char *dp; 1125 1126 do { 1127 struct stat stb; 1128 /* 1129 * Try to find out whether this was 1130 * created by amd. Do this by checking 1131 * for owner write permission. 1132 */ 1133 if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) { 1134 if (rmdir(xdp) < 0) { 1135 if (errno != ENOTEMPTY && 1136 errno != EBUSY && 1137 errno != EEXIST && 1138 errno != EROFS && 1139 errno != EINVAL) 1140 plog(XLOG_ERROR, "rmdir(%s): %m", xdp); 1141 break; 1142 } else { 1143 dlog("rmdir(%s)", xdp); 1144 } 1145 } else { 1146 break; 1147 } 1148 1149 dp = strrchr(xdp, '/'); 1150 if (dp) 1151 *dp = '\0'; 1152 } while (dp && dp > xdp); 1153 1154 XFREE(xdp); 1155} 1156 1157/* 1158 * Dup a string 1159 */ 1160char * 1161xstrdup(const char *s) 1162{ 1163 size_t len = strlen(s); 1164 char *sp = xmalloc(len + 1); 1165 memcpy(sp, s, len + 1); 1166 return sp; 1167} 1168