142994Swollman/* 242994Swollman * Copyright (c) 1997-2006 Erez Zadok 342994Swollman * Copyright (c) 1990 Jan-Simon Pendry 442994Swollman * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 542994Swollman * Copyright (c) 1990 The Regents of the University of California. 642994Swollman * All rights reserved. 742994Swollman * 842994Swollman * This code is derived from software contributed to Berkeley by 942994Swollman * Jan-Simon Pendry at Imperial College, London. 1042994Swollman * 1142997Swollman * Redistribution and use in source and binary forms, with or without 1242997Swollman * modification, are permitted provided that the following conditions 1342997Swollman * are met: 1442997Swollman * 1. Redistributions of source code must retain the above copyright 1542997Swollman * notice, this list of conditions and the following disclaimer. 1650479Speter * 2. Redistributions in binary form must reproduce the above copyright 1742997Swollman * notice, this list of conditions and the following disclaimer in the 1842997Swollman * documentation and/or other materials provided with the distribution. 1942997Swollman * 3. All advertising materials mentioning features or use of this software 2042994Swollman * must display the following acknowledgment: 2142994Swollman * This product includes software developed by the University of 2242994Swollman * California, Berkeley and its contributors. 2342994Swollman * 4. Neither the name of the University nor the names of its contributors 2442994Swollman * may be used to endorse or promote products derived from this software 2542994Swollman * without specific prior written permission. 2642994Swollman * 2742994Swollman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2842994Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2942994Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3042994Swollman * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3142994Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3242994Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33130819Sstefanf * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3442994Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3542994Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3642994Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3742994Swollman * SUCH DAMAGE. 3842994Swollman * 3942994Swollman * 4042994Swollman * File: am-utils/libamu/xutil.c 4142994Swollman * 4242994Swollman */ 4342994Swollman 4442994Swollman/* 4542994Swollman * Miscellaneous Utilities: Logging, TTY, timers, signals, RPC, memory, etc. 4642994Swollman */ 4742997Swollman 4842994Swollman#ifdef HAVE_CONFIG_H 4942994Swollman# include <config.h> 5042994Swollman#endif /* HAVE_CONFIG_H */ 5142994Swollman#include <am_defs.h> 5242994Swollman#include <amu.h> 5342994Swollman 54130819Sstefanf/* 55130819Sstefanf * Logfp is the default logging device, and is initialized to stderr by 56130819Sstefanf * default in dplog/plog below, and in 57130819Sstefanf * amd/amfs_program.c:amfs_program_exec(). 58130819Sstefanf */ 59130819SstefanfFILE *logfp = NULL; 60130819Sstefanf 61130819Sstefanfstatic char *am_progname = "unknown"; /* "amd" */ 6242994Swollmanstatic char am_hostname[MAXHOSTNAMELEN] = "unknown"; /* Hostname */ 6342994Swollmanpid_t am_mypid = -1; /* process ID */ 6442994Swollmanserv_state amd_state; /* amd's state */ 6542994Swollmanint foreground = 1; /* 1 == this is the top-level server */ 6642994Swollmanint debug_flags = 0; 6742994Swollman 6842994Swollman#ifdef HAVE_SYSLOG 6942994Swollmanint syslogging; 7042994Swollman#endif /* HAVE_SYSLOG */ 7142994Swollmanint xlog_level = XLOG_ALL & ~XLOG_MAP & ~XLOG_STATS; 7242994Swollmanint xlog_level_init = ~0; 7342994Swollmanstatic int amd_program_number = AMQ_PROGRAM; 7442994Swollman 7542994Swollman#ifdef DEBUG_MEM 7642994Swollman# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) 7742994Swollmanstatic int mem_bytes; 7842994Swollmanstatic int orig_mem_bytes; 7942994Swollman# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ 8042994Swollman#endif /* DEBUG_MEM */ 8142994Swollman 82130819Sstefanf/* forward definitions */ 83130819Sstefanf/* for GCC format string auditing */ 84130819Sstefanfstatic void real_plog(int lvl, const char *fmt, va_list vargs) 85130819Sstefanf __attribute__((__format__(__printf__, 2, 0))); 8642994Swollman 8742994Swollman 8842994Swollman#ifdef DEBUG 8942994Swollman/* 9042994Swollman * List of debug options. 9142994Swollman */ 9242994Swollmanstruct opt_tab dbg_opt[] = 9342994Swollman{ 9442994Swollman {"all", D_ALL}, /* All non-disruptive options */ 9542994Swollman {"amq", D_AMQ}, /* Don't register for AMQ program */ 9642994Swollman {"daemon", D_DAEMON}, /* Don't enter daemon mode */ 9742994Swollman {"fork", D_FORK}, /* Don't fork server */ 9842994Swollman {"full", D_FULL}, /* Program trace */ 9942994Swollman#ifdef HAVE_CLOCK_GETTIME 10042994Swollman {"hrtime", D_HRTIME}, /* Print high resolution time stamps */ 10142994Swollman#endif /* HAVE_CLOCK_GETTIME */ 10242997Swollman /* info service specific debugging (hesiod, nis, etc) */ 10342994Swollman {"info", D_INFO}, 10442994Swollman {"mem", D_MEM}, /* Trace memory allocations */ 10542994Swollman {"mtab", D_MTAB}, /* Use local mtab file */ 10642994Swollman {"readdir", D_READDIR}, /* Check on browsable_dirs progress */ 10742994Swollman {"str", D_STR}, /* Debug string munging */ 10842994Swollman {"test", D_TEST}, /* Full debug - no daemon, no amq, local mtab */ 10942994Swollman {"trace", D_TRACE}, /* Protocol trace */ 11042994Swollman {"xdrtrace", D_XDRTRACE}, /* Trace xdr routines */ 11142994Swollman {0, 0} 11242994Swollman}; 11342994Swollman#endif /* DEBUG */ 11479858Sdd 11542994Swollman/* 11642994Swollman * List of log options 11742994Swollman */ 11842994Swollmanstruct opt_tab xlog_opt[] = 11942994Swollman{ 12042994Swollman {"all", XLOG_ALL}, /* All messages */ 12142994Swollman#ifdef DEBUG 12242994Swollman {"debug", XLOG_DEBUG}, /* Debug messages */ 12342994Swollman#endif /* DEBUG */ /* DEBUG */ 12442994Swollman {"error", XLOG_ERROR}, /* Non-fatal system errors */ 12542994Swollman {"fatal", XLOG_FATAL}, /* Fatal errors */ 12642994Swollman {"info", XLOG_INFO}, /* Information */ 12742994Swollman {"map", XLOG_MAP}, /* Map errors */ 12842994Swollman {"stats", XLOG_STATS}, /* Additional statistical information */ 12942994Swollman {"user", XLOG_USER}, /* Non-fatal user errors */ 13042994Swollman {"warn", XLOG_WARNING}, /* Warnings */ 13142994Swollman {"warning", XLOG_WARNING}, /* Warnings */ 13242994Swollman {0, 0} 13342994Swollman}; 13442994Swollman 13542994Swollman 13642994Swollmanvoid 13742994Swollmanam_set_progname(char *pn) 13842994Swollman{ 13942994Swollman am_progname = pn; 14042994Swollman} 14142994Swollman 14242994Swollman 14342994Swollmanconst char * 14442994Swollmanam_get_progname(void) 14542994Swollman{ 14642994Swollman return am_progname; 14742994Swollman} 14842994Swollman 14942994Swollman 15042994Swollmanvoid 15142994Swollmanam_set_hostname(char *hn) 15242994Swollman{ 15342994Swollman xstrlcpy(am_hostname, hn, MAXHOSTNAMELEN); 15442994Swollman} 15542994Swollman 15642994Swollman 15742994Swollmanconst char * 15842994Swollmanam_get_hostname(void) 15942994Swollman{ 16042994Swollman return am_hostname; 16142994Swollman} 16242994Swollman 16342994Swollman 16442994Swollmanpid_t 16542994Swollmanam_set_mypid(void) 16642994Swollman{ 16742994Swollman am_mypid = getpid(); 16842994Swollman return am_mypid; 16942994Swollman} 17042994Swollman 17142994Swollman 17242994Swollmanlong 17342994Swollmanget_server_pid() 17442994Swollman{ 17542994Swollman return (long) (foreground ? am_mypid : getppid()); 17642994Swollman} 17742994Swollman 17842994Swollman 17942994Swollmanvoidp 18042994Swollmanxmalloc(int len) 18142994Swollman{ 18242994Swollman voidp p; 18342994Swollman int retries = 600; 18442994Swollman 18542994Swollman /* 18642994Swollman * Avoid malloc's which return NULL for malloc(0) 18742994Swollman */ 18842994Swollman if (len == 0) 18942994Swollman len = 1; 190130819Sstefanf 19142994Swollman do { 19242994Swollman p = (voidp) malloc((unsigned) len); 19342994Swollman if (p) { 194 if (amuDebug(D_MEM)) 195 plog(XLOG_DEBUG, "Allocated size %d; block %p", len, p); 196 return p; 197 } 198 if (retries > 0) { 199 plog(XLOG_ERROR, "Retrying memory allocation"); 200 sleep(1); 201 } 202 } while (--retries); 203 204 plog(XLOG_FATAL, "Out of memory"); 205 going_down(1); 206 207 abort(); 208 209 return 0; 210} 211 212 213/* like xmalloc, but zeros out the bytes */ 214voidp 215xzalloc(int len) 216{ 217 voidp p = xmalloc(len); 218 219 if (p) 220 memset(p, 0, len); 221 return p; 222} 223 224 225voidp 226xrealloc(voidp ptr, int len) 227{ 228 if (amuDebug(D_MEM)) 229 plog(XLOG_DEBUG, "Reallocated size %d; block %p", len, ptr); 230 231 if (len == 0) 232 len = 1; 233 234 if (ptr) 235 ptr = (voidp) realloc(ptr, (unsigned) len); 236 else 237 ptr = (voidp) xmalloc((unsigned) len); 238 239 if (!ptr) { 240 plog(XLOG_FATAL, "Out of memory in realloc"); 241 going_down(1); 242 abort(); 243 } 244 return ptr; 245} 246 247 248#ifdef DEBUG_MEM 249void 250dxfree(char *file, int line, voidp ptr) 251{ 252 if (amuDebug(D_MEM)) 253 plog(XLOG_DEBUG, "Free in %s:%d: block %p", file, line, ptr); 254 /* this is the only place that must NOT use XFREE()!!! */ 255 free(ptr); 256 ptr = NULL; /* paranoid */ 257} 258 259 260# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) 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 /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ 284#endif /* DEBUG_MEM */ 285 286 287/* 288 * Take a log format string and expand occurrences of %m 289 * with the current error code taken from errno. Make sure 290 * 'e' never gets longer than maxlen characters. 291 */ 292static const char * 293expand_error(const char *f, char *e, size_t maxlen) 294{ 295 const char *p; 296 char *q; 297 int error = errno; 298 int len = 0; 299 300 for (p = f, q = e; (*q = *p) && (size_t) len < maxlen; len++, q++, p++) { 301 if (p[0] == '%' && p[1] == 'm') { 302 xstrlcpy(q, strerror(error), maxlen); 303 len += strlen(q) - 1; 304 q += strlen(q) - 1; 305 p++; 306 } 307 } 308 e[maxlen-1] = '\0'; /* null terminate, to be sure */ 309 return e; 310} 311 312 313/* 314 * Output the time of day and hostname to the logfile 315 */ 316static void 317show_time_host_and_name(int lvl) 318{ 319 static time_t last_t = 0; 320 static char *last_ctime = 0; 321 time_t t; 322#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) 323 struct timespec ts; 324#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */ 325 char nsecs[11]; /* '.' + 9 digits + '\0' */ 326 char *sev; 327 328 nsecs[0] = '\0'; 329 330#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) 331 /* 332 * Some systems (AIX 4.3) seem to implement clock_gettime() as stub 333 * returning ENOSYS. 334 */ 335 if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { 336 t = ts.tv_sec; 337 if (amuDebug(D_HRTIME)) 338 xsnprintf(nsecs, sizeof(nsecs), ".%09ld", ts.tv_nsec); 339 } 340 else 341#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */ 342 t = clocktime(NULL); 343 344 if (t != last_t) { 345 last_ctime = ctime(&t); 346 last_t = t; 347 } 348 349 switch (lvl) { 350 case XLOG_FATAL: 351 sev = "fatal:"; 352 break; 353 case XLOG_ERROR: 354 sev = "error:"; 355 break; 356 case XLOG_USER: 357 sev = "user: "; 358 break; 359 case XLOG_WARNING: 360 sev = "warn: "; 361 break; 362 case XLOG_INFO: 363 sev = "info: "; 364 break; 365 case XLOG_DEBUG: 366 sev = "debug:"; 367 break; 368 case XLOG_MAP: 369 sev = "map: "; 370 break; 371 case XLOG_STATS: 372 sev = "stats:"; 373 break; 374 default: 375 sev = "hmm: "; 376 break; 377 } 378 fprintf(logfp, "%15.15s%s %s %s[%ld]/%s ", 379 last_ctime + 4, nsecs, am_get_hostname(), 380 am_get_progname(), 381 (long) am_mypid, 382 sev); 383} 384 385 386#ifdef DEBUG 387/* 388 * Switch on/off debug options 389 */ 390int 391debug_option(char *opt) 392{ 393 return cmdoption(opt, dbg_opt, &debug_flags); 394} 395 396 397void 398dplog(const char *fmt, ...) 399{ 400 va_list ap; 401 402 if (!logfp) 403 logfp = stderr; /* initialize before possible first use */ 404 405 va_start(ap, fmt); 406 real_plog(XLOG_DEBUG, fmt, ap); 407 va_end(ap); 408} 409#endif /* DEBUG */ 410 411 412void 413plog(int lvl, const char *fmt, ...) 414{ 415 va_list ap; 416 417 if (!logfp) 418 logfp = stderr; /* initialize before possible first use */ 419 420 va_start(ap, fmt); 421 real_plog(lvl, fmt, ap); 422 va_end(ap); 423} 424 425 426static void 427real_plog(int lvl, const char *fmt, va_list vargs) 428{ 429 char msg[1024]; 430 char efmt[1024]; 431 char *ptr = msg; 432 static char last_msg[1024]; 433 static int last_count = 0, last_lvl = 0; 434 435 if (!(xlog_level & lvl)) 436 return; 437 438#ifdef DEBUG_MEM 439# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) 440 checkup_mem(); 441# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ 442#endif /* DEBUG_MEM */ 443 444 /* 445 * Note: xvsnprintf() may call plog() if a truncation happened, but the 446 * latter has some code to break out of an infinite loop. See comment in 447 * xsnprintf() below. 448 */ 449 xvsnprintf(ptr, 1023, expand_error(fmt, efmt, 1024), vargs); 450 451 ptr += strlen(ptr); 452 if (*(ptr-1) == '\n') 453 *--ptr = '\0'; 454 455#ifdef HAVE_SYSLOG 456 if (syslogging) { 457 switch (lvl) { /* from mike <mcooper@usc.edu> */ 458 case XLOG_FATAL: 459 lvl = LOG_CRIT; 460 break; 461 case XLOG_ERROR: 462 lvl = LOG_ERR; 463 break; 464 case XLOG_USER: 465 lvl = LOG_WARNING; 466 break; 467 case XLOG_WARNING: 468 lvl = LOG_WARNING; 469 break; 470 case XLOG_INFO: 471 lvl = LOG_INFO; 472 break; 473 case XLOG_DEBUG: 474 lvl = LOG_DEBUG; 475 break; 476 case XLOG_MAP: 477 lvl = LOG_DEBUG; 478 break; 479 case XLOG_STATS: 480 lvl = LOG_INFO; 481 break; 482 default: 483 lvl = LOG_ERR; 484 break; 485 } 486 syslog(lvl, "%s", msg); 487 return; 488 } 489#endif /* HAVE_SYSLOG */ 490 491 *ptr++ = '\n'; 492 *ptr = '\0'; 493 494 /* 495 * mimic syslog behavior: only write repeated strings if they differ 496 */ 497 switch (last_count) { 498 case 0: /* never printed at all */ 499 last_count = 1; 500 if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */ 501 fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg); 502 last_lvl = lvl; 503 show_time_host_and_name(lvl); /* mimic syslog header */ 504 fwrite(msg, ptr - msg, 1, logfp); 505 fflush(logfp); 506 break; 507 508 case 1: /* item printed once, if same, don't repeat */ 509 if (STREQ(last_msg, msg)) { 510 last_count++; 511 } else { /* last msg printed once, new one differs */ 512 /* last_count remains at 1 */ 513 if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */ 514 fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg); 515 last_lvl = lvl; 516 show_time_host_and_name(lvl); /* mimic syslog header */ 517 fwrite(msg, ptr - msg, 1, logfp); 518 fflush(logfp); 519 } 520 break; 521 522 case 100: 523 /* 524 * Don't allow repetitions longer than 100, so you can see when something 525 * cycles like crazy. 526 */ 527 show_time_host_and_name(last_lvl); 528 xsnprintf(last_msg, sizeof(last_msg), 529 "last message repeated %d times\n", last_count); 530 fwrite(last_msg, strlen(last_msg), 1, logfp); 531 fflush(logfp); 532 last_count = 0; /* start from scratch */ 533 break; 534 535 default: /* item repeated multiple times */ 536 if (STREQ(last_msg, msg)) { 537 last_count++; 538 } else { /* last msg repeated+skipped, new one differs */ 539 show_time_host_and_name(last_lvl); 540 xsnprintf(last_msg, sizeof(last_msg), 541 "last message repeated %d times\n", last_count); 542 fwrite(last_msg, strlen(last_msg), 1, logfp); 543 if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */ 544 fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg); 545 last_count = 1; 546 last_lvl = lvl; 547 show_time_host_and_name(lvl); /* mimic syslog header */ 548 fwrite(msg, ptr - msg, 1, logfp); 549 fflush(logfp); 550 } 551 break; 552 } 553 554} 555 556 557/* 558 * Display current debug options 559 */ 560void 561show_opts(int ch, struct opt_tab *opts) 562{ 563 int i; 564 int s = '{'; 565 566 fprintf(stderr, "\t[-%c {no}", ch); 567 for (i = 0; opts[i].opt; i++) { 568 fprintf(stderr, "%c%s", s, opts[i].opt); 569 s = ','; 570 } 571 fputs("}]\n", stderr); 572} 573 574 575int 576cmdoption(char *s, struct opt_tab *optb, int *flags) 577{ 578 char *p = s; 579 int errs = 0; 580 581 while (p && *p) { 582 int neg; 583 char *opt; 584 struct opt_tab *dp, *dpn = 0; 585 586 s = p; 587 p = strchr(p, ','); 588 if (p) 589 *p = '\0'; 590 591 /* check for "no" prefix to options */ 592 if (s[0] == 'n' && s[1] == 'o') { 593 opt = s + 2; 594 neg = 1; 595 } else { 596 opt = s; 597 neg = 0; 598 } 599 600 /* 601 * Scan the array of debug options to find the 602 * corresponding flag value. If it is found 603 * then set (or clear) the flag (depending on 604 * whether the option was prefixed with "no"). 605 */ 606 for (dp = optb; dp->opt; dp++) { 607 if (STREQ(opt, dp->opt)) 608 break; 609 if (opt != s && !dpn && STREQ(s, dp->opt)) 610 dpn = dp; 611 } 612 613 if (dp->opt || dpn) { 614 if (!dp->opt) { 615 dp = dpn; 616 neg = !neg; 617 } 618 if (neg) 619 *flags &= ~dp->flag; 620 else 621 *flags |= dp->flag; 622 } else { 623 /* 624 * This will log to stderr when parsing the command line 625 * since any -l option will not yet have taken effect. 626 */ 627 plog(XLOG_USER, "option \"%s\" not recognized", s); 628 errs++; 629 } 630 631 /* 632 * Put the comma back 633 */ 634 if (p) 635 *p++ = ','; 636 } 637 638 return errs; 639} 640 641 642/* 643 * Switch on/off logging options 644 */ 645int 646switch_option(char *opt) 647{ 648 int xl = xlog_level; 649 int rc = cmdoption(opt, xlog_opt, &xl); 650 651 if (rc) { 652 rc = EINVAL; 653 } else { 654 /* 655 * Keep track of initial log level, and 656 * don't allow options to be turned off. 657 */ 658 if (xlog_level_init == ~0) 659 xlog_level_init = xl; 660 else 661 xl |= xlog_level_init; 662 xlog_level = xl; 663 } 664 return rc; 665} 666 667 668#ifdef LOG_DAEMON 669/* 670 * get syslog facility to use. 671 * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc. 672 */ 673static int 674get_syslog_facility(const char *logfile) 675{ 676 char *facstr; 677 678 /* parse facility string */ 679 facstr = strchr(logfile, ':'); 680 if (!facstr) /* log file was "syslog" */ 681 return LOG_DAEMON; 682 facstr++; 683 if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */ 684 plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON"); 685 return LOG_DAEMON; 686 } 687 688#ifdef LOG_KERN 689 if (STREQ(facstr, "kern")) 690 return LOG_KERN; 691#endif /* not LOG_KERN */ 692#ifdef LOG_USER 693 if (STREQ(facstr, "user")) 694 return LOG_USER; 695#endif /* not LOG_USER */ 696#ifdef LOG_MAIL 697 if (STREQ(facstr, "mail")) 698 return LOG_MAIL; 699#endif /* not LOG_MAIL */ 700 701 if (STREQ(facstr, "daemon")) 702 return LOG_DAEMON; 703 704#ifdef LOG_AUTH 705 if (STREQ(facstr, "auth")) 706 return LOG_AUTH; 707#endif /* not LOG_AUTH */ 708#ifdef LOG_SYSLOG 709 if (STREQ(facstr, "syslog")) 710 return LOG_SYSLOG; 711#endif /* not LOG_SYSLOG */ 712#ifdef LOG_LPR 713 if (STREQ(facstr, "lpr")) 714 return LOG_LPR; 715#endif /* not LOG_LPR */ 716#ifdef LOG_NEWS 717 if (STREQ(facstr, "news")) 718 return LOG_NEWS; 719#endif /* not LOG_NEWS */ 720#ifdef LOG_UUCP 721 if (STREQ(facstr, "uucp")) 722 return LOG_UUCP; 723#endif /* not LOG_UUCP */ 724#ifdef LOG_CRON 725 if (STREQ(facstr, "cron")) 726 return LOG_CRON; 727#endif /* not LOG_CRON */ 728#ifdef LOG_LOCAL0 729 if (STREQ(facstr, "local0")) 730 return LOG_LOCAL0; 731#endif /* not LOG_LOCAL0 */ 732#ifdef LOG_LOCAL1 733 if (STREQ(facstr, "local1")) 734 return LOG_LOCAL1; 735#endif /* not LOG_LOCAL1 */ 736#ifdef LOG_LOCAL2 737 if (STREQ(facstr, "local2")) 738 return LOG_LOCAL2; 739#endif /* not LOG_LOCAL2 */ 740#ifdef LOG_LOCAL3 741 if (STREQ(facstr, "local3")) 742 return LOG_LOCAL3; 743#endif /* not LOG_LOCAL3 */ 744#ifdef LOG_LOCAL4 745 if (STREQ(facstr, "local4")) 746 return LOG_LOCAL4; 747#endif /* not LOG_LOCAL4 */ 748#ifdef LOG_LOCAL5 749 if (STREQ(facstr, "local5")) 750 return LOG_LOCAL5; 751#endif /* not LOG_LOCAL5 */ 752#ifdef LOG_LOCAL6 753 if (STREQ(facstr, "local6")) 754 return LOG_LOCAL6; 755#endif /* not LOG_LOCAL6 */ 756#ifdef LOG_LOCAL7 757 if (STREQ(facstr, "local7")) 758 return LOG_LOCAL7; 759#endif /* not LOG_LOCAL7 */ 760 761 /* didn't match anything else */ 762 plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr); 763 return LOG_DAEMON; 764} 765#endif /* not LOG_DAEMON */ 766 767 768/* 769 * Change current logfile 770 */ 771int 772switch_to_logfile(char *logfile, int old_umask, int truncate_log) 773{ 774 FILE *new_logfp = stderr; 775 776 if (logfile) { 777#ifdef HAVE_SYSLOG 778 syslogging = 0; 779#endif /* HAVE_SYSLOG */ 780 781 if (STREQ(logfile, "/dev/stderr")) 782 new_logfp = stderr; 783 else if (NSTREQ(logfile, "syslog", strlen("syslog"))) { 784 785#ifdef HAVE_SYSLOG 786 syslogging = 1; 787 new_logfp = stderr; 788 openlog(am_get_progname(), 789 LOG_PID 790# ifdef LOG_NOWAIT 791 | LOG_NOWAIT 792# endif /* LOG_NOWAIT */ 793# ifdef LOG_DAEMON 794 , get_syslog_facility(logfile) 795# endif /* LOG_DAEMON */ 796 ); 797#else /* not HAVE_SYSLOG */ 798 plog(XLOG_WARNING, "syslog option not supported, logging unchanged"); 799#endif /* not HAVE_SYSLOG */ 800 801 } else { /* regular log file */ 802 (void) umask(old_umask); 803 if (truncate_log) 804 truncate(logfile, 0); 805 new_logfp = fopen(logfile, "a"); 806 umask(0); 807 } 808 } 809 810 /* 811 * If we couldn't open a new file, then continue using the old. 812 */ 813 if (!new_logfp && logfile) { 814 plog(XLOG_USER, "%s: Can't open logfile: %m", logfile); 815 return 1; 816 } 817 818 /* 819 * Close the previous file 820 */ 821 if (logfp && logfp != stderr) 822 (void) fclose(logfp); 823 logfp = new_logfp; 824 825 if (logfile) 826 plog(XLOG_INFO, "switched to logfile \"%s\"", logfile); 827 else 828 plog(XLOG_INFO, "no logfile defined; using stderr"); 829 830 return 0; 831} 832 833 834void 835unregister_amq(void) 836{ 837 if (!amuDebug(D_AMQ)) { 838 /* find which instance of amd to unregister */ 839 u_long amd_prognum = get_amd_program_number(); 840 841 if (pmap_unset(amd_prognum, AMQ_VERSION) != 1) 842 dlog("failed to de-register Amd program %lu, version %lu", 843 amd_prognum, AMQ_VERSION); 844 } 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 859#ifdef MOUNT_TABLE_ON_FILE 860 /* 861 * Call unlock_mntlist to free any important resources such as an on-disk 862 * lock file (/etc/mtab~). 863 */ 864 unlock_mntlist(); 865#endif /* MOUNT_TABLE_ON_FILE */ 866 867 if (foreground) { 868 plog(XLOG_INFO, "Finishing with status %d", rc); 869 } else { 870 dlog("background process exiting with status %d", rc); 871 } 872 /* bye bye... */ 873 exit(rc); 874} 875 876 877/* return the rpc program number under which amd was used */ 878int 879get_amd_program_number(void) 880{ 881 return amd_program_number; 882} 883 884 885/* set the rpc program number used for amd */ 886void 887set_amd_program_number(int program) 888{ 889 amd_program_number = program; 890} 891 892 893/* 894 * Release the controlling tty of the process pid. 895 * 896 * Algorithm: try these in order, if available, until one of them 897 * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0). 898 * Do not use setpgid(): on some OSs it may release the controlling tty, 899 * even if the man page does not mention it, but on other OSs it does not. 900 * Also avoid setpgrp(): it works on some systems, and on others it is 901 * identical to setpgid(). 902 */ 903void 904amu_release_controlling_tty(void) 905{ 906 int fd; 907 908 /* 909 * In daemon mode, leaving open file descriptors to terminals or pipes 910 * can be a really bad idea. 911 * Case in point: the redhat startup script calls us through their 'initlog' 912 * program, which exits as soon as the original amd process exits. If, 913 * at some point, a misbehaved library function decides to print something 914 * to the screen, we get a SIGPIPE and die. 915 * And guess what: NIS glibc functions will attempt to print to stderr 916 * "YPBINDPROC_DOMAIN: Domain not bound" if ypbind is running but can't find 917 * a ypserver. 918 * 919 * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then 920 * reopen them as /dev/null. 921 * 922 * XXX We should also probably set the SIGPIPE handler to SIG_IGN. 923 */ 924 fd = open("/dev/null", O_RDWR); 925 if (fd < 0) { 926 plog(XLOG_WARNING, "Could not open /dev/null for rw: %m"); 927 } else { 928 fflush(stdin); close(0); dup2(fd, 0); 929 fflush(stdout); close(1); dup2(fd, 1); 930 fflush(stderr); close(2); dup2(fd, 2); 931 close(fd); 932 } 933 934#ifdef HAVE_SETSID 935 /* XXX: one day maybe use vhangup(2) */ 936 if (setsid() < 0) { 937 plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m"); 938 } else { 939 plog(XLOG_INFO, "released controlling tty using setsid()"); 940 return; 941 } 942#endif /* HAVE_SETSID */ 943 944#ifdef TIOCNOTTY 945 fd = open("/dev/tty", O_RDWR); 946 if (fd < 0) { 947 /* not an error if already no controlling tty */ 948 if (errno != ENXIO) 949 plog(XLOG_WARNING, "Could not open controlling tty: %m"); 950 } else { 951 if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY) 952 plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m"); 953 else 954 plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)"); 955 close(fd); 956 } 957 return; 958#endif /* not TIOCNOTTY */ 959 960 plog(XLOG_ERROR, "unable to release controlling tty"); 961} 962 963 964/* setup a single signal handler */ 965void 966setup_sighandler(int signum, void (*handler)(int)) 967{ 968#ifdef HAVE_SIGACTION 969 struct sigaction sa; 970 memset(&sa, 0, sizeof(sa)); 971 sa.sa_flags = 0; /* unnecessary */ 972 sa.sa_handler = handler; 973 sigemptyset(&(sa.sa_mask)); /* probably unnecessary too */ 974 sigaddset(&(sa.sa_mask), signum); 975 sigaction(signum, &sa, NULL); 976#else /* not HAVE_SIGACTION */ 977 (void) signal(signum, handler); 978#endif /* not HAVE_SIGACTION */ 979} 980 981 982/* 983 * Return current time in seconds. If passed a non-null argyument, then 984 * fill it in with the current time in seconds and microseconds (useful 985 * for mtime updates). 986 */ 987time_t 988clocktime(nfstime *nt) 989{ 990 static struct timeval now; /* keep last time, as default */ 991 992 if (gettimeofday(&now, NULL) < 0) { 993 plog(XLOG_ERROR, "clocktime: gettimeofday: %m"); 994 /* hack: force time to have incremented by at least 1 second */ 995 now.tv_sec++; 996 } 997 /* copy seconds and microseconds. may demote a long to an int */ 998 if (nt) { 999 nt->nt_seconds = (u_int) now.tv_sec; 1000 nt->nt_useconds = (u_int) now.tv_usec; 1001 } 1002 return (time_t) now.tv_sec; 1003} 1004 1005 1006/* 1007 * Make all the directories in the path. 1008 */ 1009int 1010mkdirs(char *path, int mode) 1011{ 1012 /* 1013 * take a copy in case path is in readonly store 1014 */ 1015 char *p2 = strdup(path); 1016 char *sp = p2; 1017 struct stat stb; 1018 int error_so_far = 0; 1019 1020 /* 1021 * Skip through the string make the directories. 1022 * Mostly ignore errors - the result is tested at the end. 1023 * 1024 * This assumes we are root so that we can do mkdir in a 1025 * mode 555 directory... 1026 */ 1027 while ((sp = strchr(sp + 1, '/'))) { 1028 *sp = '\0'; 1029 if (mkdir(p2, mode) < 0) { 1030 error_so_far = errno; 1031 } else { 1032 dlog("mkdir(%s)", p2); 1033 } 1034 *sp = '/'; 1035 } 1036 1037 if (mkdir(p2, mode) < 0) { 1038 error_so_far = errno; 1039 } else { 1040 dlog("mkdir(%s)", p2); 1041 } 1042 1043 XFREE(p2); 1044 1045 return stat(path, &stb) == 0 && 1046 (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far; 1047} 1048 1049 1050/* 1051 * Remove as many directories in the path as possible. 1052 * Give up if the directory doesn't appear to have 1053 * been created by Amd (not mode dr-x) or an rmdir 1054 * fails for any reason. 1055 */ 1056void 1057rmdirs(char *dir) 1058{ 1059 char *xdp = strdup(dir); 1060 char *dp; 1061 1062 do { 1063 struct stat stb; 1064 /* 1065 * Try to find out whether this was 1066 * created by amd. Do this by checking 1067 * for owner write permission. 1068 */ 1069 if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) { 1070 if (rmdir(xdp) < 0) { 1071 if (errno != ENOTEMPTY && 1072 errno != EBUSY && 1073 errno != EEXIST && 1074 errno != EROFS && 1075 errno != EINVAL) 1076 plog(XLOG_ERROR, "rmdir(%s): %m", xdp); 1077 break; 1078 } else { 1079 dlog("rmdir(%s)", xdp); 1080 } 1081 } else { 1082 break; 1083 } 1084 1085 dp = strrchr(xdp, '/'); 1086 if (dp) 1087 *dp = '\0'; 1088 } while (dp && dp > xdp); 1089 1090 XFREE(xdp); 1091} 1092