138494Sobrien/* 2174294Sobrien * Copyright (c) 1997-2006 Erez Zadok 338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry 438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 538494Sobrien * Copyright (c) 1990 The Regents of the University of California. 638494Sobrien * All rights reserved. 738494Sobrien * 838494Sobrien * This code is derived from software contributed to Berkeley by 938494Sobrien * Jan-Simon Pendry at Imperial College, London. 1038494Sobrien * 1138494Sobrien * Redistribution and use in source and binary forms, with or without 1238494Sobrien * modification, are permitted provided that the following conditions 1338494Sobrien * are met: 1438494Sobrien * 1. Redistributions of source code must retain the above copyright 1538494Sobrien * notice, this list of conditions and the following disclaimer. 1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1738494Sobrien * notice, this list of conditions and the following disclaimer in the 1838494Sobrien * documentation and/or other materials provided with the distribution. 1938494Sobrien * 3. All advertising materials mentioning features or use of this software 2042629Sobrien * must display the following acknowledgment: 2138494Sobrien * This product includes software developed by the University of 2238494Sobrien * California, Berkeley and its contributors. 2338494Sobrien * 4. Neither the name of the University nor the names of its contributors 2438494Sobrien * may be used to endorse or promote products derived from this software 2538494Sobrien * without specific prior written permission. 2638494Sobrien * 2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3038494Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3738494Sobrien * SUCH DAMAGE. 3838494Sobrien * 3938494Sobrien * 40174294Sobrien * File: am-utils/libamu/xutil.c 4138494Sobrien * 4238494Sobrien */ 4338494Sobrien 44174294Sobrien/* 45174294Sobrien * Miscellaneous Utilities: Logging, TTY, timers, signals, RPC, memory, etc. 46174294Sobrien */ 47174294Sobrien 4838494Sobrien#ifdef HAVE_CONFIG_H 4938494Sobrien# include <config.h> 5038494Sobrien#endif /* HAVE_CONFIG_H */ 5138494Sobrien#include <am_defs.h> 5238494Sobrien#include <amu.h> 5338494Sobrien 5451292Sobrien/* 5551292Sobrien * Logfp is the default logging device, and is initialized to stderr by 5651292Sobrien * default in dplog/plog below, and in 5751292Sobrien * amd/amfs_program.c:amfs_program_exec(). 5851292Sobrien */ 5951292SobrienFILE *logfp = NULL; 6038494Sobrien 6142629Sobrienstatic char *am_progname = "unknown"; /* "amd" */ 62174294Sobrienstatic char am_hostname[MAXHOSTNAMELEN] = "unknown"; /* Hostname */ 6342629Sobrienpid_t am_mypid = -1; /* process ID */ 6442629Sobrienserv_state amd_state; /* amd's state */ 6542629Sobrienint foreground = 1; /* 1 == this is the top-level server */ 6642629Sobrienint debug_flags = 0; 6742629Sobrien 6838494Sobrien#ifdef HAVE_SYSLOG 6938494Sobrienint syslogging; 7038494Sobrien#endif /* HAVE_SYSLOG */ 7138494Sobrienint xlog_level = XLOG_ALL & ~XLOG_MAP & ~XLOG_STATS; 7238494Sobrienint xlog_level_init = ~0; 7338494Sobrienstatic int amd_program_number = AMQ_PROGRAM; 7438494Sobrien 7538494Sobrien#ifdef DEBUG_MEM 76174294Sobrien# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) 7738494Sobrienstatic int mem_bytes; 7838494Sobrienstatic int orig_mem_bytes; 79174294Sobrien# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ 8038494Sobrien#endif /* DEBUG_MEM */ 8138494Sobrien 8238494Sobrien/* forward definitions */ 83119679Smbr/* for GCC format string auditing */ 8482794Sobrienstatic void real_plog(int lvl, const char *fmt, va_list vargs) 8582794Sobrien __attribute__((__format__(__printf__, 2, 0))); 8638494Sobrien 87119679Smbr 8838494Sobrien#ifdef DEBUG 8938494Sobrien/* 9038494Sobrien * List of debug options. 9138494Sobrien */ 9238494Sobrienstruct opt_tab dbg_opt[] = 9338494Sobrien{ 94174294Sobrien {"all", D_ALL}, /* All non-disruptive options */ 95174294Sobrien {"amq", D_AMQ}, /* Don't register for AMQ program */ 96174294Sobrien {"daemon", D_DAEMON}, /* Don't enter daemon mode */ 97174294Sobrien {"fork", D_FORK}, /* Don't fork server */ 9838494Sobrien {"full", D_FULL}, /* Program trace */ 9982794Sobrien#ifdef HAVE_CLOCK_GETTIME 10082794Sobrien {"hrtime", D_HRTIME}, /* Print high resolution time stamps */ 10182794Sobrien#endif /* HAVE_CLOCK_GETTIME */ 10238494Sobrien /* info service specific debugging (hesiod, nis, etc) */ 10338494Sobrien {"info", D_INFO}, 10438494Sobrien {"mem", D_MEM}, /* Trace memory allocations */ 10538494Sobrien {"mtab", D_MTAB}, /* Use local mtab file */ 106174294Sobrien {"readdir", D_READDIR}, /* Check on browsable_dirs progress */ 10738494Sobrien {"str", D_STR}, /* Debug string munging */ 108174294Sobrien {"test", D_TEST}, /* Full debug - no daemon, no amq, local mtab */ 10938494Sobrien {"trace", D_TRACE}, /* Protocol trace */ 11082794Sobrien {"xdrtrace", D_XDRTRACE}, /* Trace xdr routines */ 11138494Sobrien {0, 0} 11238494Sobrien}; 11338494Sobrien#endif /* DEBUG */ 11438494Sobrien 11538494Sobrien/* 11638494Sobrien * List of log options 11738494Sobrien */ 11838494Sobrienstruct opt_tab xlog_opt[] = 11938494Sobrien{ 12038494Sobrien {"all", XLOG_ALL}, /* All messages */ 12138494Sobrien#ifdef DEBUG 12238494Sobrien {"debug", XLOG_DEBUG}, /* Debug messages */ 12338494Sobrien#endif /* DEBUG */ /* DEBUG */ 12438494Sobrien {"error", XLOG_ERROR}, /* Non-fatal system errors */ 12538494Sobrien {"fatal", XLOG_FATAL}, /* Fatal errors */ 12638494Sobrien {"info", XLOG_INFO}, /* Information */ 12738494Sobrien {"map", XLOG_MAP}, /* Map errors */ 12838494Sobrien {"stats", XLOG_STATS}, /* Additional statistical information */ 12938494Sobrien {"user", XLOG_USER}, /* Non-fatal user errors */ 13038494Sobrien {"warn", XLOG_WARNING}, /* Warnings */ 13138494Sobrien {"warning", XLOG_WARNING}, /* Warnings */ 13238494Sobrien {0, 0} 13338494Sobrien}; 13438494Sobrien 13538494Sobrien 13642629Sobrienvoid 13742629Sobrienam_set_progname(char *pn) 13842629Sobrien{ 13942629Sobrien am_progname = pn; 14042629Sobrien} 14142629Sobrien 14242629Sobrien 14342629Sobrienconst char * 14442629Sobrienam_get_progname(void) 14542629Sobrien{ 14642629Sobrien return am_progname; 14742629Sobrien} 14842629Sobrien 14942629Sobrien 15042629Sobrienvoid 15142629Sobrienam_set_hostname(char *hn) 15242629Sobrien{ 153174294Sobrien xstrlcpy(am_hostname, hn, MAXHOSTNAMELEN); 15442629Sobrien} 15542629Sobrien 15642629Sobrien 15742629Sobrienconst char * 15842629Sobrienam_get_hostname(void) 15942629Sobrien{ 16042629Sobrien return am_hostname; 16142629Sobrien} 16242629Sobrien 16342629Sobrien 16442629Sobrienpid_t 16542629Sobrienam_set_mypid(void) 16642629Sobrien{ 16742629Sobrien am_mypid = getpid(); 16842629Sobrien return am_mypid; 16942629Sobrien} 17042629Sobrien 17142629Sobrien 172174294Sobrienlong 173174294Sobrienget_server_pid() 174174294Sobrien{ 175174294Sobrien return (long) (foreground ? am_mypid : getppid()); 176174294Sobrien} 177174294Sobrien 178174294Sobrien 17938494Sobrienvoidp 18038494Sobrienxmalloc(int len) 18138494Sobrien{ 18238494Sobrien voidp p; 18338494Sobrien int retries = 600; 18438494Sobrien 18538494Sobrien /* 18638494Sobrien * Avoid malloc's which return NULL for malloc(0) 18738494Sobrien */ 18838494Sobrien if (len == 0) 18938494Sobrien len = 1; 19038494Sobrien 19138494Sobrien do { 19238494Sobrien p = (voidp) malloc((unsigned) len); 19338494Sobrien if (p) { 194174294Sobrien if (amuDebug(D_MEM)) 195174294Sobrien plog(XLOG_DEBUG, "Allocated size %d; block %p", len, p); 19638494Sobrien return p; 19738494Sobrien } 19838494Sobrien if (retries > 0) { 19938494Sobrien plog(XLOG_ERROR, "Retrying memory allocation"); 20038494Sobrien sleep(1); 20138494Sobrien } 20238494Sobrien } while (--retries); 20338494Sobrien 20438494Sobrien plog(XLOG_FATAL, "Out of memory"); 20538494Sobrien going_down(1); 20638494Sobrien 20738494Sobrien abort(); 20838494Sobrien 20938494Sobrien return 0; 21038494Sobrien} 21138494Sobrien 21238494Sobrien 21338494Sobrien/* like xmalloc, but zeros out the bytes */ 21438494Sobrienvoidp 21538494Sobrienxzalloc(int len) 21638494Sobrien{ 21738494Sobrien voidp p = xmalloc(len); 21838494Sobrien 21938494Sobrien if (p) 22038494Sobrien memset(p, 0, len); 22138494Sobrien return p; 22238494Sobrien} 22338494Sobrien 22438494Sobrien 22538494Sobrienvoidp 22638494Sobrienxrealloc(voidp ptr, int len) 22738494Sobrien{ 228174294Sobrien if (amuDebug(D_MEM)) 229174294Sobrien plog(XLOG_DEBUG, "Reallocated size %d; block %p", len, ptr); 23038494Sobrien 23138494Sobrien if (len == 0) 23238494Sobrien len = 1; 23338494Sobrien 23438494Sobrien if (ptr) 23538494Sobrien ptr = (voidp) realloc(ptr, (unsigned) len); 23638494Sobrien else 23738494Sobrien ptr = (voidp) xmalloc((unsigned) len); 23838494Sobrien 23938494Sobrien if (!ptr) { 24038494Sobrien plog(XLOG_FATAL, "Out of memory in realloc"); 24138494Sobrien going_down(1); 24238494Sobrien abort(); 24338494Sobrien } 24438494Sobrien return ptr; 24538494Sobrien} 24638494Sobrien 24738494Sobrien 248174294Sobrien#ifdef DEBUG_MEM 24938494Sobrienvoid 25038494Sobriendxfree(char *file, int line, voidp ptr) 25138494Sobrien{ 252174294Sobrien if (amuDebug(D_MEM)) 253174294Sobrien plog(XLOG_DEBUG, "Free in %s:%d: block %p", file, line, ptr); 25438494Sobrien /* this is the only place that must NOT use XFREE()!!! */ 25538494Sobrien free(ptr); 25638494Sobrien ptr = NULL; /* paranoid */ 25738494Sobrien} 25838494Sobrien 25938494Sobrien 260174294Sobrien# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) 26138494Sobrienstatic void 26238494Sobriencheckup_mem(void) 26338494Sobrien{ 26438494Sobrien struct mallinfo mi = mallinfo(); 26538494Sobrien u_long uordbytes = mi.uordblks * 4096; 26638494Sobrien 26738494Sobrien if (mem_bytes != uordbytes) { 26838494Sobrien if (orig_mem_bytes == 0) 26938494Sobrien mem_bytes = orig_mem_bytes = uordbytes; 27038494Sobrien else { 27142629Sobrien fprintf(logfp, "%s[%ld]: ", am_get_progname(), (long) am_mypid); 27238494Sobrien if (mem_bytes < uordbytes) { 27338494Sobrien fprintf(logfp, "ALLOC: %ld bytes", uordbytes - mem_bytes); 27438494Sobrien } else { 27538494Sobrien fprintf(logfp, "FREE: %ld bytes", mem_bytes - uordbytes); 27638494Sobrien } 27738494Sobrien mem_bytes = uordbytes; 27838494Sobrien fprintf(logfp, ", making %d missing\n", mem_bytes - orig_mem_bytes); 27938494Sobrien } 28038494Sobrien } 28138494Sobrien malloc_verify(); 28238494Sobrien} 283174294Sobrien# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ 28438494Sobrien#endif /* DEBUG_MEM */ 28538494Sobrien 28638494Sobrien 28738494Sobrien/* 28842629Sobrien * Take a log format string and expand occurrences of %m 28951292Sobrien * with the current error code taken from errno. Make sure 29051292Sobrien * 'e' never gets longer than maxlen characters. 29138494Sobrien */ 29282794Sobrienstatic const char * 293174294Sobrienexpand_error(const char *f, char *e, size_t maxlen) 29438494Sobrien{ 29582794Sobrien const char *p; 29682794Sobrien char *q; 29738494Sobrien int error = errno; 29851292Sobrien int len = 0; 29938494Sobrien 300174294Sobrien for (p = f, q = e; (*q = *p) && (size_t) len < maxlen; len++, q++, p++) { 30138494Sobrien if (p[0] == '%' && p[1] == 'm') { 302174294Sobrien xstrlcpy(q, strerror(error), maxlen); 30351292Sobrien len += strlen(q) - 1; 30451292Sobrien q += strlen(q) - 1; 30538494Sobrien p++; 30638494Sobrien } 30738494Sobrien } 30851292Sobrien e[maxlen-1] = '\0'; /* null terminate, to be sure */ 30982794Sobrien return e; 31038494Sobrien} 31138494Sobrien 31238494Sobrien 31338494Sobrien/* 31438494Sobrien * Output the time of day and hostname to the logfile 31538494Sobrien */ 31638494Sobrienstatic void 31738494Sobrienshow_time_host_and_name(int lvl) 31838494Sobrien{ 31938494Sobrien static time_t last_t = 0; 32038494Sobrien static char *last_ctime = 0; 32182794Sobrien time_t t; 322174294Sobrien#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) 32382794Sobrien struct timespec ts; 324174294Sobrien#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */ 325174294Sobrien char nsecs[11]; /* '.' + 9 digits + '\0' */ 32638494Sobrien char *sev; 32738494Sobrien 328174294Sobrien nsecs[0] = '\0'; 329174294Sobrien 330174294Sobrien#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) 33182794Sobrien /* 33282794Sobrien * Some systems (AIX 4.3) seem to implement clock_gettime() as stub 33382794Sobrien * returning ENOSYS. 33482794Sobrien */ 33582794Sobrien if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { 33682794Sobrien t = ts.tv_sec; 337174294Sobrien if (amuDebug(D_HRTIME)) 338174294Sobrien xsnprintf(nsecs, sizeof(nsecs), ".%09ld", ts.tv_nsec); 33982794Sobrien } 34082794Sobrien else 341174294Sobrien#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */ 342174294Sobrien t = clocktime(NULL); 34382794Sobrien 34438494Sobrien if (t != last_t) { 34538494Sobrien last_ctime = ctime(&t); 34638494Sobrien last_t = t; 34738494Sobrien } 34882794Sobrien 34938494Sobrien switch (lvl) { 35038494Sobrien case XLOG_FATAL: 35138494Sobrien sev = "fatal:"; 35238494Sobrien break; 35338494Sobrien case XLOG_ERROR: 35438494Sobrien sev = "error:"; 35538494Sobrien break; 35638494Sobrien case XLOG_USER: 35738494Sobrien sev = "user: "; 35838494Sobrien break; 35938494Sobrien case XLOG_WARNING: 36038494Sobrien sev = "warn: "; 36138494Sobrien break; 36238494Sobrien case XLOG_INFO: 36338494Sobrien sev = "info: "; 36438494Sobrien break; 36538494Sobrien case XLOG_DEBUG: 36638494Sobrien sev = "debug:"; 36738494Sobrien break; 36838494Sobrien case XLOG_MAP: 36938494Sobrien sev = "map: "; 37038494Sobrien break; 37138494Sobrien case XLOG_STATS: 37238494Sobrien sev = "stats:"; 37338494Sobrien break; 37438494Sobrien default: 37538494Sobrien sev = "hmm: "; 37638494Sobrien break; 37738494Sobrien } 37882794Sobrien fprintf(logfp, "%15.15s%s %s %s[%ld]/%s ", 37982794Sobrien last_ctime + 4, nsecs, am_get_hostname(), 38042629Sobrien am_get_progname(), 38142629Sobrien (long) am_mypid, 38238494Sobrien sev); 38338494Sobrien} 38438494Sobrien 38538494Sobrien 38638494Sobrien#ifdef DEBUG 38738494Sobrien/* 38838494Sobrien * Switch on/off debug options 38938494Sobrien */ 39038494Sobrienint 39138494Sobriendebug_option(char *opt) 39238494Sobrien{ 39338494Sobrien return cmdoption(opt, dbg_opt, &debug_flags); 39438494Sobrien} 39538494Sobrien 39638494Sobrien 39738494Sobrienvoid 39882794Sobriendplog(const char *fmt, ...) 39938494Sobrien{ 40038494Sobrien va_list ap; 40138494Sobrien 40251292Sobrien if (!logfp) 40351292Sobrien logfp = stderr; /* initialize before possible first use */ 40451292Sobrien 40538494Sobrien va_start(ap, fmt); 40638494Sobrien real_plog(XLOG_DEBUG, fmt, ap); 40738494Sobrien va_end(ap); 40838494Sobrien} 40938494Sobrien#endif /* DEBUG */ 41038494Sobrien 41138494Sobrien 41238494Sobrienvoid 41382794Sobrienplog(int lvl, const char *fmt, ...) 41438494Sobrien{ 41538494Sobrien va_list ap; 41638494Sobrien 41751292Sobrien if (!logfp) 41851292Sobrien logfp = stderr; /* initialize before possible first use */ 41951292Sobrien 42038494Sobrien va_start(ap, fmt); 42138494Sobrien real_plog(lvl, fmt, ap); 42238494Sobrien va_end(ap); 42338494Sobrien} 42438494Sobrien 42538494Sobrien 42638494Sobrienstatic void 42782794Sobrienreal_plog(int lvl, const char *fmt, va_list vargs) 42838494Sobrien{ 42938494Sobrien char msg[1024]; 43038494Sobrien char efmt[1024]; 43138494Sobrien char *ptr = msg; 43238494Sobrien static char last_msg[1024]; 43338494Sobrien static int last_count = 0, last_lvl = 0; 43438494Sobrien 43538494Sobrien if (!(xlog_level & lvl)) 43638494Sobrien return; 43738494Sobrien 43838494Sobrien#ifdef DEBUG_MEM 439174294Sobrien# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) 44038494Sobrien checkup_mem(); 441174294Sobrien# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ 44238494Sobrien#endif /* DEBUG_MEM */ 44338494Sobrien 44482794Sobrien /* 445174294Sobrien * Note: xvsnprintf() may call plog() if a truncation happened, but the 446174294Sobrien * latter has some code to break out of an infinite loop. See comment in 447174294Sobrien * xsnprintf() below. 44882794Sobrien */ 449174294Sobrien xvsnprintf(ptr, 1023, expand_error(fmt, efmt, 1024), vargs); 45038494Sobrien 45138494Sobrien ptr += strlen(ptr); 452174294Sobrien if (*(ptr-1) == '\n') 45338494Sobrien *--ptr = '\0'; 45438494Sobrien 45538494Sobrien#ifdef HAVE_SYSLOG 45638494Sobrien if (syslogging) { 45738494Sobrien switch (lvl) { /* from mike <mcooper@usc.edu> */ 45838494Sobrien case XLOG_FATAL: 45938494Sobrien lvl = LOG_CRIT; 46038494Sobrien break; 46138494Sobrien case XLOG_ERROR: 46238494Sobrien lvl = LOG_ERR; 46338494Sobrien break; 46438494Sobrien case XLOG_USER: 46538494Sobrien lvl = LOG_WARNING; 46638494Sobrien break; 46738494Sobrien case XLOG_WARNING: 46838494Sobrien lvl = LOG_WARNING; 46938494Sobrien break; 47038494Sobrien case XLOG_INFO: 47138494Sobrien lvl = LOG_INFO; 47238494Sobrien break; 47338494Sobrien case XLOG_DEBUG: 47438494Sobrien lvl = LOG_DEBUG; 47538494Sobrien break; 47638494Sobrien case XLOG_MAP: 47738494Sobrien lvl = LOG_DEBUG; 47838494Sobrien break; 47938494Sobrien case XLOG_STATS: 48038494Sobrien lvl = LOG_INFO; 48138494Sobrien break; 48238494Sobrien default: 48338494Sobrien lvl = LOG_ERR; 48438494Sobrien break; 48538494Sobrien } 48638494Sobrien syslog(lvl, "%s", msg); 48738494Sobrien return; 48838494Sobrien } 48938494Sobrien#endif /* HAVE_SYSLOG */ 49038494Sobrien 49138494Sobrien *ptr++ = '\n'; 49238494Sobrien *ptr = '\0'; 49338494Sobrien 49438494Sobrien /* 49538494Sobrien * mimic syslog behavior: only write repeated strings if they differ 49638494Sobrien */ 49738494Sobrien switch (last_count) { 49838494Sobrien case 0: /* never printed at all */ 49938494Sobrien last_count = 1; 500174294Sobrien if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */ 501174294Sobrien fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg); 50238494Sobrien last_lvl = lvl; 50338494Sobrien show_time_host_and_name(lvl); /* mimic syslog header */ 50438494Sobrien fwrite(msg, ptr - msg, 1, logfp); 50538494Sobrien fflush(logfp); 50638494Sobrien break; 50738494Sobrien 50838494Sobrien case 1: /* item printed once, if same, don't repeat */ 50938494Sobrien if (STREQ(last_msg, msg)) { 51038494Sobrien last_count++; 51138494Sobrien } else { /* last msg printed once, new one differs */ 51238494Sobrien /* last_count remains at 1 */ 513174294Sobrien if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */ 514174294Sobrien fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg); 51538494Sobrien last_lvl = lvl; 51638494Sobrien show_time_host_and_name(lvl); /* mimic syslog header */ 51738494Sobrien fwrite(msg, ptr - msg, 1, logfp); 51838494Sobrien fflush(logfp); 51938494Sobrien } 52038494Sobrien break; 52138494Sobrien 52238494Sobrien case 100: 52338494Sobrien /* 52438494Sobrien * Don't allow repetitions longer than 100, so you can see when something 52538494Sobrien * cycles like crazy. 52638494Sobrien */ 52738494Sobrien show_time_host_and_name(last_lvl); 528174294Sobrien xsnprintf(last_msg, sizeof(last_msg), 529174294Sobrien "last message repeated %d times\n", last_count); 53038494Sobrien fwrite(last_msg, strlen(last_msg), 1, logfp); 53138494Sobrien fflush(logfp); 53238494Sobrien last_count = 0; /* start from scratch */ 53338494Sobrien break; 53438494Sobrien 53538494Sobrien default: /* item repeated multiple times */ 53638494Sobrien if (STREQ(last_msg, msg)) { 53738494Sobrien last_count++; 53838494Sobrien } else { /* last msg repeated+skipped, new one differs */ 53938494Sobrien show_time_host_and_name(last_lvl); 540174294Sobrien xsnprintf(last_msg, sizeof(last_msg), 541174294Sobrien "last message repeated %d times\n", last_count); 54238494Sobrien fwrite(last_msg, strlen(last_msg), 1, logfp); 543174294Sobrien if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */ 544174294Sobrien fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg); 54538494Sobrien last_count = 1; 54638494Sobrien last_lvl = lvl; 54738494Sobrien show_time_host_and_name(lvl); /* mimic syslog header */ 54838494Sobrien fwrite(msg, ptr - msg, 1, logfp); 54938494Sobrien fflush(logfp); 55038494Sobrien } 55138494Sobrien break; 55238494Sobrien } 55338494Sobrien 55438494Sobrien} 55538494Sobrien 55638494Sobrien 55738494Sobrien/* 55838494Sobrien * Display current debug options 55938494Sobrien */ 56038494Sobrienvoid 56138494Sobrienshow_opts(int ch, struct opt_tab *opts) 56238494Sobrien{ 56338494Sobrien int i; 56438494Sobrien int s = '{'; 56538494Sobrien 56638494Sobrien fprintf(stderr, "\t[-%c {no}", ch); 56738494Sobrien for (i = 0; opts[i].opt; i++) { 56838494Sobrien fprintf(stderr, "%c%s", s, opts[i].opt); 56938494Sobrien s = ','; 57038494Sobrien } 57138494Sobrien fputs("}]\n", stderr); 57238494Sobrien} 57338494Sobrien 57438494Sobrien 57538494Sobrienint 57638494Sobriencmdoption(char *s, struct opt_tab *optb, int *flags) 57738494Sobrien{ 57838494Sobrien char *p = s; 57938494Sobrien int errs = 0; 58038494Sobrien 58138494Sobrien while (p && *p) { 58238494Sobrien int neg; 58338494Sobrien char *opt; 58438494Sobrien struct opt_tab *dp, *dpn = 0; 58538494Sobrien 58638494Sobrien s = p; 58738494Sobrien p = strchr(p, ','); 58838494Sobrien if (p) 58938494Sobrien *p = '\0'; 59038494Sobrien 59138494Sobrien /* check for "no" prefix to options */ 59238494Sobrien if (s[0] == 'n' && s[1] == 'o') { 59338494Sobrien opt = s + 2; 59438494Sobrien neg = 1; 59538494Sobrien } else { 59638494Sobrien opt = s; 59738494Sobrien neg = 0; 59838494Sobrien } 59938494Sobrien 60038494Sobrien /* 60138494Sobrien * Scan the array of debug options to find the 60238494Sobrien * corresponding flag value. If it is found 60338494Sobrien * then set (or clear) the flag (depending on 60438494Sobrien * whether the option was prefixed with "no"). 60538494Sobrien */ 60638494Sobrien for (dp = optb; dp->opt; dp++) { 60738494Sobrien if (STREQ(opt, dp->opt)) 60838494Sobrien break; 60938494Sobrien if (opt != s && !dpn && STREQ(s, dp->opt)) 61038494Sobrien dpn = dp; 61138494Sobrien } 61238494Sobrien 61338494Sobrien if (dp->opt || dpn) { 61438494Sobrien if (!dp->opt) { 61538494Sobrien dp = dpn; 61638494Sobrien neg = !neg; 61738494Sobrien } 61838494Sobrien if (neg) 61938494Sobrien *flags &= ~dp->flag; 62038494Sobrien else 62138494Sobrien *flags |= dp->flag; 62238494Sobrien } else { 62338494Sobrien /* 62438494Sobrien * This will log to stderr when parsing the command line 62538494Sobrien * since any -l option will not yet have taken effect. 62638494Sobrien */ 62742629Sobrien plog(XLOG_USER, "option \"%s\" not recognized", s); 62838494Sobrien errs++; 62938494Sobrien } 63038494Sobrien 63138494Sobrien /* 63238494Sobrien * Put the comma back 63338494Sobrien */ 63438494Sobrien if (p) 63538494Sobrien *p++ = ','; 63638494Sobrien } 63738494Sobrien 63838494Sobrien return errs; 63938494Sobrien} 64038494Sobrien 64138494Sobrien 64238494Sobrien/* 64338494Sobrien * Switch on/off logging options 64438494Sobrien */ 64538494Sobrienint 64638494Sobrienswitch_option(char *opt) 64738494Sobrien{ 64838494Sobrien int xl = xlog_level; 64938494Sobrien int rc = cmdoption(opt, xlog_opt, &xl); 65038494Sobrien 65138494Sobrien if (rc) { 65238494Sobrien rc = EINVAL; 65338494Sobrien } else { 65438494Sobrien /* 65538494Sobrien * Keep track of initial log level, and 65638494Sobrien * don't allow options to be turned off. 65738494Sobrien */ 65838494Sobrien if (xlog_level_init == ~0) 65938494Sobrien xlog_level_init = xl; 66038494Sobrien else 66138494Sobrien xl |= xlog_level_init; 66238494Sobrien xlog_level = xl; 66338494Sobrien } 66438494Sobrien return rc; 66538494Sobrien} 66638494Sobrien 667174294Sobrien 66851292Sobrien#ifdef LOG_DAEMON 66938494Sobrien/* 67038494Sobrien * get syslog facility to use. 67138494Sobrien * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc. 67238494Sobrien */ 67338494Sobrienstatic int 67438494Sobrienget_syslog_facility(const char *logfile) 67538494Sobrien{ 67638494Sobrien char *facstr; 67738494Sobrien 67838494Sobrien /* parse facility string */ 67938494Sobrien facstr = strchr(logfile, ':'); 68038494Sobrien if (!facstr) /* log file was "syslog" */ 68138494Sobrien return LOG_DAEMON; 68238494Sobrien facstr++; 68338494Sobrien if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */ 68438494Sobrien plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON"); 68538494Sobrien return LOG_DAEMON; 68638494Sobrien } 68738494Sobrien 68838494Sobrien#ifdef LOG_KERN 68938494Sobrien if (STREQ(facstr, "kern")) 69038494Sobrien return LOG_KERN; 69138494Sobrien#endif /* not LOG_KERN */ 69238494Sobrien#ifdef LOG_USER 69338494Sobrien if (STREQ(facstr, "user")) 69438494Sobrien return LOG_USER; 69538494Sobrien#endif /* not LOG_USER */ 69638494Sobrien#ifdef LOG_MAIL 69738494Sobrien if (STREQ(facstr, "mail")) 69838494Sobrien return LOG_MAIL; 69938494Sobrien#endif /* not LOG_MAIL */ 70051292Sobrien 70138494Sobrien if (STREQ(facstr, "daemon")) 70238494Sobrien return LOG_DAEMON; 70351292Sobrien 70438494Sobrien#ifdef LOG_AUTH 70538494Sobrien if (STREQ(facstr, "auth")) 70638494Sobrien return LOG_AUTH; 70738494Sobrien#endif /* not LOG_AUTH */ 70838494Sobrien#ifdef LOG_SYSLOG 70938494Sobrien if (STREQ(facstr, "syslog")) 71038494Sobrien return LOG_SYSLOG; 71138494Sobrien#endif /* not LOG_SYSLOG */ 71238494Sobrien#ifdef LOG_LPR 71338494Sobrien if (STREQ(facstr, "lpr")) 71438494Sobrien return LOG_LPR; 71538494Sobrien#endif /* not LOG_LPR */ 71638494Sobrien#ifdef LOG_NEWS 71738494Sobrien if (STREQ(facstr, "news")) 71838494Sobrien return LOG_NEWS; 71938494Sobrien#endif /* not LOG_NEWS */ 72038494Sobrien#ifdef LOG_UUCP 72138494Sobrien if (STREQ(facstr, "uucp")) 72238494Sobrien return LOG_UUCP; 72338494Sobrien#endif /* not LOG_UUCP */ 72438494Sobrien#ifdef LOG_CRON 72538494Sobrien if (STREQ(facstr, "cron")) 72638494Sobrien return LOG_CRON; 72738494Sobrien#endif /* not LOG_CRON */ 72838494Sobrien#ifdef LOG_LOCAL0 72938494Sobrien if (STREQ(facstr, "local0")) 73038494Sobrien return LOG_LOCAL0; 73138494Sobrien#endif /* not LOG_LOCAL0 */ 73238494Sobrien#ifdef LOG_LOCAL1 73338494Sobrien if (STREQ(facstr, "local1")) 73438494Sobrien return LOG_LOCAL1; 73538494Sobrien#endif /* not LOG_LOCAL1 */ 73638494Sobrien#ifdef LOG_LOCAL2 73738494Sobrien if (STREQ(facstr, "local2")) 73838494Sobrien return LOG_LOCAL2; 73938494Sobrien#endif /* not LOG_LOCAL2 */ 74038494Sobrien#ifdef LOG_LOCAL3 74138494Sobrien if (STREQ(facstr, "local3")) 74238494Sobrien return LOG_LOCAL3; 74338494Sobrien#endif /* not LOG_LOCAL3 */ 74438494Sobrien#ifdef LOG_LOCAL4 74538494Sobrien if (STREQ(facstr, "local4")) 74638494Sobrien return LOG_LOCAL4; 74738494Sobrien#endif /* not LOG_LOCAL4 */ 74838494Sobrien#ifdef LOG_LOCAL5 74938494Sobrien if (STREQ(facstr, "local5")) 75038494Sobrien return LOG_LOCAL5; 75138494Sobrien#endif /* not LOG_LOCAL5 */ 75238494Sobrien#ifdef LOG_LOCAL6 75338494Sobrien if (STREQ(facstr, "local6")) 75438494Sobrien return LOG_LOCAL6; 75538494Sobrien#endif /* not LOG_LOCAL6 */ 75638494Sobrien#ifdef LOG_LOCAL7 75738494Sobrien if (STREQ(facstr, "local7")) 75838494Sobrien return LOG_LOCAL7; 75938494Sobrien#endif /* not LOG_LOCAL7 */ 76038494Sobrien 76138494Sobrien /* didn't match anything else */ 76238494Sobrien plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr); 76338494Sobrien return LOG_DAEMON; 76438494Sobrien} 76551292Sobrien#endif /* not LOG_DAEMON */ 76638494Sobrien 76738494Sobrien 76838494Sobrien/* 76938494Sobrien * Change current logfile 77038494Sobrien */ 77138494Sobrienint 772174294Sobrienswitch_to_logfile(char *logfile, int old_umask, int truncate_log) 77338494Sobrien{ 77438494Sobrien FILE *new_logfp = stderr; 77538494Sobrien 77638494Sobrien if (logfile) { 77738494Sobrien#ifdef HAVE_SYSLOG 77838494Sobrien syslogging = 0; 77938494Sobrien#endif /* HAVE_SYSLOG */ 78038494Sobrien 78138494Sobrien if (STREQ(logfile, "/dev/stderr")) 78238494Sobrien new_logfp = stderr; 78338494Sobrien else if (NSTREQ(logfile, "syslog", strlen("syslog"))) { 78438494Sobrien 78538494Sobrien#ifdef HAVE_SYSLOG 78638494Sobrien syslogging = 1; 78738494Sobrien new_logfp = stderr; 78842629Sobrien openlog(am_get_progname(), 78938494Sobrien LOG_PID 79038494Sobrien# ifdef LOG_NOWAIT 79138494Sobrien | LOG_NOWAIT 79238494Sobrien# endif /* LOG_NOWAIT */ 79338494Sobrien# ifdef LOG_DAEMON 79438494Sobrien , get_syslog_facility(logfile) 79538494Sobrien# endif /* LOG_DAEMON */ 79638494Sobrien ); 79738494Sobrien#else /* not HAVE_SYSLOG */ 79838494Sobrien plog(XLOG_WARNING, "syslog option not supported, logging unchanged"); 79938494Sobrien#endif /* not HAVE_SYSLOG */ 80038494Sobrien 801174294Sobrien } else { /* regular log file */ 80242629Sobrien (void) umask(old_umask); 803174294Sobrien if (truncate_log) 804174294Sobrien truncate(logfile, 0); 80538494Sobrien new_logfp = fopen(logfile, "a"); 80638494Sobrien umask(0); 80738494Sobrien } 80838494Sobrien } 80938494Sobrien 81038494Sobrien /* 81138494Sobrien * If we couldn't open a new file, then continue using the old. 81238494Sobrien */ 81338494Sobrien if (!new_logfp && logfile) { 81438494Sobrien plog(XLOG_USER, "%s: Can't open logfile: %m", logfile); 81538494Sobrien return 1; 81638494Sobrien } 81738494Sobrien 81838494Sobrien /* 81938494Sobrien * Close the previous file 82038494Sobrien */ 82138494Sobrien if (logfp && logfp != stderr) 82238494Sobrien (void) fclose(logfp); 82338494Sobrien logfp = new_logfp; 82438494Sobrien 825119679Smbr if (logfile) 826119679Smbr plog(XLOG_INFO, "switched to logfile \"%s\"", logfile); 827119679Smbr else 828119679Smbr plog(XLOG_INFO, "no logfile defined; using stderr"); 829119679Smbr 83038494Sobrien return 0; 83138494Sobrien} 83238494Sobrien 83338494Sobrien 83438494Sobrienvoid 83538494Sobrienunregister_amq(void) 83638494Sobrien{ 837174294Sobrien if (!amuDebug(D_AMQ)) { 83838494Sobrien /* find which instance of amd to unregister */ 839174294Sobrien u_long amd_prognum = get_amd_program_number(); 840174294Sobrien 841174294Sobrien if (pmap_unset(amd_prognum, AMQ_VERSION) != 1) 842174294Sobrien dlog("failed to de-register Amd program %lu, version %lu", 843174294Sobrien amd_prognum, AMQ_VERSION); 844174294Sobrien } 84538494Sobrien} 84638494Sobrien 84738494Sobrien 84838494Sobrienvoid 84938494Sobriengoing_down(int rc) 85038494Sobrien{ 85138494Sobrien if (foreground) { 85238494Sobrien if (amd_state != Start) { 85338494Sobrien if (amd_state != Done) 85438494Sobrien return; 85538494Sobrien unregister_amq(); 85638494Sobrien } 85738494Sobrien } 858174294Sobrien 859174294Sobrien#ifdef MOUNT_TABLE_ON_FILE 860174294Sobrien /* 861174294Sobrien * Call unlock_mntlist to free any important resources such as an on-disk 862174294Sobrien * lock file (/etc/mtab~). 863174294Sobrien */ 864174294Sobrien unlock_mntlist(); 865174294Sobrien#endif /* MOUNT_TABLE_ON_FILE */ 866174294Sobrien 86738494Sobrien if (foreground) { 86838494Sobrien plog(XLOG_INFO, "Finishing with status %d", rc); 86938494Sobrien } else { 87038494Sobrien dlog("background process exiting with status %d", rc); 87138494Sobrien } 872174294Sobrien /* bye bye... */ 87338494Sobrien exit(rc); 87438494Sobrien} 87538494Sobrien 87638494Sobrien 87738494Sobrien/* return the rpc program number under which amd was used */ 87838494Sobrienint 87938494Sobrienget_amd_program_number(void) 88038494Sobrien{ 88138494Sobrien return amd_program_number; 88238494Sobrien} 88338494Sobrien 88438494Sobrien 88538494Sobrien/* set the rpc program number used for amd */ 88638494Sobrienvoid 88738494Sobrienset_amd_program_number(int program) 88838494Sobrien{ 88938494Sobrien amd_program_number = program; 89038494Sobrien} 89138494Sobrien 89238494Sobrien 89338494Sobrien/* 89438494Sobrien * Release the controlling tty of the process pid. 89538494Sobrien * 89638494Sobrien * Algorithm: try these in order, if available, until one of them 89738494Sobrien * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0). 89838494Sobrien * Do not use setpgid(): on some OSs it may release the controlling tty, 89938494Sobrien * even if the man page does not mention it, but on other OSs it does not. 90038494Sobrien * Also avoid setpgrp(): it works on some systems, and on others it is 90138494Sobrien * identical to setpgid(). 90238494Sobrien */ 90338494Sobrienvoid 90438494Sobrienamu_release_controlling_tty(void) 90538494Sobrien{ 90638494Sobrien int fd; 90738494Sobrien 90882794Sobrien /* 90982794Sobrien * In daemon mode, leaving open file descriptors to terminals or pipes 91082794Sobrien * can be a really bad idea. 91182794Sobrien * Case in point: the redhat startup script calls us through their 'initlog' 912119679Smbr * program, which exits as soon as the original amd process exits. If, 913119679Smbr * at some point, a misbehaved library function decides to print something 914119679Smbr * to the screen, we get a SIGPIPE and die. 915119679Smbr * And guess what: NIS glibc functions will attempt to print to stderr 91682794Sobrien * "YPBINDPROC_DOMAIN: Domain not bound" if ypbind is running but can't find 91782794Sobrien * a ypserver. 91882794Sobrien * 91982794Sobrien * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then 92082794Sobrien * reopen them as /dev/null. 92182794Sobrien * 92282794Sobrien * XXX We should also probably set the SIGPIPE handler to SIG_IGN. 92382794Sobrien */ 924174294Sobrien fd = open("/dev/null", O_RDWR); 925174294Sobrien if (fd < 0) { 926174294Sobrien plog(XLOG_WARNING, "Could not open /dev/null for rw: %m"); 927174294Sobrien } else { 928174294Sobrien fflush(stdin); close(0); dup2(fd, 0); 929174294Sobrien fflush(stdout); close(1); dup2(fd, 1); 930174294Sobrien fflush(stderr); close(2); dup2(fd, 2); 931174294Sobrien close(fd); 932174294Sobrien } 93382794Sobrien 934119679Smbr#ifdef HAVE_SETSID 935119679Smbr /* XXX: one day maybe use vhangup(2) */ 936119679Smbr if (setsid() < 0) { 937119679Smbr plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m"); 938119679Smbr } else { 939119679Smbr plog(XLOG_INFO, "released controlling tty using setsid()"); 940119679Smbr return; 941119679Smbr } 942119679Smbr#endif /* HAVE_SETSID */ 943119679Smbr 94438494Sobrien#ifdef TIOCNOTTY 94538494Sobrien fd = open("/dev/tty", O_RDWR); 94638494Sobrien if (fd < 0) { 94738494Sobrien /* not an error if already no controlling tty */ 94838494Sobrien if (errno != ENXIO) 94938494Sobrien plog(XLOG_WARNING, "Could not open controlling tty: %m"); 95038494Sobrien } else { 95138494Sobrien if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY) 95238494Sobrien plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m"); 95338494Sobrien else 95438494Sobrien plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)"); 95538494Sobrien close(fd); 95638494Sobrien } 95738494Sobrien return; 95838494Sobrien#endif /* not TIOCNOTTY */ 95938494Sobrien 96038494Sobrien plog(XLOG_ERROR, "unable to release controlling tty"); 96138494Sobrien} 962174294Sobrien 963174294Sobrien 964174294Sobrien/* setup a single signal handler */ 965174294Sobrienvoid 966174294Sobriensetup_sighandler(int signum, void (*handler)(int)) 967174294Sobrien{ 968174294Sobrien#ifdef HAVE_SIGACTION 969174294Sobrien struct sigaction sa; 970174294Sobrien memset(&sa, 0, sizeof(sa)); 971174294Sobrien sa.sa_flags = 0; /* unnecessary */ 972174294Sobrien sa.sa_handler = handler; 973174294Sobrien sigemptyset(&(sa.sa_mask)); /* probably unnecessary too */ 974174294Sobrien sigaddset(&(sa.sa_mask), signum); 975174294Sobrien sigaction(signum, &sa, NULL); 976174294Sobrien#else /* not HAVE_SIGACTION */ 977174294Sobrien (void) signal(signum, handler); 978174294Sobrien#endif /* not HAVE_SIGACTION */ 979174294Sobrien} 980174294Sobrien 981174294Sobrien 982174294Sobrien/* 983174294Sobrien * Return current time in seconds. If passed a non-null argyument, then 984174294Sobrien * fill it in with the current time in seconds and microseconds (useful 985174294Sobrien * for mtime updates). 986174294Sobrien */ 987174294Sobrientime_t 988174294Sobrienclocktime(nfstime *nt) 989174294Sobrien{ 990174294Sobrien static struct timeval now; /* keep last time, as default */ 991174294Sobrien 992174294Sobrien if (gettimeofday(&now, NULL) < 0) { 993174294Sobrien plog(XLOG_ERROR, "clocktime: gettimeofday: %m"); 994174294Sobrien /* hack: force time to have incremented by at least 1 second */ 995174294Sobrien now.tv_sec++; 996174294Sobrien } 997174294Sobrien /* copy seconds and microseconds. may demote a long to an int */ 998174294Sobrien if (nt) { 999174294Sobrien nt->nt_seconds = (u_int) now.tv_sec; 1000174294Sobrien nt->nt_useconds = (u_int) now.tv_usec; 1001174294Sobrien } 1002174294Sobrien return (time_t) now.tv_sec; 1003174294Sobrien} 1004174294Sobrien 1005174294Sobrien 1006174294Sobrien/* 1007174294Sobrien * Make all the directories in the path. 1008174294Sobrien */ 1009174294Sobrienint 1010174294Sobrienmkdirs(char *path, int mode) 1011174294Sobrien{ 1012174294Sobrien /* 1013174294Sobrien * take a copy in case path is in readonly store 1014174294Sobrien */ 1015174294Sobrien char *p2 = strdup(path); 1016174294Sobrien char *sp = p2; 1017174294Sobrien struct stat stb; 1018174294Sobrien int error_so_far = 0; 1019174294Sobrien 1020174294Sobrien /* 1021174294Sobrien * Skip through the string make the directories. 1022174294Sobrien * Mostly ignore errors - the result is tested at the end. 1023174294Sobrien * 1024174294Sobrien * This assumes we are root so that we can do mkdir in a 1025174294Sobrien * mode 555 directory... 1026174294Sobrien */ 1027174294Sobrien while ((sp = strchr(sp + 1, '/'))) { 1028174294Sobrien *sp = '\0'; 1029174294Sobrien if (mkdir(p2, mode) < 0) { 1030174294Sobrien error_so_far = errno; 1031174294Sobrien } else { 1032174294Sobrien dlog("mkdir(%s)", p2); 1033174294Sobrien } 1034174294Sobrien *sp = '/'; 1035174294Sobrien } 1036174294Sobrien 1037174294Sobrien if (mkdir(p2, mode) < 0) { 1038174294Sobrien error_so_far = errno; 1039174294Sobrien } else { 1040174294Sobrien dlog("mkdir(%s)", p2); 1041174294Sobrien } 1042174294Sobrien 1043174294Sobrien XFREE(p2); 1044174294Sobrien 1045174294Sobrien return stat(path, &stb) == 0 && 1046174294Sobrien (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far; 1047174294Sobrien} 1048174294Sobrien 1049174294Sobrien 1050174294Sobrien/* 1051174294Sobrien * Remove as many directories in the path as possible. 1052174294Sobrien * Give up if the directory doesn't appear to have 1053174294Sobrien * been created by Amd (not mode dr-x) or an rmdir 1054174294Sobrien * fails for any reason. 1055174294Sobrien */ 1056174294Sobrienvoid 1057174294Sobrienrmdirs(char *dir) 1058174294Sobrien{ 1059174294Sobrien char *xdp = strdup(dir); 1060174294Sobrien char *dp; 1061174294Sobrien 1062174294Sobrien do { 1063174294Sobrien struct stat stb; 1064174294Sobrien /* 1065174294Sobrien * Try to find out whether this was 1066174294Sobrien * created by amd. Do this by checking 1067174294Sobrien * for owner write permission. 1068174294Sobrien */ 1069174294Sobrien if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) { 1070174294Sobrien if (rmdir(xdp) < 0) { 1071174294Sobrien if (errno != ENOTEMPTY && 1072174294Sobrien errno != EBUSY && 1073174294Sobrien errno != EEXIST && 1074174294Sobrien errno != EROFS && 1075174294Sobrien errno != EINVAL) 1076174294Sobrien plog(XLOG_ERROR, "rmdir(%s): %m", xdp); 1077174294Sobrien break; 1078174294Sobrien } else { 1079174294Sobrien dlog("rmdir(%s)", xdp); 1080174294Sobrien } 1081174294Sobrien } else { 1082174294Sobrien break; 1083174294Sobrien } 1084174294Sobrien 1085174294Sobrien dp = strrchr(xdp, '/'); 1086174294Sobrien if (dp) 1087174294Sobrien *dp = '\0'; 1088174294Sobrien } while (dp && dp > xdp); 1089174294Sobrien 1090174294Sobrien XFREE(xdp); 1091174294Sobrien} 1092