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