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