diff options
author | Pedro F. Giffuni <pfg@FreeBSD.org> | 2015-01-28 22:55:06 +0000 |
---|---|---|
committer | Pedro F. Giffuni <pfg@FreeBSD.org> | 2015-01-28 22:55:06 +0000 |
commit | e925d4a747df4a349c2baac436a71f606945baa5 (patch) | |
tree | e3a2ccc21623f0453c6f43f9016535205ba10d21 /libamu/xutil.c | |
parent | 509fcedcfec077a6070002f4059db0ae675be869 (diff) | |
download | src-e925d4a747df4a349c2baac436a71f606945baa5.tar.gz src-e925d4a747df4a349c2baac436a71f606945baa5.zip |
amd: flatten the tree
Notes
Notes:
svn path=/vendor/amd/dist/; revision=277863
Diffstat (limited to 'libamu/xutil.c')
-rw-r--r-- | libamu/xutil.c | 1091 |
1 files changed, 1091 insertions, 0 deletions
diff --git a/libamu/xutil.c b/libamu/xutil.c new file mode 100644 index 000000000000..3a33b9c0b149 --- /dev/null +++ b/libamu/xutil.c @@ -0,0 +1,1091 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/libamu/xutil.c + * + */ + +/* + * Miscellaneous Utilities: Logging, TTY, timers, signals, RPC, memory, etc. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amu.h> + +/* + * Logfp is the default logging device, and is initialized to stderr by + * default in dplog/plog below, and in + * amd/amfs_program.c:amfs_program_exec(). + */ +FILE *logfp = NULL; + +static char *am_progname = "unknown"; /* "amd" */ +static char am_hostname[MAXHOSTNAMELEN] = "unknown"; /* Hostname */ +pid_t am_mypid = -1; /* process ID */ +serv_state amd_state; /* amd's state */ +int foreground = 1; /* 1 == this is the top-level server */ +int debug_flags = 0; + +#ifdef HAVE_SYSLOG +int syslogging; +#endif /* HAVE_SYSLOG */ +int xlog_level = XLOG_ALL & ~XLOG_MAP & ~XLOG_STATS; +int xlog_level_init = ~0; +static int amd_program_number = AMQ_PROGRAM; + +#ifdef DEBUG_MEM +# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) +static int mem_bytes; +static int orig_mem_bytes; +# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ +#endif /* DEBUG_MEM */ + +/* forward definitions */ +/* for GCC format string auditing */ +static void real_plog(int lvl, const char *fmt, va_list vargs) + __attribute__((__format__(__printf__, 2, 0))); + + +#ifdef DEBUG +/* + * List of debug options. + */ +struct opt_tab dbg_opt[] = +{ + {"all", D_ALL}, /* All non-disruptive options */ + {"amq", D_AMQ}, /* Don't register for AMQ program */ + {"daemon", D_DAEMON}, /* Don't enter daemon mode */ + {"fork", D_FORK}, /* Don't fork server */ + {"full", D_FULL}, /* Program trace */ +#ifdef HAVE_CLOCK_GETTIME + {"hrtime", D_HRTIME}, /* Print high resolution time stamps */ +#endif /* HAVE_CLOCK_GETTIME */ + /* info service specific debugging (hesiod, nis, etc) */ + {"info", D_INFO}, + {"mem", D_MEM}, /* Trace memory allocations */ + {"mtab", D_MTAB}, /* Use local mtab file */ + {"readdir", D_READDIR}, /* Check on browsable_dirs progress */ + {"str", D_STR}, /* Debug string munging */ + {"test", D_TEST}, /* Full debug - no daemon, no amq, local mtab */ + {"trace", D_TRACE}, /* Protocol trace */ + {"xdrtrace", D_XDRTRACE}, /* Trace xdr routines */ + {0, 0} +}; +#endif /* DEBUG */ + +/* + * List of log options + */ +struct opt_tab xlog_opt[] = +{ + {"all", XLOG_ALL}, /* All messages */ +#ifdef DEBUG + {"debug", XLOG_DEBUG}, /* Debug messages */ +#endif /* DEBUG */ /* DEBUG */ + {"error", XLOG_ERROR}, /* Non-fatal system errors */ + {"fatal", XLOG_FATAL}, /* Fatal errors */ + {"info", XLOG_INFO}, /* Information */ + {"map", XLOG_MAP}, /* Map errors */ + {"stats", XLOG_STATS}, /* Additional statistical information */ + {"user", XLOG_USER}, /* Non-fatal user errors */ + {"warn", XLOG_WARNING}, /* Warnings */ + {"warning", XLOG_WARNING}, /* Warnings */ + {0, 0} +}; + + +void +am_set_progname(char *pn) +{ + am_progname = pn; +} + + +const char * +am_get_progname(void) +{ + return am_progname; +} + + +void +am_set_hostname(char *hn) +{ + xstrlcpy(am_hostname, hn, MAXHOSTNAMELEN); +} + + +const char * +am_get_hostname(void) +{ + return am_hostname; +} + + +pid_t +am_set_mypid(void) +{ + am_mypid = getpid(); + return am_mypid; +} + + +long +get_server_pid() +{ + return (long) (foreground ? am_mypid : getppid()); +} + + +voidp +xmalloc(int len) +{ + voidp p; + int retries = 600; + + /* + * Avoid malloc's which return NULL for malloc(0) + */ + if (len == 0) + len = 1; + + do { + p = (voidp) malloc((unsigned) len); + if (p) { + if (amuDebug(D_MEM)) + plog(XLOG_DEBUG, "Allocated size %d; block %p", len, p); + return p; + } + if (retries > 0) { + plog(XLOG_ERROR, "Retrying memory allocation"); + sleep(1); + } + } while (--retries); + + plog(XLOG_FATAL, "Out of memory"); + going_down(1); + + abort(); + + return 0; +} + + +/* like xmalloc, but zeros out the bytes */ +voidp +xzalloc(int len) +{ + voidp p = xmalloc(len); + + if (p) + memset(p, 0, len); + return p; +} + + +voidp +xrealloc(voidp ptr, int len) +{ + if (amuDebug(D_MEM)) + plog(XLOG_DEBUG, "Reallocated size %d; block %p", len, ptr); + + if (len == 0) + len = 1; + + if (ptr) + ptr = (voidp) realloc(ptr, (unsigned) len); + else + ptr = (voidp) xmalloc((unsigned) len); + + if (!ptr) { + plog(XLOG_FATAL, "Out of memory in realloc"); + going_down(1); + abort(); + } + return ptr; +} + + +#ifdef DEBUG_MEM +void +dxfree(char *file, int line, voidp ptr) +{ + if (amuDebug(D_MEM)) + plog(XLOG_DEBUG, "Free in %s:%d: block %p", file, line, ptr); + /* this is the only place that must NOT use XFREE()!!! */ + free(ptr); + ptr = NULL; /* paranoid */ +} + + +# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) +static void +checkup_mem(void) +{ + struct mallinfo mi = mallinfo(); + u_long uordbytes = mi.uordblks * 4096; + + if (mem_bytes != uordbytes) { + if (orig_mem_bytes == 0) + mem_bytes = orig_mem_bytes = uordbytes; + else { + fprintf(logfp, "%s[%ld]: ", am_get_progname(), (long) am_mypid); + if (mem_bytes < uordbytes) { + fprintf(logfp, "ALLOC: %ld bytes", uordbytes - mem_bytes); + } else { + fprintf(logfp, "FREE: %ld bytes", mem_bytes - uordbytes); + } + mem_bytes = uordbytes; + fprintf(logfp, ", making %d missing\n", mem_bytes - orig_mem_bytes); + } + } + malloc_verify(); +} +# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ +#endif /* DEBUG_MEM */ + + +/* + * Take a log format string and expand occurrences of %m + * with the current error code taken from errno. Make sure + * 'e' never gets longer than maxlen characters. + */ +static const char * +expand_error(const char *f, char *e, size_t maxlen) +{ + const char *p; + char *q; + int error = errno; + int len = 0; + + for (p = f, q = e; (*q = *p) && (size_t) len < maxlen; len++, q++, p++) { + if (p[0] == '%' && p[1] == 'm') { + xstrlcpy(q, strerror(error), maxlen); + len += strlen(q) - 1; + q += strlen(q) - 1; + p++; + } + } + e[maxlen-1] = '\0'; /* null terminate, to be sure */ + return e; +} + + +/* + * Output the time of day and hostname to the logfile + */ +static void +show_time_host_and_name(int lvl) +{ + static time_t last_t = 0; + static char *last_ctime = 0; + time_t t; +#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) + struct timespec ts; +#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */ + char nsecs[11]; /* '.' + 9 digits + '\0' */ + char *sev; + + nsecs[0] = '\0'; + +#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) + /* + * Some systems (AIX 4.3) seem to implement clock_gettime() as stub + * returning ENOSYS. + */ + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { + t = ts.tv_sec; + if (amuDebug(D_HRTIME)) + xsnprintf(nsecs, sizeof(nsecs), ".%09ld", ts.tv_nsec); + } + else +#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */ + t = clocktime(NULL); + + if (t != last_t) { + last_ctime = ctime(&t); + last_t = t; + } + + switch (lvl) { + case XLOG_FATAL: + sev = "fatal:"; + break; + case XLOG_ERROR: + sev = "error:"; + break; + case XLOG_USER: + sev = "user: "; + break; + case XLOG_WARNING: + sev = "warn: "; + break; + case XLOG_INFO: + sev = "info: "; + break; + case XLOG_DEBUG: + sev = "debug:"; + break; + case XLOG_MAP: + sev = "map: "; + break; + case XLOG_STATS: + sev = "stats:"; + break; + default: + sev = "hmm: "; + break; + } + fprintf(logfp, "%15.15s%s %s %s[%ld]/%s ", + last_ctime + 4, nsecs, am_get_hostname(), + am_get_progname(), + (long) am_mypid, + sev); +} + + +#ifdef DEBUG +/* + * Switch on/off debug options + */ +int +debug_option(char *opt) +{ + return cmdoption(opt, dbg_opt, &debug_flags); +} + + +void +dplog(const char *fmt, ...) +{ + va_list ap; + + if (!logfp) + logfp = stderr; /* initialize before possible first use */ + + va_start(ap, fmt); + real_plog(XLOG_DEBUG, fmt, ap); + va_end(ap); +} +#endif /* DEBUG */ + + +void +plog(int lvl, const char *fmt, ...) +{ + va_list ap; + + if (!logfp) + logfp = stderr; /* initialize before possible first use */ + + va_start(ap, fmt); + real_plog(lvl, fmt, ap); + va_end(ap); +} + + +static void +real_plog(int lvl, const char *fmt, va_list vargs) +{ + char msg[1024]; + char efmt[1024]; + char *ptr = msg; + static char last_msg[1024]; + static int last_count = 0, last_lvl = 0; + + if (!(xlog_level & lvl)) + return; + +#ifdef DEBUG_MEM +# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) + checkup_mem(); +# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ +#endif /* DEBUG_MEM */ + + /* + * Note: xvsnprintf() may call plog() if a truncation happened, but the + * latter has some code to break out of an infinite loop. See comment in + * xsnprintf() below. + */ + xvsnprintf(ptr, 1023, expand_error(fmt, efmt, 1024), vargs); + + ptr += strlen(ptr); + if (*(ptr-1) == '\n') + *--ptr = '\0'; + +#ifdef HAVE_SYSLOG + if (syslogging) { + switch (lvl) { /* from mike <mcooper@usc.edu> */ + case XLOG_FATAL: + lvl = LOG_CRIT; + break; + case XLOG_ERROR: + lvl = LOG_ERR; + break; + case XLOG_USER: + lvl = LOG_WARNING; + break; + case XLOG_WARNING: + lvl = LOG_WARNING; + break; + case XLOG_INFO: + lvl = LOG_INFO; + break; + case XLOG_DEBUG: + lvl = LOG_DEBUG; + break; + case XLOG_MAP: + lvl = LOG_DEBUG; + break; + case XLOG_STATS: + lvl = LOG_INFO; + break; + default: + lvl = LOG_ERR; + break; + } + syslog(lvl, "%s", msg); + return; + } +#endif /* HAVE_SYSLOG */ + + *ptr++ = '\n'; + *ptr = '\0'; + + /* + * mimic syslog behavior: only write repeated strings if they differ + */ + switch (last_count) { + case 0: /* never printed at all */ + last_count = 1; + if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */ + fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg); + last_lvl = lvl; + show_time_host_and_name(lvl); /* mimic syslog header */ + fwrite(msg, ptr - msg, 1, logfp); + fflush(logfp); + break; + + case 1: /* item printed once, if same, don't repeat */ + if (STREQ(last_msg, msg)) { + last_count++; + } else { /* last msg printed once, new one differs */ + /* last_count remains at 1 */ + if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */ + fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg); + last_lvl = lvl; + show_time_host_and_name(lvl); /* mimic syslog header */ + fwrite(msg, ptr - msg, 1, logfp); + fflush(logfp); + } + break; + + case 100: + /* + * Don't allow repetitions longer than 100, so you can see when something + * cycles like crazy. + */ + show_time_host_and_name(last_lvl); + xsnprintf(last_msg, sizeof(last_msg), + "last message repeated %d times\n", last_count); + fwrite(last_msg, strlen(last_msg), 1, logfp); + fflush(logfp); + last_count = 0; /* start from scratch */ + break; + + default: /* item repeated multiple times */ + if (STREQ(last_msg, msg)) { + last_count++; + } else { /* last msg repeated+skipped, new one differs */ + show_time_host_and_name(last_lvl); + xsnprintf(last_msg, sizeof(last_msg), + "last message repeated %d times\n", last_count); + fwrite(last_msg, strlen(last_msg), 1, logfp); + if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */ + fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg); + last_count = 1; + last_lvl = lvl; + show_time_host_and_name(lvl); /* mimic syslog header */ + fwrite(msg, ptr - msg, 1, logfp); + fflush(logfp); + } + break; + } + +} + + +/* + * Display current debug options + */ +void +show_opts(int ch, struct opt_tab *opts) +{ + int i; + int s = '{'; + + fprintf(stderr, "\t[-%c {no}", ch); + for (i = 0; opts[i].opt; i++) { + fprintf(stderr, "%c%s", s, opts[i].opt); + s = ','; + } + fputs("}]\n", stderr); +} + + +int +cmdoption(char *s, struct opt_tab *optb, int *flags) +{ + char *p = s; + int errs = 0; + + while (p && *p) { + int neg; + char *opt; + struct opt_tab *dp, *dpn = 0; + + s = p; + p = strchr(p, ','); + if (p) + *p = '\0'; + + /* check for "no" prefix to options */ + if (s[0] == 'n' && s[1] == 'o') { + opt = s + 2; + neg = 1; + } else { + opt = s; + neg = 0; + } + + /* + * Scan the array of debug options to find the + * corresponding flag value. If it is found + * then set (or clear) the flag (depending on + * whether the option was prefixed with "no"). + */ + for (dp = optb; dp->opt; dp++) { + if (STREQ(opt, dp->opt)) + break; + if (opt != s && !dpn && STREQ(s, dp->opt)) + dpn = dp; + } + + if (dp->opt || dpn) { + if (!dp->opt) { + dp = dpn; + neg = !neg; + } + if (neg) + *flags &= ~dp->flag; + else + *flags |= dp->flag; + } else { + /* + * This will log to stderr when parsing the command line + * since any -l option will not yet have taken effect. + */ + plog(XLOG_USER, "option \"%s\" not recognized", s); + errs++; + } + + /* + * Put the comma back + */ + if (p) + *p++ = ','; + } + + return errs; +} + + +/* + * Switch on/off logging options + */ +int +switch_option(char *opt) +{ + int xl = xlog_level; + int rc = cmdoption(opt, xlog_opt, &xl); + + if (rc) { + rc = EINVAL; + } else { + /* + * Keep track of initial log level, and + * don't allow options to be turned off. + */ + if (xlog_level_init == ~0) + xlog_level_init = xl; + else + xl |= xlog_level_init; + xlog_level = xl; + } + return rc; +} + + +#ifdef LOG_DAEMON +/* + * get syslog facility to use. + * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc. + */ +static int +get_syslog_facility(const char *logfile) +{ + char *facstr; + + /* parse facility string */ + facstr = strchr(logfile, ':'); + if (!facstr) /* log file was "syslog" */ + return LOG_DAEMON; + facstr++; + if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */ + plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON"); + return LOG_DAEMON; + } + +#ifdef LOG_KERN + if (STREQ(facstr, "kern")) + return LOG_KERN; +#endif /* not LOG_KERN */ +#ifdef LOG_USER + if (STREQ(facstr, "user")) + return LOG_USER; +#endif /* not LOG_USER */ +#ifdef LOG_MAIL + if (STREQ(facstr, "mail")) + return LOG_MAIL; +#endif /* not LOG_MAIL */ + + if (STREQ(facstr, "daemon")) + return LOG_DAEMON; + +#ifdef LOG_AUTH + if (STREQ(facstr, "auth")) + return LOG_AUTH; +#endif /* not LOG_AUTH */ +#ifdef LOG_SYSLOG + if (STREQ(facstr, "syslog")) + return LOG_SYSLOG; +#endif /* not LOG_SYSLOG */ +#ifdef LOG_LPR + if (STREQ(facstr, "lpr")) + return LOG_LPR; +#endif /* not LOG_LPR */ +#ifdef LOG_NEWS + if (STREQ(facstr, "news")) + return LOG_NEWS; +#endif /* not LOG_NEWS */ +#ifdef LOG_UUCP + if (STREQ(facstr, "uucp")) + return LOG_UUCP; +#endif /* not LOG_UUCP */ +#ifdef LOG_CRON + if (STREQ(facstr, "cron")) + return LOG_CRON; +#endif /* not LOG_CRON */ +#ifdef LOG_LOCAL0 + if (STREQ(facstr, "local0")) + return LOG_LOCAL0; +#endif /* not LOG_LOCAL0 */ +#ifdef LOG_LOCAL1 + if (STREQ(facstr, "local1")) + return LOG_LOCAL1; +#endif /* not LOG_LOCAL1 */ +#ifdef LOG_LOCAL2 + if (STREQ(facstr, "local2")) + return LOG_LOCAL2; +#endif /* not LOG_LOCAL2 */ +#ifdef LOG_LOCAL3 + if (STREQ(facstr, "local3")) + return LOG_LOCAL3; +#endif /* not LOG_LOCAL3 */ +#ifdef LOG_LOCAL4 + if (STREQ(facstr, "local4")) + return LOG_LOCAL4; +#endif /* not LOG_LOCAL4 */ +#ifdef LOG_LOCAL5 + if (STREQ(facstr, "local5")) + return LOG_LOCAL5; +#endif /* not LOG_LOCAL5 */ +#ifdef LOG_LOCAL6 + if (STREQ(facstr, "local6")) + return LOG_LOCAL6; +#endif /* not LOG_LOCAL6 */ +#ifdef LOG_LOCAL7 + if (STREQ(facstr, "local7")) + return LOG_LOCAL7; +#endif /* not LOG_LOCAL7 */ + + /* didn't match anything else */ + plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr); + return LOG_DAEMON; +} +#endif /* not LOG_DAEMON */ + + +/* + * Change current logfile + */ +int +switch_to_logfile(char *logfile, int old_umask, int truncate_log) +{ + FILE *new_logfp = stderr; + + if (logfile) { +#ifdef HAVE_SYSLOG + syslogging = 0; +#endif /* HAVE_SYSLOG */ + + if (STREQ(logfile, "/dev/stderr")) + new_logfp = stderr; + else if (NSTREQ(logfile, "syslog", strlen("syslog"))) { + +#ifdef HAVE_SYSLOG + syslogging = 1; + new_logfp = stderr; + openlog(am_get_progname(), + LOG_PID +# ifdef LOG_NOWAIT + | LOG_NOWAIT +# endif /* LOG_NOWAIT */ +# ifdef LOG_DAEMON + , get_syslog_facility(logfile) +# endif /* LOG_DAEMON */ + ); +#else /* not HAVE_SYSLOG */ + plog(XLOG_WARNING, "syslog option not supported, logging unchanged"); +#endif /* not HAVE_SYSLOG */ + + } else { /* regular log file */ + (void) umask(old_umask); + if (truncate_log) + truncate(logfile, 0); + new_logfp = fopen(logfile, "a"); + umask(0); + } + } + + /* + * If we couldn't open a new file, then continue using the old. + */ + if (!new_logfp && logfile) { + plog(XLOG_USER, "%s: Can't open logfile: %m", logfile); + return 1; + } + + /* + * Close the previous file + */ + if (logfp && logfp != stderr) + (void) fclose(logfp); + logfp = new_logfp; + + if (logfile) + plog(XLOG_INFO, "switched to logfile \"%s\"", logfile); + else + plog(XLOG_INFO, "no logfile defined; using stderr"); + + return 0; +} + + +void +unregister_amq(void) +{ + if (!amuDebug(D_AMQ)) { + /* find which instance of amd to unregister */ + u_long amd_prognum = get_amd_program_number(); + + if (pmap_unset(amd_prognum, AMQ_VERSION) != 1) + dlog("failed to de-register Amd program %lu, version %lu", + amd_prognum, AMQ_VERSION); + } +} + + +void +going_down(int rc) +{ + if (foreground) { + if (amd_state != Start) { + if (amd_state != Done) + return; + unregister_amq(); + } + } + +#ifdef MOUNT_TABLE_ON_FILE + /* + * Call unlock_mntlist to free any important resources such as an on-disk + * lock file (/etc/mtab~). + */ + unlock_mntlist(); +#endif /* MOUNT_TABLE_ON_FILE */ + + if (foreground) { + plog(XLOG_INFO, "Finishing with status %d", rc); + } else { + dlog("background process exiting with status %d", rc); + } + /* bye bye... */ + exit(rc); +} + + +/* return the rpc program number under which amd was used */ +int +get_amd_program_number(void) +{ + return amd_program_number; +} + + +/* set the rpc program number used for amd */ +void +set_amd_program_number(int program) +{ + amd_program_number = program; +} + + +/* + * Release the controlling tty of the process pid. + * + * Algorithm: try these in order, if available, until one of them + * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0). + * Do not use setpgid(): on some OSs it may release the controlling tty, + * even if the man page does not mention it, but on other OSs it does not. + * Also avoid setpgrp(): it works on some systems, and on others it is + * identical to setpgid(). + */ +void +amu_release_controlling_tty(void) +{ + int fd; + + /* + * In daemon mode, leaving open file descriptors to terminals or pipes + * can be a really bad idea. + * Case in point: the redhat startup script calls us through their 'initlog' + * program, which exits as soon as the original amd process exits. If, + * at some point, a misbehaved library function decides to print something + * to the screen, we get a SIGPIPE and die. + * And guess what: NIS glibc functions will attempt to print to stderr + * "YPBINDPROC_DOMAIN: Domain not bound" if ypbind is running but can't find + * a ypserver. + * + * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then + * reopen them as /dev/null. + * + * XXX We should also probably set the SIGPIPE handler to SIG_IGN. + */ + fd = open("/dev/null", O_RDWR); + if (fd < 0) { + plog(XLOG_WARNING, "Could not open /dev/null for rw: %m"); + } else { + fflush(stdin); close(0); dup2(fd, 0); + fflush(stdout); close(1); dup2(fd, 1); + fflush(stderr); close(2); dup2(fd, 2); + close(fd); + } + +#ifdef HAVE_SETSID + /* XXX: one day maybe use vhangup(2) */ + if (setsid() < 0) { + plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m"); + } else { + plog(XLOG_INFO, "released controlling tty using setsid()"); + return; + } +#endif /* HAVE_SETSID */ + +#ifdef TIOCNOTTY + fd = open("/dev/tty", O_RDWR); + if (fd < 0) { + /* not an error if already no controlling tty */ + if (errno != ENXIO) + plog(XLOG_WARNING, "Could not open controlling tty: %m"); + } else { + if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY) + plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m"); + else + plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)"); + close(fd); + } + return; +#endif /* not TIOCNOTTY */ + + plog(XLOG_ERROR, "unable to release controlling tty"); +} + + +/* setup a single signal handler */ +void +setup_sighandler(int signum, void (*handler)(int)) +{ +#ifdef HAVE_SIGACTION + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = 0; /* unnecessary */ + sa.sa_handler = handler; + sigemptyset(&(sa.sa_mask)); /* probably unnecessary too */ + sigaddset(&(sa.sa_mask), signum); + sigaction(signum, &sa, NULL); +#else /* not HAVE_SIGACTION */ + (void) signal(signum, handler); +#endif /* not HAVE_SIGACTION */ +} + + +/* + * Return current time in seconds. If passed a non-null argyument, then + * fill it in with the current time in seconds and microseconds (useful + * for mtime updates). + */ +time_t +clocktime(nfstime *nt) +{ + static struct timeval now; /* keep last time, as default */ + + if (gettimeofday(&now, NULL) < 0) { + plog(XLOG_ERROR, "clocktime: gettimeofday: %m"); + /* hack: force time to have incremented by at least 1 second */ + now.tv_sec++; + } + /* copy seconds and microseconds. may demote a long to an int */ + if (nt) { + nt->nt_seconds = (u_int) now.tv_sec; + nt->nt_useconds = (u_int) now.tv_usec; + } + return (time_t) now.tv_sec; +} + + +/* + * Make all the directories in the path. + */ +int +mkdirs(char *path, int mode) +{ + /* + * take a copy in case path is in readonly store + */ + char *p2 = strdup(path); + char *sp = p2; + struct stat stb; + int error_so_far = 0; + + /* + * Skip through the string make the directories. + * Mostly ignore errors - the result is tested at the end. + * + * This assumes we are root so that we can do mkdir in a + * mode 555 directory... + */ + while ((sp = strchr(sp + 1, '/'))) { + *sp = '\0'; + if (mkdir(p2, mode) < 0) { + error_so_far = errno; + } else { + dlog("mkdir(%s)", p2); + } + *sp = '/'; + } + + if (mkdir(p2, mode) < 0) { + error_so_far = errno; + } else { + dlog("mkdir(%s)", p2); + } + + XFREE(p2); + + return stat(path, &stb) == 0 && + (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far; +} + + +/* + * Remove as many directories in the path as possible. + * Give up if the directory doesn't appear to have + * been created by Amd (not mode dr-x) or an rmdir + * fails for any reason. + */ +void +rmdirs(char *dir) +{ + char *xdp = strdup(dir); + char *dp; + + do { + struct stat stb; + /* + * Try to find out whether this was + * created by amd. Do this by checking + * for owner write permission. + */ + if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) { + if (rmdir(xdp) < 0) { + if (errno != ENOTEMPTY && + errno != EBUSY && + errno != EEXIST && + errno != EROFS && + errno != EINVAL) + plog(XLOG_ERROR, "rmdir(%s): %m", xdp); + break; + } else { + dlog("rmdir(%s)", xdp); + } + } else { + break; + } + + dp = strrchr(xdp, '/'); + if (dp) + *dp = '\0'; + } while (dp && dp > xdp); + + XFREE(xdp); +} |