All of lore.kernel.org
 help / color / mirror / Atom feed
* [patch, rfc] fix hung automounter issues
@ 2005-04-14 12:55 Jeff Moyer
  2005-04-15  1:59 ` Ian Kent
  2005-04-18 13:26 ` raven
  0 siblings, 2 replies; 16+ messages in thread
From: Jeff Moyer @ 2005-04-14 12:55 UTC (permalink / raw)
  To: raven; +Cc: autofs, Dan.Berrange

Hi, Ian,

Dan Berrange did a spectacular job of diagnosing some autofs hangs he was
experiencing.  The automount daemon would appear hung, and an an strace of
the process would show sys_futex on the call stack.  Through his
debugging, he figured out that the problem was autofs issuing syslog calls
while in a signal handler.

So, there are a few ways to fix this that come to mind.

o We could not log in signal handlers.  I don't consider this an acceptable
  solution, as we really need the debug messages generated there.
o We could defer logging to non-signal handler context.  This is in fact
  what the attached pattch does.  It queues the syslog messages, and
  flushes them upon the next syslog that occurs outside of signal handler
  context.
o We could open /dev/log directly.  This is likely a bad idea as there is
  no standard for the interface there.
o We could have a separate logging process, which we write to via a pipe.
  I'm not keen on this as it adds yet another process, and makes shutdown
  that much more complicated.

Note that in all of the above cases, we still need to implement a
signal-safe vsprintf.  That is what the bulk of this patch is.

So, here is a rough take on implementing the second bullet point.  I
wholesale copied a bunch of code from the linux kernel for doing vsprintf.
That bit is ugly.  I'd also move the definition of the new logging routines
into the vsprintf file, and rename it.  In short, this is a proof of
concept (shown to resolve the issues).  I'm happy to clean it up, but I
want to be sure that this is the direction we want to go in, first.

Limitations of this approach: we won't flush the logs that were issued in
signal handler context until another syslog call is made.  One improvement
that could be made straight-away is to have all of the logging routines
call flush_logs, even if the log priority is set low enough that they
wouldn't otherwise log.

Comments encouraged.  Thanks again to Dan!

-Jeff

Note for reviewers: the variable ringlen is a misnomer.  I started with the
idea of implementing a ring buffer, but threw it out.  This is just a fixed
size array of log buffers, and I believe that to be sufficient, since we
shouldn't be doing too much logging in signal handlers, anyway.


diff -uprN --exclude=configure --exclude=rc.autofs autofs-4.1.4_beta2.orig/daemon/automount.c autofs-4.1.4_beta2/daemon/automount.c
--- autofs-4.1.4_beta2.orig/daemon/automount.c	2005-02-10 07:56:53.000000000 -0500
+++ autofs-4.1.4_beta2/daemon/automount.c	2005-04-11 14:56:32.515069640 -0400
@@ -27,6 +27,7 @@
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdarg.h>
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
@@ -64,6 +65,7 @@ int do_debug = 0;		/* Enable full debug 
 sigset_t ready_sigs;		/* signals only accepted in ST_READY */
 sigset_t lock_sigs;		/* signals blocked for locking */
 sigset_t sigchld_mask;
+int in_interrupt = 0;		/* keeps us from logging in a signal handler */
 
 struct autofs_point ap;
 
@@ -79,6 +81,72 @@ static void cleanup_exit(const char *pat
 static int handle_packet_expire(const struct autofs_packet_expire *pkt);
 static int umount_all(int force);
 
+#define LOGQUEUE_LINE_LEN 128
+#define LOGQUEUE_MAX      128
+struct log_queue {
+	int  pri;
+	char buf[LOGQUEUE_LINE_LEN];
+} logbuf[LOGQUEUE_MAX];
+static int ringlen;
+
+static void block_signals(sigset_t *set)
+{
+	sigset_t allsigs;
+
+	sigfillset(&allsigs);
+	sigprocmask(SIG_BLOCK, &allsigs, set);
+}
+
+static void unblock_signals(sigset_t *set)
+{
+	sigprocmask(SIG_SETMASK, set, NULL);
+}
+
+void flush_logs(void)
+{
+	int i;
+	sigset_t set;
+
+	block_signals(&set);
+	assert(!in_interrupt);
+	for (i = 0; i < ringlen; i++)
+		syslog(logbuf[i].pri, logbuf[i].buf);
+	ringlen = 0;
+	unblock_signals(&set);
+}
+
+void queue_syslog(int priority, const char *format, ...)
+{
+	va_list args;
+	struct log_queue *rp;
+	sigset_t set;
+
+	assert(in_interrupt);
+
+	block_signals(&set);
+	if (ringlen >= LOGQUEUE_MAX) {
+#ifdef DEBUG
+		/*
+		 *  We want to know if we are exceeding the max number of
+		 *  log entrise.
+		 */
+		*(void *)0 = 0;
+#else
+		unblock_signals(&set);
+		return;
+#endif
+	}
+	rp = &logbuf[ringlen];
+	ringlen++;
+	unblock_signals(&set);
+
+	va_start(args, format);
+	vsnprintf_int(rp->buf, LOGQUEUE_LINE_LEN, format, args);
+	va_end(args);
+	rp->buf[LOGQUEUE_LINE_LEN - 1] = '\0';
+	rp->pri = priority;
+}
+
 int mkdir_path(const char *path, mode_t mode)
 {
 	char *buf = alloca(strlen(path) + 1);
@@ -490,6 +558,7 @@ static void sig_statemachine(int sig)
 	int save_errno = errno;
 	enum states next = ap.state;
 
+	in_interrupt++;
 	switch (sig) {
 	default:		/* all the "can't happen" signals */
 		error("process %d got unexpected signal %d!", getpid(), sig);
@@ -521,6 +590,7 @@ static void sig_statemachine(int sig)
 	debug("sig %d switching from %d to %d", sig, ap.state, next);
 
 	errno = save_errno;
+	in_interrupt--;
 }
 
 static int send_ready(unsigned int wait_queue_token)
@@ -661,6 +731,7 @@ static void sig_child(int sig)
 	int save_errno = errno;
 	enum states next;
 
+	in_interrupt++;
 	if (sig != SIGCHLD)
 		return;
 
@@ -669,6 +740,7 @@ static void sig_child(int sig)
 		nextstate(next);
 
 	errno = save_errno;
+	in_interrupt--;
 }
 
 static int st_ready(void)
@@ -1481,6 +1553,7 @@ static void sig_supervisor(int sig)
 {
 	int save_errno = errno;
 
+	in_interrupt++;
 	switch (sig) {
 	default:		/* all the signals not handled */
 		error("process %d got unexpected signal %d!", getpid(), sig);
@@ -1512,6 +1585,7 @@ static void sig_supervisor(int sig)
 		break;
 	}
 	errno = save_errno;
+	in_interrupt--;
 }
 
 int supervisor(char *path)
diff -uprN --exclude=configure --exclude=rc.autofs autofs-4.1.4_beta2.orig/include/automount.h autofs-4.1.4_beta2/include/automount.h
--- autofs-4.1.4_beta2.orig/include/automount.h	2005-01-26 08:03:02.000000000 -0500
+++ autofs-4.1.4_beta2/include/automount.h	2005-04-11 14:24:54.770570656 -0400
@@ -13,6 +13,7 @@
 #include <paths.h>
 #include <limits.h>
 #include <time.h>
+#include <stdarg.h>
 #include "config.h"
 
 /* We MUST have the paths to mount(8) and umount(8) */
@@ -116,6 +117,7 @@ struct autofs_point {
 };
 
 extern struct autofs_point ap; 
+extern int in_interrupt;
 
 /* Standard function used by daemon or modules */
 
@@ -281,26 +283,51 @@ void free_mnt_list(struct mnt_list *list
 int is_mounted(const char *table, const char *path);
 int has_fstab_option(const char *path, const char *opt);
 int allow_owner_mount(const char *);
+int vsnprintf_int(char *buf, size_t size, const char *fmt, va_list args);
+void flush_logs(void);
+void queue_syslog(int pri, const char *format, ...);
 
 /* log notification */
 extern int do_verbose;
 extern int do_debug;
 
-#define info(msg, args...) 		\
-if (do_verbose || do_debug) 		\
-	syslog(LOG_INFO, msg, ##args);
-
-#define warn(msg, args...) 			\
-if (do_verbose || do_debug) 		\
-	syslog(LOG_WARNING, msg, ##args);
-
-#define error(msg, args...)	syslog(LOG_ERR, msg, ##args);
-
-#define crit(msg, args...)	syslog(LOG_CRIT, msg, ##args);
-
-#define debug(msg, args...) 		\
-if (do_debug) 				\
-	syslog(LOG_DEBUG, msg, ##args);
+#define safe_syslog(pri, msg, args...)		\
+do {						\
+	if (in_interrupt)			\
+		queue_syslog(pri, msg, ##args);	\
+	else {					\
+		flush_logs();			\
+		syslog(pri, msg, ##args);	\
+	}					\
+} while (0)
+
+#define info(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		safe_syslog(LOG_INFO, msg, ##args);	\
+} while (0)
+
+#define warn(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		safe_syslog(LOG_INFO, msg, ##args);	\
+} while (0)
+
+#define error(msg, args...)			\
+do {						\
+	safe_syslog(LOG_ERR, msg, ##args);	\
+} while (0)
+
+#define crit(msg, args...)			\
+do {						\
+	safe_syslog(LOG_CRIT, msg, ##args);	\
+} while (0)
+
+#define debug(msg, args...)				\
+do {							\
+	if (do_debug)					\
+		safe_syslog(LOG_DEBUG, msg, ##args);	\
+} while (0)
 
 #endif
 
diff -uprN --exclude=configure --exclude=rc.autofs autofs-4.1.4_beta2.orig/lib/Makefile autofs-4.1.4_beta2/lib/Makefile
--- autofs-4.1.4_beta2.orig/lib/Makefile	2005-01-09 04:16:43.000000000 -0500
+++ autofs-4.1.4_beta2/lib/Makefile	2005-04-08 17:09:08.000000000 -0400
@@ -12,7 +12,7 @@ RANLIB = /usr/bin/ranlib
 SRCS = cache.c listmount.c cat_path.c rpc_subs.c mounts.c lock.c
 RPCS = mount.h mount_clnt.c mount_xdr.c
 OBJS = cache.o mount_clnt.o mount_xdr.o listmount.o \
-	cat_path.o rpc_subs.o mounts.o lock.o
+	cat_path.o rpc_subs.o mounts.o lock.o vsprintf.o
 
 LIB = autofs.a
 
@@ -48,6 +48,10 @@ listmount.o: listmount.c
 	$(CC) $(CFLAGS) -o listmount.o -c listmount.c
 	$(STRIP) listmount.o
 
+vsprintf.o: vsprintf.c
+	$(CC) $(CFLAGS) -o vsprintf.o -c vsprintf.c
+	$(STRIP) vsprintf.o
+
 install: all
 
 clean:
diff -uprN --exclude=configure --exclude=rc.autofs autofs-4.1.4_beta2.orig/lib/vsprintf.c autofs-4.1.4_beta2/lib/vsprintf.c
--- autofs-4.1.4_beta2.orig/lib/vsprintf.c	1969-12-31 19:00:00.000000000 -0500
+++ autofs-4.1.4_beta2/lib/vsprintf.c	2005-04-11 14:37:55.614864280 -0400
@@ -0,0 +1,594 @@
+/*
+ *  Stolen from the linux kernel.
+ *
+ *  License: GPL
+ */
+/*------------------ Original Copyright -----------------*/
+/*
+ *  linux/lib/vsprintf.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
+/*
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
+ */
+
+/* 
+ * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
+ * - changed to provide snprintf and vsnprintf functions
+ * So Feb  1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de>
+ * - scnprintf and vscnprintf
+ */
+
+/* Also copied from: */
+
+/*
+ *  linux/lib/string.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * -  Added strsep() which will replace strtok() soon (because strsep() is
+ *    reentrant and should be faster). Use only strsep() in new code, please.
+ *
+ * * Sat Feb 09 2002, Jason Thomas <jason@topic.com.au>,
+ *                    Matthew Hawkins <matt@mh.dropbear.id.au>
+ * -  Kissed strtok() goodbye
+ */
+/*-------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#define BITS_PER_LONG	__WORDSIZE
+#define PAGE_SIZE	getpagesize()
+
+
+#if BITS_PER_LONG == 64
+
+# define do_div(n,base) ({					\
+	uint32_t __base = (base);				\
+	uint32_t __rem;						\
+	__rem = ((uint64_t)(n)) % __base;			\
+	(n) = ((uint64_t)(n)) / __base;				\
+	__rem;							\
+ })
+
+#elif BITS_PER_LONG == 32
+
+/* Not needed on 64bit architectures */
+uint32_t __div64_32(uint64_t *n, uint32_t base)
+{
+	uint64_t rem = *n;
+	uint64_t b = base;
+	uint64_t res, d = 1;
+	uint32_t high = rem >> 32;
+
+	/* Reduce the thing a bit first */
+	res = 0;
+	if (high >= base) {
+		high /= base;
+		res = (uint64_t) high << 32;
+		rem -= (uint64_t) (high*base) << 32;
+	}
+
+	while ((int64_t)b > 0 && b < rem) {
+		b = b+b;
+		d = d+d;
+	}
+
+	do {
+		if (rem >= b) {
+			rem -= b;
+			res += d;
+		}
+		b >>= 1;
+		d >>= 1;
+	} while (d);
+
+	*n = res;
+	return rem;
+}
+
+/* The unnecessary pointer compare is there
+ * to check for type safety (n must be 64bit)
+ */
+# define do_div(n,base) ({				\
+	uint32_t __base = (base);			\
+	uint32_t __rem;					\
+	(void)(((typeof((n)) *)0) == ((uint64_t *)0));	\
+	if (((n) >> 32) == 0) {				\
+		__rem = (uint32_t)(n) % __base;		\
+		(n) = (uint32_t)(n) / __base;		\
+	} else 						\
+		__rem = __div64_32(&(n), __base);	\
+	__rem;						\
+ })
+
+# else
+
+# error do_div() does not yet support the C64
+
+#endif /* BITS_PER_LONG */
+
+
+/**
+ * strnlen - Find the length of a length-limited string
+ * @s: The string to be sized
+ * @count: The maximum number of bytes to search
+ */
+size_t strnlen(const char * s, size_t count)
+{
+	const char *sc;
+
+	for (sc = s; count-- && *sc != '\0'; ++sc)
+		/* nothing */;
+	return sc - s;
+}
+
+/**
+ * simple_strtoul - convert a string to an unsigned long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
+{
+	unsigned long result = 0,value;
+
+	if (!base) {
+		base = 10;
+		if (*cp == '0') {
+			base = 8;
+			cp++;
+			if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
+				cp++;
+				base = 16;
+			}
+		}
+	} else if (base == 16) {
+		if (cp[0] == '0' && toupper(cp[1]) == 'X')
+			cp += 2;
+	}
+	while (isxdigit(*cp) &&
+	       (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
+		result = result*base + value;
+		cp++;
+	}
+	if (endp)
+		*endp = (char *)cp;
+	return result;
+}
+
+/**
+ * simple_strtol - convert a string to a signed long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long simple_strtol(const char *cp,char **endp,unsigned int base)
+{
+	if(*cp=='-')
+		return -simple_strtoul(cp+1,endp,base);
+	return simple_strtoul(cp,endp,base);
+}
+
+/**
+ * simple_strtoull - convert a string to an unsigned long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base)
+{
+	unsigned long long result = 0,value;
+
+	if (!base) {
+		base = 10;
+		if (*cp == '0') {
+			base = 8;
+			cp++;
+			if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
+				cp++;
+				base = 16;
+			}
+		}
+	} else if (base == 16) {
+		if (cp[0] == '0' && toupper(cp[1]) == 'X')
+			cp += 2;
+	}
+	while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
+	    ? toupper(*cp) : *cp)-'A'+10) < base) {
+		result = result*base + value;
+		cp++;
+	}
+	if (endp)
+		*endp = (char *)cp;
+	return result;
+}
+
+/**
+ * simple_strtoll - convert a string to a signed long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long long simple_strtoll(const char *cp,char **endp,unsigned int base)
+{
+	if(*cp=='-')
+		return -simple_strtoull(cp+1,endp,base);
+	return simple_strtoull(cp,endp,base);
+}
+
+static int skip_atoi(const char **s)
+{
+	int i=0;
+
+	while (isdigit(**s))
+		i = i*10 + *((*s)++) - '0';
+	return i;
+}
+
+#define ZEROPAD	1		/* pad with zero */
+#define SIGN	2		/* unsigned/signed long */
+#define PLUS	4		/* show plus */
+#define SPACE	8		/* space if plus */
+#define LEFT	16		/* left justified */
+#define SPECIAL	32		/* 0x */
+#define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */
+
+static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type)
+{
+	char c,sign,tmp[66];
+	const char *digits;
+	static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+	static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	int i;
+
+	digits = (type & LARGE) ? large_digits : small_digits;
+	if (type & LEFT)
+		type &= ~ZEROPAD;
+	if (base < 2 || base > 36)
+		return NULL;
+	c = (type & ZEROPAD) ? '0' : ' ';
+	sign = 0;
+	if (type & SIGN) {
+		if ((signed long long) num < 0) {
+			sign = '-';
+			num = - (signed long long) num;
+			size--;
+		} else if (type & PLUS) {
+			sign = '+';
+			size--;
+		} else if (type & SPACE) {
+			sign = ' ';
+			size--;
+		}
+	}
+	if (type & SPECIAL) {
+		if (base == 16)
+			size -= 2;
+		else if (base == 8)
+			size--;
+	}
+	i = 0;
+	if (num == 0)
+		tmp[i++]='0';
+	else while (num != 0)
+		tmp[i++] = digits[do_div(num,base)];
+	if (i > precision)
+		precision = i;
+	size -= precision;
+	if (!(type&(ZEROPAD+LEFT))) {
+		while(size-->0) {
+			if (buf <= end)
+				*buf = ' ';
+			++buf;
+		}
+	}
+	if (sign) {
+		if (buf <= end)
+			*buf = sign;
+		++buf;
+	}
+	if (type & SPECIAL) {
+		if (base==8) {
+			if (buf <= end)
+				*buf = '0';
+			++buf;
+		} else if (base==16) {
+			if (buf <= end)
+				*buf = '0';
+			++buf;
+			if (buf <= end)
+				*buf = digits[33];
+			++buf;
+		}
+	}
+	if (!(type & LEFT)) {
+		while (size-- > 0) {
+			if (buf <= end)
+				*buf = c;
+			++buf;
+		}
+	}
+	while (i < precision--) {
+		if (buf <= end)
+			*buf = '0';
+		++buf;
+	}
+	while (i-- > 0) {
+		if (buf <= end)
+			*buf = tmp[i];
+		++buf;
+	}
+	while (size-- > 0) {
+		if (buf <= end)
+			*buf = ' ';
+		++buf;
+	}
+	return buf;
+}
+
+/**
+ * vsnprintf_int - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * The return value is the number of characters which would
+ * be generated for the given input, excluding the trailing
+ * '\0', as per ISO C99. If you want to have the exact
+ * number of characters written into @buf as return value
+ * (not including the trailing '\0'), use vscnprintf. If the
+ * return is greater than or equal to @size, the resulting
+ * string is truncated.
+ *
+ * Call this function if you are already dealing with a va_list.
+ * You probably want snprintf instead.
+ */
+int vsnprintf_int(char *buf, size_t size, const char *fmt, va_list args)
+{
+	int len;
+	unsigned long long num;
+	int i, base;
+	char *str, *end, c;
+	const char *s;
+
+	int flags;		/* flags to number() */
+
+	int field_width;	/* width of output field */
+	int precision;		/* min. # of digits for integers; max
+				   number of chars for from string */
+	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
+				/* 'z' support added 23/7/1999 S.H.    */
+				/* 'z' changed to 'Z' --davidm 1/25/99 */
+
+	/* Reject out-of-range values early */
+	if ((int) size < 0)
+		return 0;
+
+	str = buf;
+	end = buf + size - 1;
+
+	if (end < buf - 1) {
+		end = ((void *) -1);
+		size = end - buf + 1;
+	}
+
+	for (; *fmt ; ++fmt) {
+		if (*fmt != '%') {
+			if (str <= end)
+				*str = *fmt;
+			++str;
+			continue;
+		}
+
+		/* process flags */
+		flags = 0;
+		repeat:
+			++fmt;		/* this also skips first '%' */
+			switch (*fmt) {
+				case '-': flags |= LEFT; goto repeat;
+				case '+': flags |= PLUS; goto repeat;
+				case ' ': flags |= SPACE; goto repeat;
+				case '#': flags |= SPECIAL; goto repeat;
+				case '0': flags |= ZEROPAD; goto repeat;
+			}
+
+		/* get field width */
+		field_width = -1;
+		if (isdigit(*fmt))
+			field_width = skip_atoi(&fmt);
+		else if (*fmt == '*') {
+			++fmt;
+			/* it's the next argument */
+			field_width = va_arg(args, int);
+			if (field_width < 0) {
+				field_width = -field_width;
+				flags |= LEFT;
+			}
+		}
+
+		/* get the precision */
+		precision = -1;
+		if (*fmt == '.') {
+			++fmt;	
+			if (isdigit(*fmt))
+				precision = skip_atoi(&fmt);
+			else if (*fmt == '*') {
+				++fmt;
+				/* it's the next argument */
+				precision = va_arg(args, int);
+			}
+			if (precision < 0)
+				precision = 0;
+		}
+
+		/* get the conversion qualifier */
+		qualifier = -1;
+		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
+		    *fmt =='Z' || *fmt == 'z') {
+			qualifier = *fmt;
+			++fmt;
+			if (qualifier == 'l' && *fmt == 'l') {
+				qualifier = 'L';
+				++fmt;
+			}
+		}
+
+		/* default base */
+		base = 10;
+
+		switch (*fmt) {
+			case 'c':
+				if (!(flags & LEFT)) {
+					while (--field_width > 0) {
+						if (str <= end)
+							*str = ' ';
+						++str;
+					}
+				}
+				c = (unsigned char) va_arg(args, int);
+				if (str <= end)
+					*str = c;
+				++str;
+				while (--field_width > 0) {
+					if (str <= end)
+						*str = ' ';
+					++str;
+				}
+				continue;
+
+			case 's':
+				s = va_arg(args, char *);
+				if ((unsigned long)s < PAGE_SIZE)
+					s = "<NULL>";
+
+				len = strnlen(s, precision);
+
+				if (!(flags & LEFT)) {
+					while (len < field_width--) {
+						if (str <= end)
+							*str = ' ';
+						++str;
+					}
+				}
+				for (i = 0; i < len; ++i) {
+					if (str <= end)
+						*str = *s;
+					++str; ++s;
+				}
+				while (len < field_width--) {
+					if (str <= end)
+						*str = ' ';
+					++str;
+				}
+				continue;
+
+			case 'p':
+				if (field_width == -1) {
+					field_width = 2*sizeof(void *);
+					flags |= ZEROPAD;
+				}
+				str = number(str, end,
+						(unsigned long) va_arg(args, void *),
+						16, field_width, precision, flags);
+				continue;
+
+
+			case 'n':
+				/* FIXME:
+				* What does C99 say about the overflow case here? */
+				if (qualifier == 'l') {
+					long * ip = va_arg(args, long *);
+					*ip = (str - buf);
+				} else if (qualifier == 'Z' || qualifier == 'z') {
+					size_t * ip = va_arg(args, size_t *);
+					*ip = (str - buf);
+				} else {
+					int * ip = va_arg(args, int *);
+					*ip = (str - buf);
+				}
+				continue;
+
+			case '%':
+				if (str <= end)
+					*str = '%';
+				++str;
+				continue;
+
+				/* integer number formats - set up the flags and "break" */
+			case 'o':
+				base = 8;
+				break;
+
+			case 'X':
+				flags |= LARGE;
+			case 'x':
+				base = 16;
+				break;
+
+			case 'd':
+			case 'i':
+				flags |= SIGN;
+			case 'u':
+				break;
+
+			default:
+				if (str <= end)
+					*str = '%';
+				++str;
+				if (*fmt) {
+					if (str <= end)
+						*str = *fmt;
+					++str;
+				} else {
+					--fmt;
+				}
+				continue;
+		}
+		if (qualifier == 'L')
+			num = va_arg(args, long long);
+		else if (qualifier == 'l') {
+			num = va_arg(args, unsigned long);
+			if (flags & SIGN)
+				num = (signed long) num;
+		} else if (qualifier == 'Z' || qualifier == 'z') {
+			num = va_arg(args, size_t);
+		} else if (qualifier == 'h') {
+			num = (unsigned short) va_arg(args, int);
+			if (flags & SIGN)
+				num = (signed short) num;
+		} else {
+			num = va_arg(args, unsigned int);
+			if (flags & SIGN)
+				num = (signed int) num;
+		}
+		str = number(str, end, num, base,
+				field_width, precision, flags);
+	}
+	if (str <= end)
+		*str = '\0';
+	else if (size > 0)
+		/* don't write out a null byte if the buf size is zero */
+		*end = '\0';
+	/* the trailing null byte doesn't count towards the total
+	* ++str;
+	*/
+	return str-buf;
+}
Binary files autofs-4.1.4_beta2.orig/samples/autofs-ldap-auto-master and autofs-4.1.4_beta2/samples/autofs-ldap-auto-master differ
Binary files autofs-4.1.4_beta2.orig/samples/autofs-ldap-auto-master.o and autofs-4.1.4_beta2/samples/autofs-ldap-auto-master.o differ

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch, rfc] fix hung automounter issues
  2005-04-14 12:55 [patch, rfc] fix hung automounter issues Jeff Moyer
@ 2005-04-15  1:59 ` Ian Kent
  2005-04-15 11:58   ` Jeff Moyer
  2005-04-18 13:26 ` raven
  1 sibling, 1 reply; 16+ messages in thread
From: Ian Kent @ 2005-04-15  1:59 UTC (permalink / raw)
  To: Jeff Moyer; +Cc: autofs mailing list, Dan.Berrange

On Thu, 14 Apr 2005, Jeff Moyer wrote:

I'm inpressed, this looks like a fine piece of work guys.
It's going to take a while to work through this.

Also This is bound to make merging Denis Vlasenkos' logging patches really 
hard. So there's potentially a fair bit of work in this.

> Hi, Ian,
> 
> Dan Berrange did a spectacular job of diagnosing some autofs hangs he was
> experiencing.  The automount daemon would appear hung, and an an strace of
> the process would show sys_futex on the call stack.  Through his
> debugging, he figured out that the problem was autofs issuing syslog calls
> while in a signal handler.

One, probably stupid question (humour me).

Shouldn't we be able use syslog in a signal handler?

> 
> So, there are a few ways to fix this that come to mind.
> 
> o We could not log in signal handlers.  I don't consider this an acceptable
>   solution, as we really need the debug messages generated there.
> o We could defer logging to non-signal handler context.  This is in fact
>   what the attached pattch does.  It queues the syslog messages, and
>   flushes them upon the next syslog that occurs outside of signal handler
>   context.
> o We could open /dev/log directly.  This is likely a bad idea as there is
>   no standard for the interface there.
> o We could have a separate logging process, which we write to via a pipe.
>   I'm not keen on this as it adds yet another process, and makes shutdown
>   that much more complicated.
> 

I actually like the last option but a multi threaded autofs is not likely 
to happen for a long time.

Would another option be to make autofs signal safe by doing all the work 
in subprocesses?

> Note that in all of the above cases, we still need to implement a
> signal-safe vsprintf.  That is what the bulk of this patch is.

What about a signal safe syslog?
Is this something we push to the libc guys, both a signal safe vsprintf 
and syslog?

> 
> So, here is a rough take on implementing the second bullet point.  I
> wholesale copied a bunch of code from the linux kernel for doing vsprintf.
> That bit is ugly.  I'd also move the definition of the new logging routines
> into the vsprintf file, and rename it.  In short, this is a proof of
> concept (shown to resolve the issues).  I'm happy to clean it up, but I
> want to be sure that this is the direction we want to go in, first.
> 
> Limitations of this approach: we won't flush the logs that were issued in
> signal handler context until another syslog call is made.  One improvement
> that could be made straight-away is to have all of the logging routines
> call flush_logs, even if the log priority is set low enough that they
> wouldn't otherwise log.
> 
> Comments encouraged.  Thanks again to Dan!
> 

Ian

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch, rfc] fix hung automounter issues
  2005-04-15  1:59 ` Ian Kent
@ 2005-04-15 11:58   ` Jeff Moyer
  2005-04-16  4:46     ` raven
  0 siblings, 1 reply; 16+ messages in thread
From: Jeff Moyer @ 2005-04-15 11:58 UTC (permalink / raw)
  To: Ian Kent; +Cc: autofs mailing list, Dan.Berrange

==> Regarding Re: [patch, rfc] fix hung automounter issues; Ian Kent <raven@themaw.net> adds:

raven> On Thu, 14 Apr 2005, Jeff Moyer wrote: I'm inpressed, this looks
raven> like a fine piece of work guys.  It's going to take a while to work
raven> through this.

raven> Also This is bound to make merging Denis Vlasenkos' logging patches
raven> really hard. So there's potentially a fair bit of work in this.

Well, you can go ahead and apply his patches, and I'll merge this stuff in
afterwards.  Just let me know when you have that all set in CVS.

>> Hi, Ian,
>> 
>> Dan Berrange did a spectacular job of diagnosing some autofs hangs he
>> was experiencing.  The automount daemon would appear hung, and an an
>> strace of the process would show sys_futex on the call stack.  Through
>> his debugging, he figured out that the problem was autofs issuing syslog
>> calls while in a signal handler.

raven> One, probably stupid question (humour me).

raven> Shouldn't we be able use syslog in a signal handler?

Nope:

http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04

>> So, there are a few ways to fix this that come to mind.
>> 
>> o We could not log in signal handlers.  I don't consider this an
>> acceptable solution, as we really need the debug messages generated
>> there.  o We could defer logging to non-signal handler context.  This is
>> in fact what the attached pattch does.  It queues the syslog messages,
>> and flushes them upon the next syslog that occurs outside of signal
>> handler context.  o We could open /dev/log directly.  This is likely a
>> bad idea as there is no standard for the interface there.  o We could
>> have a separate logging process, which we write to via a pipe.  I'm not
>> keen on this as it adds yet another process, and makes shutdown that
>> much more complicated.
>> 

raven> I actually like the last option but a multi threaded autofs is not
raven> likely to happen for a long time.

It does not imply threading.  You can simply fork().

raven> Would another option be to make autofs signal safe by doing all the
raven> work in subprocesses?

There are a number of ways to address this by changing the control
structure of the daemon.  However, that seemed way to invasive at this
stage of the game.  The ideal solution is to do less in the sig_child
handler, deferring the work just like we do for all of the other caught
signals.

>> Note that in all of the above cases, we still need to implement a
>> signal-safe vsprintf.  That is what the bulk of this patch is.

raven> What about a signal safe syslog?  Is this something we push to the
raven> libc guys, both a signal safe vsprintf and syslog?

Well, we can certainly try, but this won't gain you anything in the short
term.

I'll think more about having a separate logging process.  If it seems easy
enough to implement, I'll post a first whack at it.  In the mean time, I
think we should continue to discuss our alternatives.

-Jeff

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch, rfc] fix hung automounter issues
  2005-04-15 11:58   ` Jeff Moyer
@ 2005-04-16  4:46     ` raven
  0 siblings, 0 replies; 16+ messages in thread
From: raven @ 2005-04-16  4:46 UTC (permalink / raw)
  To: Jeff Moyer; +Cc: autofs mailing list, Dan.Berrange

On Fri, 15 Apr 2005, Jeff Moyer wrote:

> ==> Regarding Re: [patch, rfc] fix hung automounter issues; Ian Kent <raven@themaw.net> adds:
>
> raven> On Thu, 14 Apr 2005, Jeff Moyer wrote: I'm inpressed, this looks
> raven> like a fine piece of work guys.  It's going to take a while to work
> raven> through this.
>
> raven> Also This is bound to make merging Denis Vlasenkos' logging patches
> raven> really hard. So there's potentially a fair bit of work in this.
>
> Well, you can go ahead and apply his patches, and I'll merge this stuff in
> afterwards.  Just let me know when you have that all set in CVS.
>
>>> Hi, Ian,
>>>
>>> Dan Berrange did a spectacular job of diagnosing some autofs hangs he
>>> was experiencing.  The automount daemon would appear hung, and an an
>>> strace of the process would show sys_futex on the call stack.  Through
>>> his debugging, he figured out that the problem was autofs issuing syslog
>>> calls while in a signal handler.
>
> raven> One, probably stupid question (humour me).
>
> raven> Shouldn't we be able use syslog in a signal handler?
>
> Nope:
>
> http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04

I have to plead guilty to the screw up (at least I'm not the only 
guilty party) here and point out that there's also a few ioctl calls in 
the signal handling execution paths.

I should be able to the move the ioctl in ST_SHUTDOWN_PENDING to the 
st_shutdown_pending function but moving them in send_ready and send_fail 
will be somewhat harder.

What else have we missed?

>
>>> So, there are a few ways to fix this that come to mind.
>>>
>>> o We could not log in signal handlers.  I don't consider this an
>>> acceptable solution, as we really need the debug messages generated
>>> there.  o We could defer logging to non-signal handler context.  This is
>>> in fact what the attached pattch does.  It queues the syslog messages,
>>> and flushes them upon the next syslog that occurs outside of signal
>>> handler context.  o We could open /dev/log directly.  This is likely a
>>> bad idea as there is no standard for the interface there.  o We could
>>> have a separate logging process, which we write to via a pipe.  I'm not
>>> keen on this as it adds yet another process, and makes shutdown that
>>> much more complicated.
>>>
>
> raven> I actually like the last option but a multi threaded autofs is not
> raven> likely to happen for a long time.
>
> It does not imply threading.  You can simply fork().
>
> raven> Would another option be to make autofs signal safe by doing all the
> raven> work in subprocesses?
>
> There are a number of ways to address this by changing the control
> structure of the daemon.  However, that seemed way to invasive at this
> stage of the game.  The ideal solution is to do less in the sig_child
> handler, deferring the work just like we do for all of the other caught
> signals.
>
>>> Note that in all of the above cases, we still need to implement a
>>> signal-safe vsprintf.  That is what the bulk of this patch is.
>
> raven> What about a signal safe syslog?  Is this something we push to the
> raven> libc guys, both a signal safe vsprintf and syslog?
>
> Well, we can certainly try, but this won't gain you anything in the short
> term.
>
> I'll think more about having a separate logging process.  If it seems easy
> enough to implement, I'll post a first whack at it.  In the mean time, I
> think we should continue to discuss our alternatives.

I must admit, the more I delve into this, the more the deffered logging 
idea grows on me.

Let me look at the patches a while longer.

Ian

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch, rfc] fix hung automounter issues
  2005-04-14 12:55 [patch, rfc] fix hung automounter issues Jeff Moyer
  2005-04-15  1:59 ` Ian Kent
@ 2005-04-18 13:26 ` raven
  2005-04-18 13:38   ` raven
  2005-04-19  0:08   ` Jeff Moyer
  1 sibling, 2 replies; 16+ messages in thread
From: raven @ 2005-04-18 13:26 UTC (permalink / raw)
  To: Jeff Moyer; +Cc: autofs, Dan.Berrange

On Thu, 14 Apr 2005, Jeff Moyer wrote:

> Hi, Ian,
>
> Dan Berrange did a spectacular job of diagnosing some autofs hangs he was
> experiencing.  The automount daemon would appear hung, and an an strace of
> the process would show sys_futex on the call stack.  Through his
> debugging, he figured out that the problem was autofs issuing syslog calls
> while in a signal handler.
>
> So, there are a few ways to fix this that come to mind.
>
> o We could not log in signal handlers.  I don't consider this an acceptable
>  solution, as we really need the debug messages generated there.
> o We could defer logging to non-signal handler context.  This is in fact
>  what the attached pattch does.  It queues the syslog messages, and
>  flushes them upon the next syslog that occurs outside of signal handler
>  context.
> o We could open /dev/log directly.  This is likely a bad idea as there is
>  no standard for the interface there.
> o We could have a separate logging process, which we write to via a pipe.
>  I'm not keen on this as it adds yet another process, and makes shutdown
>  that much more complicated.
>
> Note that in all of the above cases, we still need to implement a
> signal-safe vsprintf.  That is what the bulk of this patch is.
>
> So, here is a rough take on implementing the second bullet point.  I
> wholesale copied a bunch of code from the linux kernel for doing vsprintf.
> That bit is ugly.  I'd also move the definition of the new logging routines
> into the vsprintf file, and rename it.  In short, this is a proof of
> concept (shown to resolve the issues).  I'm happy to clean it up, but I
> want to be sure that this is the direction we want to go in, first.
>
> Limitations of this approach: we won't flush the logs that were issued in
> signal handler context until another syslog call is made.  One improvement
> that could be made straight-away is to have all of the logging routines
> call flush_logs, even if the log priority is set low enough that they
> wouldn't otherwise log.
>
> Comments encouraged.  Thanks again to Dan!

Hi Jeff,

There were a few things missed in the patch as there's a few other places 
that calls to illegal routines are made.

I did a bit of work on it over the weekend.

Basically the changes I have made are:

- moved calls to signal_children out of signal handler.
- created seperate module for safe_syslog implementation.
- changed assert to use safe_syslog also.
- removed assertions from safe logging routines as it also calls
   (safe_)syslog.
- added attempt at last gasp message in queue_syslog.
- added some string termination logic to queue_syslog.

There's probably stuff that I've missed.
I've done some basic testing but more is needed.

Can you review this and alter as you see fit please Jeff?

This just leaves the ioctl calls.
I'm hoping we can verify they are safe. We can check the kernel control 
path and if we can verify that the glibc code is suitably reentrant they 
are probably OK. I'm having trouble finding the right code in glibc. Do 
you have a glibc person that could point us in the right direction Jeff?

We probably should agree on a name for the patch to avoid confusion later.
I've called mine autofs-4.1.4-signal-safe-logging.patch. What name do you 
like?

Ian

diff -Nurp autofs-4.1.4.orig/daemon/automount.c autofs-4.1.4/daemon/automount.c
--- autofs-4.1.4.orig/daemon/automount.c	2005-03-06 17:43:55.000000000 +0800
+++ autofs-4.1.4/daemon/automount.c	2005-04-18 20:59:43.000000000 +0800
@@ -5,7 +5,7 @@
   *
   *   Copyright 1997 Transmeta Corporation - All Rights Reserved
   *   Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
- *   Copyright 2001-2003 Ian Kent <raven@themaw.net>
+ *   Copyright 2001-2005 Ian Kent <raven@themaw.net>
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
@@ -39,7 +39,14 @@
  #include <linux/auto_fs4.h>

  #ifndef NDEBUG
-#define assert(x)	do { if (!(x)) { syslog(LOG_CRIT, __FILE__ ":%d: assertion failed: " #x, __LINE__); } } while(0)
+#define assert(x) 							   \
+	do { 								   \
+		if (!(x)) {						   \
+			safe_logger(LOG_CRIT,				   \
+				    __FILE__ ":%d: assertion failed: " #x, \
+				    __LINE__);				   \
+		}							   \
+	} while(0)
  #else
  #define assert(x)	do { } while(0)
  #endif
@@ -58,14 +65,12 @@ int kproto_sub_version = 0;	/* Kernel pr

  static int submount = 0;

-int do_verbose = 0;		/* Verbose feedback option */
-int do_debug = 0;		/* Enable full debug output */
-
  sigset_t ready_sigs;		/* signals only accepted in ST_READY */
  sigset_t lock_sigs;		/* signals blocked for locking */
  sigset_t sigchld_mask;

  struct autofs_point ap;
+extern volatile int in_interrupt;

  volatile struct pending_mount *junk_mounts = NULL;

@@ -490,6 +495,7 @@ static void sig_statemachine(int sig)
  	int save_errno = errno;
  	enum states next = ap.state;

+	in_interrupt++;
  	switch (sig) {
  	default:		/* all the "can't happen" signals */
  		error("process %d got unexpected signal %d!", getpid(), sig);
@@ -521,6 +527,7 @@ static void sig_statemachine(int sig)
  	debug("sig %d switching from %d to %d", sig, ap.state, next);

  	errno = save_errno;
+	in_interrupt--;
  }

  static int send_ready(unsigned int wait_queue_token)
@@ -565,7 +572,7 @@ static enum states handle_child(int hang

  		/* Check to see if expire process finished */
  		if (pid == ap.exp_process) {
-			int success, ret;
+			int success;

  			if (!WIFEXITED(status))
  				continue;
@@ -594,15 +601,8 @@ static enum states handle_child(int hang

  			case ST_SHUTDOWN_PENDING:
  				next = ST_SHUTDOWN;
-				if (success) {
-					ret = ioctl(ap.ioctlfd,
-						AUTOFS_IOC_ASKUMOUNT, &status);
-					if (!ret) {
-						if (status)
-							break;
-					} else
-						break;
-				}
+				if (success)
+					break;

  				/* Failed shutdown returns to ready */
  				warn("can't shutdown: filesystem %s still busy",
@@ -661,6 +661,7 @@ static void sig_child(int sig)
  	int save_errno = errno;
  	enum states next;

+	in_interrupt++;
  	if (sig != SIGCHLD)
  		return;

@@ -669,6 +670,7 @@ static void sig_child(int sig)
  		nextstate(next);

  	errno = save_errno;
+	in_interrupt--;
  }

  static int st_ready(void)
@@ -1487,6 +1489,7 @@ static void sig_supervisor(int sig)
  {
  	int save_errno = errno;

+	in_interrupt++;
  	switch (sig) {
  	default:		/* all the signals not handled */
  		error("process %d got unexpected signal %d!", getpid(), sig);
@@ -1495,13 +1498,11 @@ static void sig_supervisor(int sig)

  	case SIGTERM:
  	case SIGUSR2:
-		/* Tell everyone to finish up */
-		signal_children(sig);
+		ap.state = ST_SHUTDOWN_PENDING;
  		break;

  	case SIGUSR1:
-		/* Pass on the prune event and ignore self signal */
-		signal_children(sig);
+		ap.state = ST_PRUNE;
  		break;

  	case SIGCHLD:
@@ -1509,20 +1510,18 @@ static void sig_supervisor(int sig)
  		break;

  	case SIGHUP:
-		ap.lookup->lookup_ghost(ap.path, ap.ghost, 0, ap.lookup->context);
-
  		/* Pass on the reread event and ignore self signal */
-		kill(0, SIGHUP);
-		discard_pending(SIGHUP);
-
+		ap.state = ST_READMAP;
  		break;
  	}
  	errno = save_errno;
+	in_interrupt--;
  }

  int supervisor(char *path)
  {
  	unsigned int map = 0;
+	int ret;

  	ap.path = alloca(strlen(path) + 1);
  	strcpy(ap.path, path);
@@ -1538,6 +1537,37 @@ int supervisor(char *path)

  	setup_signals(sig_supervisor, sig_supervisor);

+	while (ap.state != ST_SHUTDOWN) {
+		switch (ap.state) {
+		case ST_READMAP:
+			st_readmap();
+			signal_children(SIGHUP);
+			ap.state = ST_READY;
+			break;
+		case ST_SHUTDOWN_PENDING:
+			ret = signal_children(SIGUSR2);
+			if (!ret) {
+				ap.state = ST_SHUTDOWN;
+				break;
+			}
+
+			/* Failed shutdown returns to ready */
+			warn("can't shutdown: filesystem %s still busy",
+				     ap.path);
+			ap.state = ST_READY;
+			break;
+		case ST_PRUNE:
+			/* Pass on the prune event and ignore self signal */
+			signal_children(SIGUSR1);
+			ap.state = ST_READY;
+			break;
+		default:
+			ap.state = ST_READY;
+			break;
+		}
+		sleep(1);
+	}
+
  	while (waitpid(0, NULL, 0) > 0);

  	return 0;
@@ -1644,8 +1674,23 @@ int handle_mounts(char *path)
  		kill(my_pid, SIGSTOP);

  	while (ap.state != ST_SHUTDOWN) {
-		if (handle_packet() && errno != EINTR)
-			break;
+		if (handle_packet() && errno != EINTR) {
+			int ret, status = 0;
+
+			ret = ioctl(ap.ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status);
+			/* 
+			 * If the ioctl fails assume the kernel doesn't have
+			 * AUTOFS_IOC_ASKUMOUNT and just continue.
+			 */
+			if (!ret && status)
+				break;
+
+			/* Failed shutdown returns to ready */
+			warn("can't shutdown: filesystem %s still busy",
+				     ap.path);
+			ap.state = ST_READY;
+			alarm(ap.exp_runfreq);
+		}
  	}

  	/* Mop up remaining kids */
diff -Nurp autofs-4.1.4.orig/include/automount.h autofs-4.1.4/include/automount.h
--- autofs-4.1.4.orig/include/automount.h	2005-01-26 21:03:02.000000000 +0800
+++ autofs-4.1.4/include/automount.h	2005-04-17 21:07:27.000000000 +0800
@@ -109,7 +109,7 @@ struct autofs_point {
  	volatile pid_t exp_process;		/* Process that is currently expiring */
  	volatile struct pending_mount *mounts;	/* Pending mount queue */
  	struct lookup_mod *lookup;		/* Lookup module */
-	enum states state;
+	volatile enum states state;
  	int state_pipe[2];
  	unsigned dir_created;		/* Was a directory created for this
  					   mount? */
@@ -283,24 +283,53 @@ int has_fstab_option(const char *path, c
  int allow_owner_mount(const char *);

  /* log notification */
-extern int do_verbose;
-extern int do_debug;
-
-#define info(msg, args...) 		\
-if (do_verbose || do_debug) 		\
-	syslog(LOG_INFO, msg, ##args);
-
-#define warn(msg, args...) 			\
-if (do_verbose || do_debug) 		\
-	syslog(LOG_WARNING, msg, ##args);
-
-#define error(msg, args...)	syslog(LOG_ERR, msg, ##args);
-
-#define crit(msg, args...)	syslog(LOG_CRIT, msg, ##args);
-
-#define debug(msg, args...) 		\
-if (do_debug) 				\
-	syslog(LOG_DEBUG, msg, ##args);
+extern int do_verbose;		/* Verbose feedback option */
+extern int do_debug;		/* Enable full debug output */
+void safe_logger(int priority, const char *format, ...);
+
+#define debug(msg, args...)				\
+do {							\
+	if (do_debug)					\
+		safe_logger(LOG_DEBUG, msg, ##args);	\
+} while (0)
+
+#define info(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		safe_logger(LOG_INFO, msg, ##args);	\
+} while (0)
+
+#define notice(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		safe_logger(LOG_NOTICE, msg, ##args);	\
+} while (0)
+
+#define warn(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		safe_logger(LOG_WARNING, msg, ##args);	\
+} while (0)
+
+#define error(msg, args...)				\
+do {							\
+	safe_logger(LOG_ERR, msg, ##args);		\
+} while (0)
+
+#define crit(msg, args...)				\
+do {							\
+	safe_logger(LOG_CRIT, msg, ##args);		\
+} while (0)
+
+#define alert(msg, args...)				\
+do {							\
+	safe_logger(LOG_ALERT, msg, ##args);		\
+} while (0)
+
+#define emerg(msg, args...)				\
+do {							\
+	safe_logger(LOG_EMERG, msg, ##args);		\
+} while (0)

  #endif

diff -Nurp autofs-4.1.4.orig/lib/Makefile autofs-4.1.4/lib/Makefile
--- autofs-4.1.4.orig/lib/Makefile	2005-01-09 17:16:43.000000000 +0800
+++ autofs-4.1.4/lib/Makefile	2005-04-17 15:41:54.000000000 +0800
@@ -9,10 +9,12 @@ include ../Makefile.rules
  RPCGEN = /usr/bin/rpcgen
  RANLIB = /usr/bin/ranlib

-SRCS = cache.c listmount.c cat_path.c rpc_subs.c mounts.c lock.c
+SRCS = cache.c listmount.c cat_path.c rpc_subs.c mounts.c lock.c \
+	safe_logger.c vsprintf.c
  RPCS = mount.h mount_clnt.c mount_xdr.c
  OBJS = cache.o mount_clnt.o mount_xdr.o listmount.o \
-	cat_path.o rpc_subs.o mounts.o lock.o
+	cat_path.o rpc_subs.o mounts.o lock.o \
+	safe_logger.o vsprintf.o

  LIB = autofs.a

diff -Nurp autofs-4.1.4.orig/lib/safe_logger.c autofs-4.1.4/lib/safe_logger.c
--- autofs-4.1.4.orig/lib/safe_logger.c	1970-01-01 08:00:00.000000000 +0800
+++ autofs-4.1.4/lib/safe_logger.c	2005-04-17 21:22:52.000000000 +0800
@@ -0,0 +1,116 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ * 
+ *  safe_logger.c - module to provide signal safe syslog
+ *
+ *   Copyright 2005 Jeff Moyer <jmoyer@redhat.com> - All Rights Reserved
+ *   Copyright 2005 Ian Kent <raven@themaw.net> - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ *   USA; either version 2 of the License, or (at your option) any later
+ *   version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+
+int do_verbose = 0;		/* Verbose feedback option */
+int do_debug = 0;		/* Enable full debug output */
+
+#define LOGQUEUE_LINE_LEN 256
+#define LOGQUEUE_MAX      256
+
+struct log_queue {
+	int  pri;
+	char buf[LOGQUEUE_LINE_LEN];
+};
+static struct log_queue logbuf[LOGQUEUE_MAX + 1];
+static int ringlen = 0;
+
+volatile int in_interrupt = 0;	/* keeps us from logging in a signal handler */
+
+extern int vsnprintf_int(char *buf, size_t size, const char *fmt, va_list args);
+
+static void block_signals(sigset_t *set)
+{
+	sigset_t allsigs;
+
+	sigfillset(&allsigs);
+	sigprocmask(SIG_BLOCK, &allsigs, set);
+}
+
+static void unblock_signals(sigset_t *set)
+{
+	sigprocmask(SIG_SETMASK, set, NULL);
+}
+
+static void flush_log(void)
+{
+	int i;
+
+	for (i = 0; i < ringlen; i++)
+		syslog(logbuf[i].pri, logbuf[i].buf);
+	ringlen = 0;
+}
+
+static void queue_syslog(int priority, const char *format, va_list args)
+{
+	struct log_queue *rp;
+	int written;
+	sigset_t set;
+
+	block_signals(&set);
+	if (ringlen >= LOGQUEUE_MAX) {
+		/* At least attempt to give some info */
+		ringlen = LOGQUEUE_MAX;
+		flush_log();
+		/* Last gasp message */
+		syslog(LOG_EMERG, "fatal: logbuffer overflow"); 
+		unblock_signals(&set);
+#ifdef DEBUG
+		/*
+		 * We want to know if we are exceeding the max number of
+		 * log entrise.
+		 */
+		*(void *)0 = 0;
+#else
+		 return;
+#endif
+	}
+	rp = &logbuf[ringlen];
+	ringlen++;
+	unblock_signals(&set);
+
+	rp->pri = priority;
+	written = vsnprintf_int(rp->buf, LOGQUEUE_LINE_LEN, format, args);
+	if (written >= LOGQUEUE_LINE_LEN)
+		rp->buf[LOGQUEUE_LINE_LEN - 1] = '\0';
+	else
+		rp->buf[written] = '\0';
+}
+
+void safe_logger(int priority, const char *format, ...)
+{
+	va_list args;
+	sigset_t set;
+
+	va_start(args, format);
+	if (in_interrupt)
+		queue_syslog(priority, format, args);
+	else {
+		block_signals(&set);
+		flush_log();
+		vsyslog(priority, format, args);
+		unblock_signals(&set);
+	}
+	va_end(args);
+}
+
diff -Nurp autofs-4.1.4.orig/lib/vsprintf.c autofs-4.1.4/lib/vsprintf.c
--- autofs-4.1.4.orig/lib/vsprintf.c	1970-01-01 08:00:00.000000000 +0800
+++ autofs-4.1.4/lib/vsprintf.c	2005-04-17 13:01:12.000000000 +0800
@@ -0,0 +1,594 @@
+/*
+ *  Stolen from the linux kernel.
+ *
+ *  License: GPL
+ */
+/*------------------ Original Copyright -----------------*/
+/*
+ *  linux/lib/vsprintf.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
+/*
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
+ */
+
+/* 
+ * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
+ * - changed to provide snprintf and vsnprintf functions
+ * So Feb  1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de>
+ * - scnprintf and vscnprintf
+ */
+
+/* Also copied from: */
+
+/*
+ *  linux/lib/string.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * -  Added strsep() which will replace strtok() soon (because strsep() is
+ *    reentrant and should be faster). Use only strsep() in new code, please.
+ *
+ * * Sat Feb 09 2002, Jason Thomas <jason@topic.com.au>,
+ *                    Matthew Hawkins <matt@mh.dropbear.id.au>
+ * -  Kissed strtok() goodbye
+ */
+/*-------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#define BITS_PER_LONG	__WORDSIZE
+#define PAGE_SIZE	getpagesize()
+
+
+#if BITS_PER_LONG == 64
+
+# define do_div(n,base) ({					\
+	uint32_t __base = (base);				\
+	uint32_t __rem;						\
+	__rem = ((uint64_t)(n)) % __base;			\
+	(n) = ((uint64_t)(n)) / __base;				\
+	__rem;							\
+ })
+
+#elif BITS_PER_LONG == 32
+
+/* Not needed on 64bit architectures */
+uint32_t __div64_32(uint64_t *n, uint32_t base)
+{
+	uint64_t rem = *n;
+	uint64_t b = base;
+	uint64_t res, d = 1;
+	uint32_t high = rem >> 32;
+
+	/* Reduce the thing a bit first */
+	res = 0;
+	if (high >= base) {
+		high /= base;
+		res = (uint64_t) high << 32;
+		rem -= (uint64_t) (high*base) << 32;
+	}
+
+	while ((int64_t)b > 0 && b < rem) {
+		b = b+b;
+		d = d+d;
+	}
+
+	do {
+		if (rem >= b) {
+			rem -= b;
+			res += d;
+		}
+		b >>= 1;
+		d >>= 1;
+	} while (d);
+
+	*n = res;
+	return rem;
+}
+
+/* The unnecessary pointer compare is there
+ * to check for type safety (n must be 64bit)
+ */
+# define do_div(n,base) ({				\
+	uint32_t __base = (base);			\
+	uint32_t __rem;					\
+	(void)(((typeof((n)) *)0) == ((uint64_t *)0));	\
+	if (((n) >> 32) == 0) {				\
+		__rem = (uint32_t)(n) % __base;		\
+		(n) = (uint32_t)(n) / __base;		\
+	} else 						\
+		__rem = __div64_32(&(n), __base);	\
+	__rem;						\
+ })
+
+# else
+
+# error do_div() does not yet support the C64
+
+#endif /* BITS_PER_LONG */
+
+
+/**
+ * strnlen - Find the length of a length-limited string
+ * @s: The string to be sized
+ * @count: The maximum number of bytes to search
+ */
+size_t strnlen(const char * s, size_t count)
+{
+	const char *sc;
+
+	for (sc = s; count-- && *sc != '\0'; ++sc)
+		/* nothing */;
+	return sc - s;
+}
+
+/**
+ * simple_strtoul - convert a string to an unsigned long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
+{
+	unsigned long result = 0,value;
+
+	if (!base) {
+		base = 10;
+		if (*cp == '0') {
+			base = 8;
+			cp++;
+			if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
+				cp++;
+				base = 16;
+			}
+		}
+	} else if (base == 16) {
+		if (cp[0] == '0' && toupper(cp[1]) == 'X')
+			cp += 2;
+	}
+	while (isxdigit(*cp) &&
+	       (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
+		result = result*base + value;
+		cp++;
+	}
+	if (endp)
+		*endp = (char *)cp;
+	return result;
+}
+
+/**
+ * simple_strtol - convert a string to a signed long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long simple_strtol(const char *cp,char **endp,unsigned int base)
+{
+	if(*cp=='-')
+		return -simple_strtoul(cp+1,endp,base);
+	return simple_strtoul(cp,endp,base);
+}
+
+/**
+ * simple_strtoull - convert a string to an unsigned long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base)
+{
+	unsigned long long result = 0,value;
+
+	if (!base) {
+		base = 10;
+		if (*cp == '0') {
+			base = 8;
+			cp++;
+			if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
+				cp++;
+				base = 16;
+			}
+		}
+	} else if (base == 16) {
+		if (cp[0] == '0' && toupper(cp[1]) == 'X')
+			cp += 2;
+	}
+	while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
+	    ? toupper(*cp) : *cp)-'A'+10) < base) {
+		result = result*base + value;
+		cp++;
+	}
+	if (endp)
+		*endp = (char *)cp;
+	return result;
+}
+
+/**
+ * simple_strtoll - convert a string to a signed long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long long simple_strtoll(const char *cp,char **endp,unsigned int base)
+{
+	if(*cp=='-')
+		return -simple_strtoull(cp+1,endp,base);
+	return simple_strtoull(cp,endp,base);
+}
+
+static int skip_atoi(const char **s)
+{
+	int i=0;
+
+	while (isdigit(**s))
+		i = i*10 + *((*s)++) - '0';
+	return i;
+}
+
+#define ZEROPAD	1		/* pad with zero */
+#define SIGN	2		/* unsigned/signed long */
+#define PLUS	4		/* show plus */
+#define SPACE	8		/* space if plus */
+#define LEFT	16		/* left justified */
+#define SPECIAL	32		/* 0x */
+#define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */
+
+static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type)
+{
+	char c,sign,tmp[66];
+	const char *digits;
+	static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+	static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	int i;
+
+	digits = (type & LARGE) ? large_digits : small_digits;
+	if (type & LEFT)
+		type &= ~ZEROPAD;
+	if (base < 2 || base > 36)
+		return NULL;
+	c = (type & ZEROPAD) ? '0' : ' ';
+	sign = 0;
+	if (type & SIGN) {
+		if ((signed long long) num < 0) {
+			sign = '-';
+			num = - (signed long long) num;
+			size--;
+		} else if (type & PLUS) {
+			sign = '+';
+			size--;
+		} else if (type & SPACE) {
+			sign = ' ';
+			size--;
+		}
+	}
+	if (type & SPECIAL) {
+		if (base == 16)
+			size -= 2;
+		else if (base == 8)
+			size--;
+	}
+	i = 0;
+	if (num == 0)
+		tmp[i++]='0';
+	else while (num != 0)
+		tmp[i++] = digits[do_div(num,base)];
+	if (i > precision)
+		precision = i;
+	size -= precision;
+	if (!(type&(ZEROPAD+LEFT))) {
+		while(size-->0) {
+			if (buf <= end)
+				*buf = ' ';
+			++buf;
+		}
+	}
+	if (sign) {
+		if (buf <= end)
+			*buf = sign;
+		++buf;
+	}
+	if (type & SPECIAL) {
+		if (base==8) {
+			if (buf <= end)
+				*buf = '0';
+			++buf;
+		} else if (base==16) {
+			if (buf <= end)
+				*buf = '0';
+			++buf;
+			if (buf <= end)
+				*buf = digits[33];
+			++buf;
+		}
+	}
+	if (!(type & LEFT)) {
+		while (size-- > 0) {
+			if (buf <= end)
+				*buf = c;
+			++buf;
+		}
+	}
+	while (i < precision--) {
+		if (buf <= end)
+			*buf = '0';
+		++buf;
+	}
+	while (i-- > 0) {
+		if (buf <= end)
+			*buf = tmp[i];
+		++buf;
+	}
+	while (size-- > 0) {
+		if (buf <= end)
+			*buf = ' ';
+		++buf;
+	}
+	return buf;
+}
+
+/**
+ * vsnprintf_int - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * The return value is the number of characters which would
+ * be generated for the given input, excluding the trailing
+ * '\0', as per ISO C99. If you want to have the exact
+ * number of characters written into @buf as return value
+ * (not including the trailing '\0'), use vscnprintf. If the
+ * return is greater than or equal to @size, the resulting
+ * string is truncated.
+ *
+ * Call this function if you are already dealing with a va_list.
+ * You probably want snprintf instead.
+ */
+int vsnprintf_int(char *buf, size_t size, const char *fmt, va_list args)
+{
+	int len;
+	unsigned long long num;
+	int i, base;
+	char *str, *end, c;
+	const char *s;
+
+	int flags;		/* flags to number() */
+
+	int field_width;	/* width of output field */
+	int precision;		/* min. # of digits for integers; max
+				   number of chars for from string */
+	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
+				/* 'z' support added 23/7/1999 S.H.    */
+				/* 'z' changed to 'Z' --davidm 1/25/99 */
+
+	/* Reject out-of-range values early */
+	if ((int) size < 0)
+		return 0;
+
+	str = buf;
+	end = buf + size - 1;
+
+	if (end < buf - 1) {
+		end = ((void *) -1);
+		size = end - buf + 1;
+	}
+
+	for (; *fmt ; ++fmt) {
+		if (*fmt != '%') {
+			if (str <= end)
+				*str = *fmt;
+			++str;
+			continue;
+		}
+
+		/* process flags */
+		flags = 0;
+		repeat:
+			++fmt;		/* this also skips first '%' */
+			switch (*fmt) {
+				case '-': flags |= LEFT; goto repeat;
+				case '+': flags |= PLUS; goto repeat;
+				case ' ': flags |= SPACE; goto repeat;
+				case '#': flags |= SPECIAL; goto repeat;
+				case '0': flags |= ZEROPAD; goto repeat;
+			}
+
+		/* get field width */
+		field_width = -1;
+		if (isdigit(*fmt))
+			field_width = skip_atoi(&fmt);
+		else if (*fmt == '*') {
+			++fmt;
+			/* it's the next argument */
+			field_width = va_arg(args, int);
+			if (field_width < 0) {
+				field_width = -field_width;
+				flags |= LEFT;
+			}
+		}
+
+		/* get the precision */
+		precision = -1;
+		if (*fmt == '.') {
+			++fmt; 
+			if (isdigit(*fmt))
+				precision = skip_atoi(&fmt);
+			else if (*fmt == '*') {
+				++fmt;
+				/* it's the next argument */
+				precision = va_arg(args, int);
+			}
+			if (precision < 0)
+				precision = 0;
+		}
+
+		/* get the conversion qualifier */
+		qualifier = -1;
+		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
+		    *fmt =='Z' || *fmt == 'z') {
+			qualifier = *fmt;
+			++fmt;
+			if (qualifier == 'l' && *fmt == 'l') {
+				qualifier = 'L';
+				++fmt;
+			}
+		}
+
+		/* default base */
+		base = 10;
+
+		switch (*fmt) {
+			case 'c':
+				if (!(flags & LEFT)) {
+					while (--field_width > 0) {
+						if (str <= end)
+							*str = ' ';
+						++str;
+					}
+				}
+				c = (unsigned char) va_arg(args, int);
+				if (str <= end)
+					*str = c;
+				++str;
+				while (--field_width > 0) {
+					if (str <= end)
+						*str = ' ';
+					++str;
+				}
+				continue;
+
+			case 's':
+				s = va_arg(args, char *);
+				if ((unsigned long)s < PAGE_SIZE)
+					s = "<NULL>";
+
+				len = strnlen(s, precision);
+
+				if (!(flags & LEFT)) {
+					while (len < field_width--) {
+						if (str <= end)
+							*str = ' ';
+						++str;
+					}
+				}
+				for (i = 0; i < len; ++i) {
+					if (str <= end)
+						*str = *s;
+					++str; ++s;
+				}
+				while (len < field_width--) {
+					if (str <= end)
+						*str = ' ';
+					++str;
+				}
+				continue;
+
+			case 'p':
+				if (field_width == -1) {
+					field_width = 2*sizeof(void *);
+					flags |= ZEROPAD;
+				}
+				str = number(str, end,
+						(unsigned long) va_arg(args, void *),
+						16, field_width, precision, flags);
+				continue;
+
+
+			case 'n':
+				/* FIXME:
+				* What does C99 say about the overflow case here? */
+				if (qualifier == 'l') {
+					long * ip = va_arg(args, long *);
+					*ip = (str - buf);
+				} else if (qualifier == 'Z' || qualifier == 'z') {
+					size_t * ip = va_arg(args, size_t *);
+					*ip = (str - buf);
+				} else {
+					int * ip = va_arg(args, int *);
+					*ip = (str - buf);
+				}
+				continue;
+
+			case '%':
+				if (str <= end)
+					*str = '%';
+				++str;
+				continue;
+
+				/* integer number formats - set up the flags and "break" */
+			case 'o':
+				base = 8;
+				break;
+
+			case 'X':
+				flags |= LARGE;
+			case 'x':
+				base = 16;
+				break;
+
+			case 'd':
+			case 'i':
+				flags |= SIGN;
+			case 'u':
+				break;
+
+			default:
+				if (str <= end)
+					*str = '%';
+				++str;
+				if (*fmt) {
+					if (str <= end)
+						*str = *fmt;
+					++str;
+				} else {
+					--fmt;
+				}
+				continue;
+		}
+		if (qualifier == 'L')
+			num = va_arg(args, long long);
+		else if (qualifier == 'l') {
+			num = va_arg(args, unsigned long);
+			if (flags & SIGN)
+				num = (signed long) num;
+		} else if (qualifier == 'Z' || qualifier == 'z') {
+			num = va_arg(args, size_t);
+		} else if (qualifier == 'h') {
+			num = (unsigned short) va_arg(args, int);
+			if (flags & SIGN)
+				num = (signed short) num;
+		} else {
+			num = va_arg(args, unsigned int);
+			if (flags & SIGN)
+				num = (signed int) num;
+		}
+		str = number(str, end, num, base,
+				field_width, precision, flags);
+	}
+	if (str <= end)
+		*str = '\0';
+	else if (size > 0)
+		/* don't write out a null byte if the buf size is zero */
+		*end = '\0';
+	/* the trailing null byte doesn't count towards the total
+	* ++str;
+	*/
+	return str-buf;
+}

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Re: [patch, rfc] fix hung automounter issues
  2005-04-18 13:26 ` raven
@ 2005-04-18 13:38   ` raven
  2005-04-18 13:45     ` Jeff Moyer
  2005-04-18 23:55     ` Jeff Moyer
  2005-04-19  0:08   ` Jeff Moyer
  1 sibling, 2 replies; 16+ messages in thread
From: raven @ 2005-04-18 13:38 UTC (permalink / raw)
  To: Jeff Moyer; +Cc: autofs mailing list, Dan.Berrange


On Mon, 18 Apr 2005 raven@themaw.net wrote:

Hang on, I think I've got a problem with white space and my mailer.

If you have problems with the patch in the previous mail try this one 
instead (maybe just use this one).

diff -Nurp autofs-4.1.4.orig/daemon/automount.c autofs-4.1.4/daemon/automount.c
--- autofs-4.1.4.orig/daemon/automount.c	2005-03-06 17:43:55.000000000 +0800
+++ autofs-4.1.4/daemon/automount.c	2005-04-18 20:59:43.000000000 +0800
@@ -5,7 +5,7 @@
   *
   *   Copyright 1997 Transmeta Corporation - All Rights Reserved
   *   Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
- *   Copyright 2001-2003 Ian Kent <raven@themaw.net>
+ *   Copyright 2001-2005 Ian Kent <raven@themaw.net>
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
@@ -39,7 +39,14 @@
  #include <linux/auto_fs4.h>

  #ifndef NDEBUG
-#define assert(x)	do { if (!(x)) { syslog(LOG_CRIT, __FILE__ ":%d: assertion failed: " #x, __LINE__); } } while(0)
+#define assert(x) 							   \
+	do { 								   \
+		if (!(x)) {						   \
+			safe_logger(LOG_CRIT,				   \
+				    __FILE__ ":%d: assertion failed: " #x, \
+				    __LINE__);				   \
+		}							   \
+	} while(0)
  #else
  #define assert(x)	do { } while(0)
  #endif
@@ -58,14 +65,12 @@ int kproto_sub_version = 0;	/* Kernel pr

  static int submount = 0;

-int do_verbose = 0;		/* Verbose feedback option */
-int do_debug = 0;		/* Enable full debug output */
-
  sigset_t ready_sigs;		/* signals only accepted in ST_READY */
  sigset_t lock_sigs;		/* signals blocked for locking */
  sigset_t sigchld_mask;

  struct autofs_point ap;
+extern volatile int in_interrupt;

  volatile struct pending_mount *junk_mounts = NULL;

@@ -490,6 +495,7 @@ static void sig_statemachine(int sig)
  	int save_errno = errno;
  	enum states next = ap.state;

+	in_interrupt++;
  	switch (sig) {
  	default:		/* all the "can't happen" signals */
  		error("process %d got unexpected signal %d!", getpid(), sig);
@@ -521,6 +527,7 @@ static void sig_statemachine(int sig)
  	debug("sig %d switching from %d to %d", sig, ap.state, next);

  	errno = save_errno;
+	in_interrupt--;
  }

  static int send_ready(unsigned int wait_queue_token)
@@ -565,7 +572,7 @@ static enum states handle_child(int hang

  		/* Check to see if expire process finished */
  		if (pid == ap.exp_process) {
-			int success, ret;
+			int success;

  			if (!WIFEXITED(status))
  				continue;
@@ -594,15 +601,8 @@ static enum states handle_child(int hang

  			case ST_SHUTDOWN_PENDING:
  				next = ST_SHUTDOWN;
-				if (success) {
-					ret = ioctl(ap.ioctlfd,
-						AUTOFS_IOC_ASKUMOUNT, &status);
-					if (!ret) {
-						if (status)
-							break;
-					} else
-						break;
-				}
+				if (success)
+					break;

  				/* Failed shutdown returns to ready */
  				warn("can't shutdown: filesystem %s still busy",
@@ -661,6 +661,7 @@ static void sig_child(int sig)
  	int save_errno = errno;
  	enum states next;

+	in_interrupt++;
  	if (sig != SIGCHLD)
  		return;

@@ -669,6 +670,7 @@ static void sig_child(int sig)
  		nextstate(next);

  	errno = save_errno;
+	in_interrupt--;
  }

  static int st_ready(void)
@@ -1487,6 +1489,7 @@ static void sig_supervisor(int sig)
  {
  	int save_errno = errno;

+	in_interrupt++;
  	switch (sig) {
  	default:		/* all the signals not handled */
  		error("process %d got unexpected signal %d!", getpid(), sig);
@@ -1495,13 +1498,11 @@ static void sig_supervisor(int sig)

  	case SIGTERM:
  	case SIGUSR2:
-		/* Tell everyone to finish up */
-		signal_children(sig);
+		ap.state = ST_SHUTDOWN_PENDING;
  		break;

  	case SIGUSR1:
-		/* Pass on the prune event and ignore self signal */
-		signal_children(sig);
+		ap.state = ST_PRUNE;
  		break;

  	case SIGCHLD:
@@ -1509,20 +1510,18 @@ static void sig_supervisor(int sig)
  		break;

  	case SIGHUP:
-		ap.lookup->lookup_ghost(ap.path, ap.ghost, 0, ap.lookup->context);
-
  		/* Pass on the reread event and ignore self signal */
-		kill(0, SIGHUP);
-		discard_pending(SIGHUP);
-
+		ap.state = ST_READMAP;
  		break;
  	}
  	errno = save_errno;
+	in_interrupt--;
  }

  int supervisor(char *path)
  {
  	unsigned int map = 0;
+	int ret;

  	ap.path = alloca(strlen(path) + 1);
  	strcpy(ap.path, path);
@@ -1538,6 +1537,37 @@ int supervisor(char *path)

  	setup_signals(sig_supervisor, sig_supervisor);

+	while (ap.state != ST_SHUTDOWN) {
+		switch (ap.state) {
+		case ST_READMAP:
+			st_readmap();
+			signal_children(SIGHUP);
+			ap.state = ST_READY;
+			break;
+		case ST_SHUTDOWN_PENDING:
+			ret = signal_children(SIGUSR2);
+			if (!ret) {
+				ap.state = ST_SHUTDOWN;
+				break;
+			}
+
+			/* Failed shutdown returns to ready */
+			warn("can't shutdown: filesystem %s still busy",
+				     ap.path);
+			ap.state = ST_READY;
+			break;
+		case ST_PRUNE:
+			/* Pass on the prune event and ignore self signal */
+			signal_children(SIGUSR1);
+			ap.state = ST_READY;
+			break;
+		default:
+			ap.state = ST_READY;
+			break;
+		}
+		sleep(1);
+	}
+
  	while (waitpid(0, NULL, 0) > 0);

  	return 0;
@@ -1644,8 +1674,23 @@ int handle_mounts(char *path)
  		kill(my_pid, SIGSTOP);

  	while (ap.state != ST_SHUTDOWN) {
-		if (handle_packet() && errno != EINTR)
-			break;
+		if (handle_packet() && errno != EINTR) {
+			int ret, status = 0;
+
+			ret = ioctl(ap.ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status);
+			/* 
+			 * If the ioctl fails assume the kernel doesn't have
+			 * AUTOFS_IOC_ASKUMOUNT and just continue.
+			 */
+			if (!ret && status)
+				break;
+
+			/* Failed shutdown returns to ready */
+			warn("can't shutdown: filesystem %s still busy",
+				     ap.path);
+			ap.state = ST_READY;
+			alarm(ap.exp_runfreq);
+		}
  	}

  	/* Mop up remaining kids */
diff -Nurp autofs-4.1.4.orig/include/automount.h autofs-4.1.4/include/automount.h
--- autofs-4.1.4.orig/include/automount.h	2005-01-26 21:03:02.000000000 +0800
+++ autofs-4.1.4/include/automount.h	2005-04-17 21:07:27.000000000 +0800
@@ -109,7 +109,7 @@ struct autofs_point {
  	volatile pid_t exp_process;		/* Process that is currently expiring */
  	volatile struct pending_mount *mounts;	/* Pending mount queue */
  	struct lookup_mod *lookup;		/* Lookup module */
-	enum states state;
+	volatile enum states state;
  	int state_pipe[2];
  	unsigned dir_created;		/* Was a directory created for this
  					   mount? */
@@ -283,24 +283,53 @@ int has_fstab_option(const char *path, c
  int allow_owner_mount(const char *);

  /* log notification */
-extern int do_verbose;
-extern int do_debug;
-
-#define info(msg, args...) 		\
-if (do_verbose || do_debug) 		\
-	syslog(LOG_INFO, msg, ##args);
-
-#define warn(msg, args...) 			\
-if (do_verbose || do_debug) 		\
-	syslog(LOG_WARNING, msg, ##args);
-
-#define error(msg, args...)	syslog(LOG_ERR, msg, ##args);
-
-#define crit(msg, args...)	syslog(LOG_CRIT, msg, ##args);
-
-#define debug(msg, args...) 		\
-if (do_debug) 				\
-	syslog(LOG_DEBUG, msg, ##args);
+extern int do_verbose;		/* Verbose feedback option */
+extern int do_debug;		/* Enable full debug output */
+void safe_logger(int priority, const char *format, ...);
+
+#define debug(msg, args...)				\
+do {							\
+	if (do_debug)					\
+		safe_logger(LOG_DEBUG, msg, ##args);	\
+} while (0)
+
+#define info(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		safe_logger(LOG_INFO, msg, ##args);	\
+} while (0)
+
+#define notice(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		safe_logger(LOG_NOTICE, msg, ##args);	\
+} while (0)
+
+#define warn(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		safe_logger(LOG_WARNING, msg, ##args);	\
+} while (0)
+
+#define error(msg, args...)				\
+do {							\
+	safe_logger(LOG_ERR, msg, ##args);		\
+} while (0)
+
+#define crit(msg, args...)				\
+do {							\
+	safe_logger(LOG_CRIT, msg, ##args);		\
+} while (0)
+
+#define alert(msg, args...)				\
+do {							\
+	safe_logger(LOG_ALERT, msg, ##args);		\
+} while (0)
+
+#define emerg(msg, args...)				\
+do {							\
+	safe_logger(LOG_EMERG, msg, ##args);		\
+} while (0)

  #endif

diff -Nurp autofs-4.1.4.orig/lib/Makefile autofs-4.1.4/lib/Makefile
--- autofs-4.1.4.orig/lib/Makefile	2005-01-09 17:16:43.000000000 +0800
+++ autofs-4.1.4/lib/Makefile	2005-04-17 15:41:54.000000000 +0800
@@ -9,10 +9,12 @@ include ../Makefile.rules
  RPCGEN = /usr/bin/rpcgen
  RANLIB = /usr/bin/ranlib

-SRCS = cache.c listmount.c cat_path.c rpc_subs.c mounts.c lock.c
+SRCS = cache.c listmount.c cat_path.c rpc_subs.c mounts.c lock.c \
+	safe_logger.c vsprintf.c
  RPCS = mount.h mount_clnt.c mount_xdr.c
  OBJS = cache.o mount_clnt.o mount_xdr.o listmount.o \
-	cat_path.o rpc_subs.o mounts.o lock.o
+	cat_path.o rpc_subs.o mounts.o lock.o \
+	safe_logger.o vsprintf.o

  LIB = autofs.a

diff -Nurp autofs-4.1.4.orig/lib/safe_logger.c autofs-4.1.4/lib/safe_logger.c
--- autofs-4.1.4.orig/lib/safe_logger.c	1970-01-01 08:00:00.000000000 +0800
+++ autofs-4.1.4/lib/safe_logger.c	2005-04-17 21:22:52.000000000 +0800
@@ -0,0 +1,116 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ * 
+ *  safe_logger.c - module to provide signal safe syslog
+ *
+ *   Copyright 2005 Jeff Moyer <jmoyer@redhat.com> - All Rights Reserved
+ *   Copyright 2005 Ian Kent <raven@themaw.net> - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ *   USA; either version 2 of the License, or (at your option) any later
+ *   version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+
+int do_verbose = 0;		/* Verbose feedback option */
+int do_debug = 0;		/* Enable full debug output */
+
+#define LOGQUEUE_LINE_LEN 256
+#define LOGQUEUE_MAX      256
+
+struct log_queue {
+	int  pri;
+	char buf[LOGQUEUE_LINE_LEN];
+};
+static struct log_queue logbuf[LOGQUEUE_MAX + 1];
+static int ringlen = 0;
+
+volatile int in_interrupt = 0;	/* keeps us from logging in a signal handler */
+
+extern int vsnprintf_int(char *buf, size_t size, const char *fmt, va_list args);
+
+static void block_signals(sigset_t *set)
+{
+	sigset_t allsigs;
+
+	sigfillset(&allsigs);
+	sigprocmask(SIG_BLOCK, &allsigs, set);
+}
+
+static void unblock_signals(sigset_t *set)
+{
+	sigprocmask(SIG_SETMASK, set, NULL);
+}
+
+static void flush_log(void)
+{
+	int i;
+
+	for (i = 0; i < ringlen; i++)
+		syslog(logbuf[i].pri, logbuf[i].buf);
+	ringlen = 0;
+}
+
+static void queue_syslog(int priority, const char *format, va_list args)
+{
+	struct log_queue *rp;
+	int written;
+	sigset_t set;
+
+	block_signals(&set);
+	if (ringlen >= LOGQUEUE_MAX) {
+		/* At least attempt to give some info */
+		ringlen = LOGQUEUE_MAX;
+		flush_log();
+		/* Last gasp message */
+		syslog(LOG_EMERG, "fatal: logbuffer overflow"); 
+		unblock_signals(&set);
+#ifdef DEBUG
+		/*
+		 * We want to know if we are exceeding the max number of
+		 * log entrise.
+		 */
+		*(void *)0 = 0;
+#else
+		 return;
+#endif
+	}
+	rp = &logbuf[ringlen];
+	ringlen++;
+	unblock_signals(&set);
+
+	rp->pri = priority;
+	written = vsnprintf_int(rp->buf, LOGQUEUE_LINE_LEN, format, args);
+	if (written >= LOGQUEUE_LINE_LEN)
+		rp->buf[LOGQUEUE_LINE_LEN - 1] = '\0';
+	else
+		rp->buf[written] = '\0';
+}
+
+void safe_logger(int priority, const char *format, ...)
+{
+	va_list args;
+	sigset_t set;
+
+	va_start(args, format);
+	if (in_interrupt)
+		queue_syslog(priority, format, args);
+	else {
+		block_signals(&set);
+		flush_log();
+		vsyslog(priority, format, args);
+		unblock_signals(&set);
+	}
+	va_end(args);
+}
+
diff -Nurp autofs-4.1.4.orig/lib/vsprintf.c autofs-4.1.4/lib/vsprintf.c
--- autofs-4.1.4.orig/lib/vsprintf.c	1970-01-01 08:00:00.000000000 +0800
+++ autofs-4.1.4/lib/vsprintf.c	2005-04-17 13:01:12.000000000 +0800
@@ -0,0 +1,594 @@
+/*
+ *  Stolen from the linux kernel.
+ *
+ *  License: GPL
+ */
+/*------------------ Original Copyright -----------------*/
+/*
+ *  linux/lib/vsprintf.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
+/*
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
+ */
+
+/* 
+ * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
+ * - changed to provide snprintf and vsnprintf functions
+ * So Feb  1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de>
+ * - scnprintf and vscnprintf
+ */
+
+/* Also copied from: */
+
+/*
+ *  linux/lib/string.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * -  Added strsep() which will replace strtok() soon (because strsep() is
+ *    reentrant and should be faster). Use only strsep() in new code, please.
+ *
+ * * Sat Feb 09 2002, Jason Thomas <jason@topic.com.au>,
+ *                    Matthew Hawkins <matt@mh.dropbear.id.au>
+ * -  Kissed strtok() goodbye
+ */
+/*-------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#define BITS_PER_LONG	__WORDSIZE
+#define PAGE_SIZE	getpagesize()
+
+
+#if BITS_PER_LONG == 64
+
+# define do_div(n,base) ({					\
+	uint32_t __base = (base);				\
+	uint32_t __rem;						\
+	__rem = ((uint64_t)(n)) % __base;			\
+	(n) = ((uint64_t)(n)) / __base;				\
+	__rem;							\
+ })
+
+#elif BITS_PER_LONG == 32
+
+/* Not needed on 64bit architectures */
+uint32_t __div64_32(uint64_t *n, uint32_t base)
+{
+	uint64_t rem = *n;
+	uint64_t b = base;
+	uint64_t res, d = 1;
+	uint32_t high = rem >> 32;
+
+	/* Reduce the thing a bit first */
+	res = 0;
+	if (high >= base) {
+		high /= base;
+		res = (uint64_t) high << 32;
+		rem -= (uint64_t) (high*base) << 32;
+	}
+
+	while ((int64_t)b > 0 && b < rem) {
+		b = b+b;
+		d = d+d;
+	}
+
+	do {
+		if (rem >= b) {
+			rem -= b;
+			res += d;
+		}
+		b >>= 1;
+		d >>= 1;
+	} while (d);
+
+	*n = res;
+	return rem;
+}
+
+/* The unnecessary pointer compare is there
+ * to check for type safety (n must be 64bit)
+ */
+# define do_div(n,base) ({				\
+	uint32_t __base = (base);			\
+	uint32_t __rem;					\
+	(void)(((typeof((n)) *)0) == ((uint64_t *)0));	\
+	if (((n) >> 32) == 0) {				\
+		__rem = (uint32_t)(n) % __base;		\
+		(n) = (uint32_t)(n) / __base;		\
+	} else 						\
+		__rem = __div64_32(&(n), __base);	\
+	__rem;						\
+ })
+
+# else
+
+# error do_div() does not yet support the C64
+
+#endif /* BITS_PER_LONG */
+
+
+/**
+ * strnlen - Find the length of a length-limited string
+ * @s: The string to be sized
+ * @count: The maximum number of bytes to search
+ */
+size_t strnlen(const char * s, size_t count)
+{
+	const char *sc;
+
+	for (sc = s; count-- && *sc != '\0'; ++sc)
+		/* nothing */;
+	return sc - s;
+}
+
+/**
+ * simple_strtoul - convert a string to an unsigned long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
+{
+	unsigned long result = 0,value;
+
+	if (!base) {
+		base = 10;
+		if (*cp == '0') {
+			base = 8;
+			cp++;
+			if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
+				cp++;
+				base = 16;
+			}
+		}
+	} else if (base == 16) {
+		if (cp[0] == '0' && toupper(cp[1]) == 'X')
+			cp += 2;
+	}
+	while (isxdigit(*cp) &&
+	       (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
+		result = result*base + value;
+		cp++;
+	}
+	if (endp)
+		*endp = (char *)cp;
+	return result;
+}
+
+/**
+ * simple_strtol - convert a string to a signed long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long simple_strtol(const char *cp,char **endp,unsigned int base)
+{
+	if(*cp=='-')
+		return -simple_strtoul(cp+1,endp,base);
+	return simple_strtoul(cp,endp,base);
+}
+
+/**
+ * simple_strtoull - convert a string to an unsigned long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base)
+{
+	unsigned long long result = 0,value;
+
+	if (!base) {
+		base = 10;
+		if (*cp == '0') {
+			base = 8;
+			cp++;
+			if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
+				cp++;
+				base = 16;
+			}
+		}
+	} else if (base == 16) {
+		if (cp[0] == '0' && toupper(cp[1]) == 'X')
+			cp += 2;
+	}
+	while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
+	    ? toupper(*cp) : *cp)-'A'+10) < base) {
+		result = result*base + value;
+		cp++;
+	}
+	if (endp)
+		*endp = (char *)cp;
+	return result;
+}
+
+/**
+ * simple_strtoll - convert a string to a signed long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long long simple_strtoll(const char *cp,char **endp,unsigned int base)
+{
+	if(*cp=='-')
+		return -simple_strtoull(cp+1,endp,base);
+	return simple_strtoull(cp,endp,base);
+}
+
+static int skip_atoi(const char **s)
+{
+	int i=0;
+
+	while (isdigit(**s))
+		i = i*10 + *((*s)++) - '0';
+	return i;
+}
+
+#define ZEROPAD	1		/* pad with zero */
+#define SIGN	2		/* unsigned/signed long */
+#define PLUS	4		/* show plus */
+#define SPACE	8		/* space if plus */
+#define LEFT	16		/* left justified */
+#define SPECIAL	32		/* 0x */
+#define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */
+
+static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type)
+{
+	char c,sign,tmp[66];
+	const char *digits;
+	static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+	static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	int i;
+
+	digits = (type & LARGE) ? large_digits : small_digits;
+	if (type & LEFT)
+		type &= ~ZEROPAD;
+	if (base < 2 || base > 36)
+		return NULL;
+	c = (type & ZEROPAD) ? '0' : ' ';
+	sign = 0;
+	if (type & SIGN) {
+		if ((signed long long) num < 0) {
+			sign = '-';
+			num = - (signed long long) num;
+			size--;
+		} else if (type & PLUS) {
+			sign = '+';
+			size--;
+		} else if (type & SPACE) {
+			sign = ' ';
+			size--;
+		}
+	}
+	if (type & SPECIAL) {
+		if (base == 16)
+			size -= 2;
+		else if (base == 8)
+			size--;
+	}
+	i = 0;
+	if (num == 0)
+		tmp[i++]='0';
+	else while (num != 0)
+		tmp[i++] = digits[do_div(num,base)];
+	if (i > precision)
+		precision = i;
+	size -= precision;
+	if (!(type&(ZEROPAD+LEFT))) {
+		while(size-->0) {
+			if (buf <= end)
+				*buf = ' ';
+			++buf;
+		}
+	}
+	if (sign) {
+		if (buf <= end)
+			*buf = sign;
+		++buf;
+	}
+	if (type & SPECIAL) {
+		if (base==8) {
+			if (buf <= end)
+				*buf = '0';
+			++buf;
+		} else if (base==16) {
+			if (buf <= end)
+				*buf = '0';
+			++buf;
+			if (buf <= end)
+				*buf = digits[33];
+			++buf;
+		}
+	}
+	if (!(type & LEFT)) {
+		while (size-- > 0) {
+			if (buf <= end)
+				*buf = c;
+			++buf;
+		}
+	}
+	while (i < precision--) {
+		if (buf <= end)
+			*buf = '0';
+		++buf;
+	}
+	while (i-- > 0) {
+		if (buf <= end)
+			*buf = tmp[i];
+		++buf;
+	}
+	while (size-- > 0) {
+		if (buf <= end)
+			*buf = ' ';
+		++buf;
+	}
+	return buf;
+}
+
+/**
+ * vsnprintf_int - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * The return value is the number of characters which would
+ * be generated for the given input, excluding the trailing
+ * '\0', as per ISO C99. If you want to have the exact
+ * number of characters written into @buf as return value
+ * (not including the trailing '\0'), use vscnprintf. If the
+ * return is greater than or equal to @size, the resulting
+ * string is truncated.
+ *
+ * Call this function if you are already dealing with a va_list.
+ * You probably want snprintf instead.
+ */
+int vsnprintf_int(char *buf, size_t size, const char *fmt, va_list args)
+{
+	int len;
+	unsigned long long num;
+	int i, base;
+	char *str, *end, c;
+	const char *s;
+
+	int flags;		/* flags to number() */
+
+	int field_width;	/* width of output field */
+	int precision;		/* min. # of digits for integers; max
+				   number of chars for from string */
+	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
+				/* 'z' support added 23/7/1999 S.H.    */
+				/* 'z' changed to 'Z' --davidm 1/25/99 */
+
+	/* Reject out-of-range values early */
+	if ((int) size < 0)
+		return 0;
+
+	str = buf;
+	end = buf + size - 1;
+
+	if (end < buf - 1) {
+		end = ((void *) -1);
+		size = end - buf + 1;
+	}
+
+	for (; *fmt ; ++fmt) {
+		if (*fmt != '%') {
+			if (str <= end)
+				*str = *fmt;
+			++str;
+			continue;
+		}
+
+		/* process flags */
+		flags = 0;
+		repeat:
+			++fmt;		/* this also skips first '%' */
+			switch (*fmt) {
+				case '-': flags |= LEFT; goto repeat;
+				case '+': flags |= PLUS; goto repeat;
+				case ' ': flags |= SPACE; goto repeat;
+				case '#': flags |= SPECIAL; goto repeat;
+				case '0': flags |= ZEROPAD; goto repeat;
+			}
+
+		/* get field width */
+		field_width = -1;
+		if (isdigit(*fmt))
+			field_width = skip_atoi(&fmt);
+		else if (*fmt == '*') {
+			++fmt;
+			/* it's the next argument */
+			field_width = va_arg(args, int);
+			if (field_width < 0) {
+				field_width = -field_width;
+				flags |= LEFT;
+			}
+		}
+
+		/* get the precision */
+		precision = -1;
+		if (*fmt == '.') {
+			++fmt; 
+			if (isdigit(*fmt))
+				precision = skip_atoi(&fmt);
+			else if (*fmt == '*') {
+				++fmt;
+				/* it's the next argument */
+				precision = va_arg(args, int);
+			}
+			if (precision < 0)
+				precision = 0;
+		}
+
+		/* get the conversion qualifier */
+		qualifier = -1;
+		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
+		    *fmt =='Z' || *fmt == 'z') {
+			qualifier = *fmt;
+			++fmt;
+			if (qualifier == 'l' && *fmt == 'l') {
+				qualifier = 'L';
+				++fmt;
+			}
+		}
+
+		/* default base */
+		base = 10;
+
+		switch (*fmt) {
+			case 'c':
+				if (!(flags & LEFT)) {
+					while (--field_width > 0) {
+						if (str <= end)
+							*str = ' ';
+						++str;
+					}
+				}
+				c = (unsigned char) va_arg(args, int);
+				if (str <= end)
+					*str = c;
+				++str;
+				while (--field_width > 0) {
+					if (str <= end)
+						*str = ' ';
+					++str;
+				}
+				continue;
+
+			case 's':
+				s = va_arg(args, char *);
+				if ((unsigned long)s < PAGE_SIZE)
+					s = "<NULL>";
+
+				len = strnlen(s, precision);
+
+				if (!(flags & LEFT)) {
+					while (len < field_width--) {
+						if (str <= end)
+							*str = ' ';
+						++str;
+					}
+				}
+				for (i = 0; i < len; ++i) {
+					if (str <= end)
+						*str = *s;
+					++str; ++s;
+				}
+				while (len < field_width--) {
+					if (str <= end)
+						*str = ' ';
+					++str;
+				}
+				continue;
+
+			case 'p':
+				if (field_width == -1) {
+					field_width = 2*sizeof(void *);
+					flags |= ZEROPAD;
+				}
+				str = number(str, end,
+						(unsigned long) va_arg(args, void *),
+						16, field_width, precision, flags);
+				continue;
+
+
+			case 'n':
+				/* FIXME:
+				* What does C99 say about the overflow case here? */
+				if (qualifier == 'l') {
+					long * ip = va_arg(args, long *);
+					*ip = (str - buf);
+				} else if (qualifier == 'Z' || qualifier == 'z') {
+					size_t * ip = va_arg(args, size_t *);
+					*ip = (str - buf);
+				} else {
+					int * ip = va_arg(args, int *);
+					*ip = (str - buf);
+				}
+				continue;
+
+			case '%':
+				if (str <= end)
+					*str = '%';
+				++str;
+				continue;
+
+				/* integer number formats - set up the flags and "break" */
+			case 'o':
+				base = 8;
+				break;
+
+			case 'X':
+				flags |= LARGE;
+			case 'x':
+				base = 16;
+				break;
+
+			case 'd':
+			case 'i':
+				flags |= SIGN;
+			case 'u':
+				break;
+
+			default:
+				if (str <= end)
+					*str = '%';
+				++str;
+				if (*fmt) {
+					if (str <= end)
+						*str = *fmt;
+					++str;
+				} else {
+					--fmt;
+				}
+				continue;
+		}
+		if (qualifier == 'L')
+			num = va_arg(args, long long);
+		else if (qualifier == 'l') {
+			num = va_arg(args, unsigned long);
+			if (flags & SIGN)
+				num = (signed long) num;
+		} else if (qualifier == 'Z' || qualifier == 'z') {
+			num = va_arg(args, size_t);
+		} else if (qualifier == 'h') {
+			num = (unsigned short) va_arg(args, int);
+			if (flags & SIGN)
+				num = (signed short) num;
+		} else {
+			num = va_arg(args, unsigned int);
+			if (flags & SIGN)
+				num = (signed int) num;
+		}
+		str = number(str, end, num, base,
+				field_width, precision, flags);
+	}
+	if (str <= end)
+		*str = '\0';
+	else if (size > 0)
+		/* don't write out a null byte if the buf size is zero */
+		*end = '\0';
+	/* the trailing null byte doesn't count towards the total
+	* ++str;
+	*/
+	return str-buf;
+}

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Re: [patch, rfc] fix hung automounter issues
  2005-04-18 13:38   ` raven
@ 2005-04-18 13:45     ` Jeff Moyer
  2005-04-18 17:15       ` raven
  2005-04-18 23:55     ` Jeff Moyer
  1 sibling, 1 reply; 16+ messages in thread
From: Jeff Moyer @ 2005-04-18 13:45 UTC (permalink / raw)
  To: raven; +Cc: autofs mailing list, Dan.Berrange

==> Regarding Re: [autofs] Re: [patch, rfc] fix hung automounter issues; raven@themaw.net adds:

raven> On Mon, 18 Apr 2005 raven@themaw.net wrote:

raven> Hang on, I think I've got a problem with white space and my mailer.

raven> If you have problems with the patch in the previous mail try this one 
raven> instead (maybe just use this one).

Oops, we need to disable signals when flushing the logs.  The following is
unsafe:

> +static void flush_log(void)
> +{
> +	int i;
> +
> +	for (i = 0; i < ringlen; i++)
> +		syslog(logbuf[i].pri, logbuf[i].buf);
> +	ringlen = 0;
> +}
> +

-Jeff

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Re: [patch, rfc] fix hung automounter issues
  2005-04-18 13:45     ` Jeff Moyer
@ 2005-04-18 17:15       ` raven
  2005-04-18 17:19         ` Jeff Moyer
  0 siblings, 1 reply; 16+ messages in thread
From: raven @ 2005-04-18 17:15 UTC (permalink / raw)
  To: Jeff Moyer; +Cc: autofs mailing list, Dan.Berrange

On Mon, 18 Apr 2005, Jeff Moyer wrote:

> ==> Regarding Re: [autofs] Re: [patch, rfc] fix hung automounter issues; raven@themaw.net adds:
>
> raven> On Mon, 18 Apr 2005 raven@themaw.net wrote:
>
> raven> Hang on, I think I've got a problem with white space and my mailer.
>
> raven> If you have problems with the patch in the previous mail try this one
> raven> instead (maybe just use this one).
>
> Oops, we need to disable signals when flushing the logs.  The following is
> unsafe:
>
>> +static void flush_log(void)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < ringlen; i++)
>> +		syslog(logbuf[i].pri, logbuf[i].buf);
>> +	ringlen = 0;
>> +}
>> +

But it's static and only called when signals are blocked.

Ian

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Re: [patch, rfc] fix hung automounter issues
  2005-04-18 17:15       ` raven
@ 2005-04-18 17:19         ` Jeff Moyer
  0 siblings, 0 replies; 16+ messages in thread
From: Jeff Moyer @ 2005-04-18 17:19 UTC (permalink / raw)
  To: raven; +Cc: autofs mailing list, Dan.Berrange

==> Regarding Re: [autofs] Re: [patch, rfc] fix hung automounter issues; raven@themaw.net adds:

raven> On Mon, 18 Apr 2005, Jeff Moyer wrote:
>> ==> Regarding Re: [autofs] Re: [patch, rfc] fix hung automounter issues; raven@themaw.net adds:
>> 
raven> On Mon, 18 Apr 2005 raven@themaw.net wrote:
>> 
raven> Hang on, I think I've got a problem with white space and my mailer.
>> 
raven> If you have problems with the patch in the previous mail try this one
raven> instead (maybe just use this one).
>> 
>> Oops, we need to disable signals when flushing the logs.  The following is
>> unsafe:
>> 
>>> +static void flush_log(void)
>>> +{
>>> +	int i;
>>> +
>>> +	for (i = 0; i < ringlen; i++)
>>> +		syslog(logbuf[i].pri, logbuf[i].buf);
>>> +	ringlen = 0;
>>> +}
>>> +

raven> But it's static and only called when signals are blocked.

That's what I get for not going over the whole patch.  Sorry about that!
I'll try to review this version of the patch either today or tomorrow.

-Jeff

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Re: [patch, rfc] fix hung automounter issues
  2005-04-18 13:38   ` raven
  2005-04-18 13:45     ` Jeff Moyer
@ 2005-04-18 23:55     ` Jeff Moyer
  2005-04-19  2:56       ` Ian Kent
                         ` (2 more replies)
  1 sibling, 3 replies; 16+ messages in thread
From: Jeff Moyer @ 2005-04-18 23:55 UTC (permalink / raw)
  To: raven; +Cc: autofs mailing list, Dan.Berrange

==> Regarding Re: [autofs] Re: [patch, rfc] fix hung automounter issues; raven@themaw.net adds:

raven> On Mon, 18 Apr 2005 raven@themaw.net wrote:

raven> Hang on, I think I've got a problem with white space and my mailer.

raven> If you have problems with the patch in the previous mail try this one 
raven> instead (maybe just use this one).

Okay, I've reviewed all parts except for the actual logging functions.  I'm
actually not convinced this is the right approach.  The overhead of setting
up and tearing down the signal mask is quite expensive.  I fear that
environments with a great number of mounts will fall over when debugging is
turned on, and that is the very thing we are trying to save.  Perhaps it is
worth another look at a separate logging process.

Having said that, the rest of your patch is still relevant.  I've spent a
good deal of time looking it over, and here are my comments.

@@ -58,14 +65,12 @@ int kproto_sub_version = 0;	/* Kernel pr

  static int submount = 0;

-int do_verbose = 0;		/* Verbose feedback option */
-int do_debug = 0;		/* Enable full debug output */
-
  sigset_t ready_sigs;		/* signals only accepted in ST_READY */
  sigset_t lock_sigs;		/* signals blocked for locking */
  sigset_t sigchld_mask;

  struct autofs_point ap;
+extern volatile int in_interrupt;

Doesn't need to be volatile, so far as I can tell.  Also note that we need
to be careful to reset in_interrupt if we ever fork() from a signal
handler.


@@ -565,7 +572,7 @@ static enum states handle_child(int hang

  		/* Check to see if expire process finished */
  		if (pid == ap.exp_process) {
-			int success, ret;
+			int success;

  			if (!WIFEXITED(status))
  				continue;
@@ -594,15 +601,8 @@ static enum states handle_child(int hang

  			case ST_SHUTDOWN_PENDING:
  				next = ST_SHUTDOWN;
-				if (success) {
-					ret = ioctl(ap.ioctlfd,
-						AUTOFS_IOC_ASKUMOUNT, &status);
-					if (!ret) {
-						if (status)
-							break;
-					} else
-						break;
-				}
+				if (success)
+					break;

  				/* Failed shutdown returns to ready */
  				warn("can't shutdown: filesystem %s still busy",

This would be okay, except that the AUTOFS_IOC_ASKUMOUNT won't always
be run at shutdown.  See below.


@@ -1495,13 +1498,11 @@ static void sig_supervisor(int sig)

  	case SIGTERM:
  	case SIGUSR2:
-		/* Tell everyone to finish up */
-		signal_children(sig);
+		ap.state = ST_SHUTDOWN_PENDING;
  		break;

  	case SIGUSR1:
-		/* Pass on the prune event and ignore self signal */
-		signal_children(sig);
+		ap.state = ST_PRUNE;
  		break;

  	case SIGCHLD:
@@ -1509,20 +1510,18 @@ static void sig_supervisor(int sig)
  		break;

  	case SIGHUP:
-		ap.lookup->lookup_ghost(ap.path, ap.ghost, 0, ap.lookup->context);
-
  		/* Pass on the reread event and ignore self signal */
-		kill(0, SIGHUP);
-		discard_pending(SIGHUP);
-
+		ap.state = ST_READMAP;
  		break;
  	}
  	errno = save_errno;
+	in_interrupt--;
  }

  int supervisor(char *path)
  {
  	unsigned int map = 0;
+	int ret;

  	ap.path = alloca(strlen(path) + 1);
  	strcpy(ap.path, path);
@@ -1538,6 +1537,37 @@ int supervisor(char *path)

  	setup_signals(sig_supervisor, sig_supervisor);

I think we should set state to ST_READY here.  That would make things a bit
more clear.

+	while (ap.state != ST_SHUTDOWN) {
+		switch (ap.state) {
+		case ST_READMAP:
+			st_readmap();

No check of return value?

+			signal_children(SIGHUP);
+			ap.state = ST_READY;
+			break;
+		case ST_SHUTDOWN_PENDING:
+			ret = signal_children(SIGUSR2);
+			if (!ret) {
+				ap.state = ST_SHUTDOWN;
+				break;
+			}
+
+			/* Failed shutdown returns to ready */
+			warn("can't shutdown: filesystem %s still busy",
+				     ap.path);
+			ap.state = ST_READY;
+			break;
+		case ST_PRUNE:
+			/* Pass on the prune event and ignore self signal */
+			signal_children(SIGUSR1);
+			ap.state = ST_READY;
+			break;
+		default:
+			ap.state = ST_READY;
+			break;
+		}
+		sleep(1);
+	}
+
  	while (waitpid(0, NULL, 0) > 0);

  	return 0;

Hmm, this is simply not safe, we can lose signals.  You really need to
either block signals and implement a queue, or use a pipe like we do with
indirect maps.

@@ -1644,8 +1674,23 @@ int handle_mounts(char *path)
  		kill(my_pid, SIGSTOP);

  	while (ap.state != ST_SHUTDOWN) {
-		if (handle_packet() && errno != EINTR)
-			break;
+		if (handle_packet() && errno != EINTR) {

The checking of errno here is completely bogus.  You can simply get rid of
it, I think.  (this is what I was referring to above, about the ioctl not
always being run).

+			int ret, status = 0;
+
+			ret = ioctl(ap.ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status);
+			/* 
+			 * If the ioctl fails assume the kernel doesn't have
+			 * AUTOFS_IOC_ASKUMOUNT and just continue.
+			 */
+			if (!ret && status)
+				break;

Consider handling EINTR for the ioctl call.

+
+			/* Failed shutdown returns to ready */
+			warn("can't shutdown: filesystem %s still busy",
+				     ap.path);
+			ap.state = ST_READY;

I think we should be blocking signals when modifying ap.state.

+			alarm(ap.exp_runfreq);
+		}
  	}

  	/* Mop up remaining kids */


Okay, random tangent:

static void sig_statemachine(int sig)
{
	int save_errno = errno;
	enum states next = ap.state;

	in_interrupt++;
	switch (sig) {
	default:		/* all the "can't happen" signals */
		error("process %d got unexpected signal %d!", getpid(), sig);
		break;
		/* don't FALLTHROUGH */
	...
	}

	debug("sig %d switching from %d to %d", sig, ap.state, next);

	errno = save_errno;
	in_interrupt--;
}

So, for sigsegv, sigbus, sigfpe, etc, we are simply trying to continue?
That doesn't seem right to me, and the comment in setup_signals agrees:

#ifndef DEBUG
	/* When debugging, these signals should be in the default state; when
	   in production, we want to at least attempt to catch them and shut down. */

Also note this from the sigaction man page:
       According to POSIX, the behaviour of a process is  undefined  after  it
       ignores  a  SIGFPE, SIGILL, or SIGSEGV signal that was not generated by
       the kill() or the raise() functions.   Integer  division  by  zero  has
       undefined result.  On some architectures it will generate a SIGFPE sig-
       nal.  (Also dividing the most  negative  integer  by  -1  may  generate
       SIGFPE.)  Ignoring this signal might lead to an endless loop.

Anyway, something to fix for later, I guess.


diff -Nurp autofs-4.1.4.orig/include/automount.h autofs-4.1.4/include/automount.h
--- autofs-4.1.4.orig/include/automount.h	2005-01-26 21:03:02.000000000 +0800
+++ autofs-4.1.4/include/automount.h	2005-04-17 21:07:27.000000000 +0800
@@ -109,7 +109,7 @@ struct autofs_point {
  	volatile pid_t exp_process;		/* Process that is currently expiring */
  	volatile struct pending_mount *mounts;	/* Pending mount queue */
  	struct lookup_mod *lookup;		/* Lookup module */
-	enum states state;
+	volatile enum states state;

Again, why the volatile?  Are you worried that the compiler is going to
optimize out some code?  We certainly don't have issues with multiple
threads accessing the data...

-Jeff

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch, rfc] fix hung automounter issues
  2005-04-18 13:26 ` raven
  2005-04-18 13:38   ` raven
@ 2005-04-19  0:08   ` Jeff Moyer
  2005-04-19  2:35     ` Ian Kent
  1 sibling, 1 reply; 16+ messages in thread
From: Jeff Moyer @ 2005-04-19  0:08 UTC (permalink / raw)
  To: raven; +Cc: autofs, Dan.Berrange

==> Regarding Re: [patch, rfc] fix hung automounter issues; raven@themaw.net adds:

raven> On Thu, 14 Apr 2005, Jeff Moyer wrote:
>> Hi, Ian,
>> 
>> Dan Berrange did a spectacular job of diagnosing some autofs hangs he was
>> experiencing.  The automount daemon would appear hung, and an an strace of
>> the process would show sys_futex on the call stack.  Through his
>> debugging, he figured out that the problem was autofs issuing syslog calls
>> while in a signal handler.
>> 
>> So, there are a few ways to fix this that come to mind.
>> 
>> o We could not log in signal handlers.  I don't consider this an acceptable
>> solution, as we really need the debug messages generated there.
>> o We could defer logging to non-signal handler context.  This is in fact
>> what the attached pattch does.  It queues the syslog messages, and
>> flushes them upon the next syslog that occurs outside of signal handler
>> context.
>> o We could open /dev/log directly.  This is likely a bad idea as there is
>> no standard for the interface there.
>> o We could have a separate logging process, which we write to via a pipe.
>> I'm not keen on this as it adds yet another process, and makes shutdown
>> that much more complicated.
>> 
>> Note that in all of the above cases, we still need to implement a
>> signal-safe vsprintf.  That is what the bulk of this patch is.
>> 
>> So, here is a rough take on implementing the second bullet point.  I
>> wholesale copied a bunch of code from the linux kernel for doing vsprintf.
>> That bit is ugly.  I'd also move the definition of the new logging routines
>> into the vsprintf file, and rename it.  In short, this is a proof of
>> concept (shown to resolve the issues).  I'm happy to clean it up, but I
>> want to be sure that this is the direction we want to go in, first.
>> 
>> Limitations of this approach: we won't flush the logs that were issued in
>> signal handler context until another syslog call is made.  One improvement
>> that could be made straight-away is to have all of the logging routines
>> call flush_logs, even if the log priority is set low enough that they
>> wouldn't otherwise log.
>> 
>> Comments encouraged.  Thanks again to Dan!

raven> Hi Jeff,

raven> There were a few things missed in the patch as there's a few other 
raven> places that calls to illegal routines are made.

raven> I did a bit of work on it over the weekend.

raven> Basically the changes I have made are:

raven> - moved calls to signal_children out of signal handler.
raven> - created seperate module for safe_syslog implementation.
raven> - changed assert to use safe_syslog also.
raven> - removed assertions from safe logging routines as it also calls
raven>    (safe_)syslog.
raven> - added attempt at last gasp message in queue_syslog.
raven> - added some string termination logic to queue_syslog.

raven> There's probably stuff that I've missed.
raven> I've done some basic testing but more is needed.

raven> Can you review this and alter as you see fit please Jeff?

Done, and sent in a separate message.

raven> This just leaves the ioctl calls.
raven> I'm hoping we can verify they are safe. We can check the kernel control 
raven> path and if we can verify that the glibc code is suitably reentrant 
raven> they are probably OK. I'm having trouble finding the right code in 
raven> glibc. Do you have a glibc person that could point us in the right
raven> direction Jeff?

I think you've moved the ioctl calls from signal handler context, right?  I
think this is resolved.

raven> We probably should agree on a name for the patch to avoid confusion 
raven> later. I've called mine autofs-4.1.4-signal-safe-logging.patch. What
raven> name do you like?

That's fine with me.  It certainly isn't important enough to fret over. ;)

-Jeff

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch, rfc] fix hung automounter issues
  2005-04-19  0:08   ` Jeff Moyer
@ 2005-04-19  2:35     ` Ian Kent
  0 siblings, 0 replies; 16+ messages in thread
From: Ian Kent @ 2005-04-19  2:35 UTC (permalink / raw)
  To: Jeff Moyer; +Cc: autofs, Dan.Berrange

On Mon, 18 Apr 2005, Jeff Moyer wrote:

> ==> Regarding Re: [patch, rfc] fix hung automounter issues; raven@themaw.net adds:
> 
> raven> On Thu, 14 Apr 2005, Jeff Moyer wrote:
> >> Hi, Ian,
> >> 
> >> Dan Berrange did a spectacular job of diagnosing some autofs hangs he was
> >> experiencing.  The automount daemon would appear hung, and an an strace of
> >> the process would show sys_futex on the call stack.  Through his
> >> debugging, he figured out that the problem was autofs issuing syslog calls
> >> while in a signal handler.
> >> 
> >> So, there are a few ways to fix this that come to mind.
> >> 
> >> o We could not log in signal handlers.  I don't consider this an acceptable
> >> solution, as we really need the debug messages generated there.
> >> o We could defer logging to non-signal handler context.  This is in fact
> >> what the attached pattch does.  It queues the syslog messages, and
> >> flushes them upon the next syslog that occurs outside of signal handler
> >> context.
> >> o We could open /dev/log directly.  This is likely a bad idea as there is
> >> no standard for the interface there.
> >> o We could have a separate logging process, which we write to via a pipe.
> >> I'm not keen on this as it adds yet another process, and makes shutdown
> >> that much more complicated.
> >> 
> >> Note that in all of the above cases, we still need to implement a
> >> signal-safe vsprintf.  That is what the bulk of this patch is.
> >> 
> >> So, here is a rough take on implementing the second bullet point.  I
> >> wholesale copied a bunch of code from the linux kernel for doing vsprintf.
> >> That bit is ugly.  I'd also move the definition of the new logging routines
> >> into the vsprintf file, and rename it.  In short, this is a proof of
> >> concept (shown to resolve the issues).  I'm happy to clean it up, but I
> >> want to be sure that this is the direction we want to go in, first.
> >> 
> >> Limitations of this approach: we won't flush the logs that were issued in
> >> signal handler context until another syslog call is made.  One improvement
> >> that could be made straight-away is to have all of the logging routines
> >> call flush_logs, even if the log priority is set low enough that they
> >> wouldn't otherwise log.
> >> 
> >> Comments encouraged.  Thanks again to Dan!
> 
> raven> Hi Jeff,
> 
> raven> There were a few things missed in the patch as there's a few other 
> raven> places that calls to illegal routines are made.
> 
> raven> I did a bit of work on it over the weekend.
> 
> raven> Basically the changes I have made are:
> 
> raven> - moved calls to signal_children out of signal handler.
> raven> - created seperate module for safe_syslog implementation.
> raven> - changed assert to use safe_syslog also.
> raven> - removed assertions from safe logging routines as it also calls
> raven>    (safe_)syslog.
> raven> - added attempt at last gasp message in queue_syslog.
> raven> - added some string termination logic to queue_syslog.
> 
> raven> There's probably stuff that I've missed.
> raven> I've done some basic testing but more is needed.
> 
> raven> Can you review this and alter as you see fit please Jeff?
> 
> Done, and sent in a separate message.
> 
> raven> This just leaves the ioctl calls.
> raven> I'm hoping we can verify they are safe. We can check the kernel control 
> raven> path and if we can verify that the glibc code is suitably reentrant 
> raven> they are probably OK. I'm having trouble finding the right code in 
> raven> glibc. Do you have a glibc person that could point us in the right
> raven> direction Jeff?
> 
> I think you've moved the ioctl calls from signal handler context, right?  I
> think this is resolved.

These are the ioctls in the send_ready and send_fail functions.

Ian

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Re: [patch, rfc] fix hung automounter issues
  2005-04-18 23:55     ` Jeff Moyer
@ 2005-04-19  2:56       ` Ian Kent
  2005-04-19 16:06       ` raven
  2005-04-24  6:56       ` raven
  2 siblings, 0 replies; 16+ messages in thread
From: Ian Kent @ 2005-04-19  2:56 UTC (permalink / raw)
  To: Jeff Moyer; +Cc: autofs mailing list, Dan.Berrange

On Mon, 18 Apr 2005, Jeff Moyer wrote:

> 
> Okay, I've reviewed all parts except for the actual logging functions.  I'm
> actually not convinced this is the right approach.  The overhead of setting
> up and tearing down the signal mask is quite expensive.  I fear that
> environments with a great number of mounts will fall over when debugging is
> turned on, and that is the very thing we are trying to save.  Perhaps it is
> worth another look at a separate logging process.
> 

I'm also concerned about using an array to store the log messages.
This could overflow in heavy load mount situations.

Ian

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Re: [patch, rfc] fix hung automounter issues
  2005-04-18 23:55     ` Jeff Moyer
  2005-04-19  2:56       ` Ian Kent
@ 2005-04-19 16:06       ` raven
  2005-04-24  6:56       ` raven
  2 siblings, 0 replies; 16+ messages in thread
From: raven @ 2005-04-19 16:06 UTC (permalink / raw)
  To: Jeff Moyer; +Cc: autofs mailing list, Dan.Berrange

On Mon, 18 Apr 2005, Jeff Moyer wrote:

> ==> Regarding Re: [autofs] Re: [patch, rfc] fix hung automounter issues; raven@themaw.net adds:
>
> raven> On Mon, 18 Apr 2005 raven@themaw.net wrote:
>
> raven> Hang on, I think I've got a problem with white space and my mailer.
>
> raven> If you have problems with the patch in the previous mail try this one
> raven> instead (maybe just use this one).
>
> Okay, I've reviewed all parts except for the actual logging functions.  I'm
> actually not convinced this is the right approach.  The overhead of setting
> up and tearing down the signal mask is quite expensive.  I fear that
> environments with a great number of mounts will fall over when debugging is
> turned on, and that is the very thing we are trying to save.  Perhaps it is
> worth another look at a separate logging process.
>
> Having said that, the rest of your patch is still relevant.  I've spent a
> good deal of time looking it over, and here are my comments.
>
> @@ -58,14 +65,12 @@ int kproto_sub_version = 0;	/* Kernel pr
>
>  static int submount = 0;
>
> -int do_verbose = 0;		/* Verbose feedback option */
> -int do_debug = 0;		/* Enable full debug output */
> -
>  sigset_t ready_sigs;		/* signals only accepted in ST_READY */
>  sigset_t lock_sigs;		/* signals blocked for locking */
>  sigset_t sigchld_mask;
>
>  struct autofs_point ap;
> +extern volatile int in_interrupt;
>
> Doesn't need to be volatile, so far as I can tell.  Also note that we need
> to be careful to reset in_interrupt if we ever fork() from a signal
> handler.

Yes. I've read a bit further and I see this is not needed for integral 
data types.

>
>
> @@ -565,7 +572,7 @@ static enum states handle_child(int hang
>
>  		/* Check to see if expire process finished */
>  		if (pid == ap.exp_process) {
> -			int success, ret;
> +			int success;
>
>  			if (!WIFEXITED(status))
>  				continue;
> @@ -594,15 +601,8 @@ static enum states handle_child(int hang
>
>  			case ST_SHUTDOWN_PENDING:
>  				next = ST_SHUTDOWN;
> -				if (success) {
> -					ret = ioctl(ap.ioctlfd,
> -						AUTOFS_IOC_ASKUMOUNT, &status);
> -					if (!ret) {
> -						if (status)
> -							break;
> -					} else
> -						break;
> -				}
> +				if (success)
> +					break;
>
>  				/* Failed shutdown returns to ready */
>  				warn("can't shutdown: filesystem %s still busy",
>
> This would be okay, except that the AUTOFS_IOC_ASKUMOUNT won't always
> be run at shutdown.  See below.
>
>
> @@ -1495,13 +1498,11 @@ static void sig_supervisor(int sig)
>
>  	case SIGTERM:
>  	case SIGUSR2:
> -		/* Tell everyone to finish up */
> -		signal_children(sig);
> +		ap.state = ST_SHUTDOWN_PENDING;
>  		break;
>
>  	case SIGUSR1:
> -		/* Pass on the prune event and ignore self signal */
> -		signal_children(sig);
> +		ap.state = ST_PRUNE;
>  		break;
>
>  	case SIGCHLD:
> @@ -1509,20 +1510,18 @@ static void sig_supervisor(int sig)
>  		break;
>
>  	case SIGHUP:
> -		ap.lookup->lookup_ghost(ap.path, ap.ghost, 0, ap.lookup->context);
> -
>  		/* Pass on the reread event and ignore self signal */
> -		kill(0, SIGHUP);
> -		discard_pending(SIGHUP);
> -
> +		ap.state = ST_READMAP;
>  		break;
>  	}
>  	errno = save_errno;
> +	in_interrupt--;
>  }
>
>  int supervisor(char *path)
>  {
>  	unsigned int map = 0;
> +	int ret;
>
>  	ap.path = alloca(strlen(path) + 1);
>  	strcpy(ap.path, path);
> @@ -1538,6 +1537,37 @@ int supervisor(char *path)
>
>  	setup_signals(sig_supervisor, sig_supervisor);
>
> I think we should set state to ST_READY here.  That would make things a bit
> more clear.
>
> +	while (ap.state != ST_SHUTDOWN) {
> +		switch (ap.state) {
> +		case ST_READMAP:
> +			st_readmap();
>
> No check of return value?
>
> +			signal_children(SIGHUP);
> +			ap.state = ST_READY;
> +			break;
> +		case ST_SHUTDOWN_PENDING:
> +			ret = signal_children(SIGUSR2);
> +			if (!ret) {
> +				ap.state = ST_SHUTDOWN;
> +				break;
> +			}
> +
> +			/* Failed shutdown returns to ready */
> +			warn("can't shutdown: filesystem %s still busy",
> +				     ap.path);
> +			ap.state = ST_READY;
> +			break;
> +		case ST_PRUNE:
> +			/* Pass on the prune event and ignore self signal */
> +			signal_children(SIGUSR1);
> +			ap.state = ST_READY;
> +			break;
> +		default:
> +			ap.state = ST_READY;
> +			break;
> +		}
> +		sleep(1);
> +	}
> +
>  	while (waitpid(0, NULL, 0) > 0);
>
>  	return 0;
>
> Hmm, this is simply not safe, we can lose signals.  You really need to
> either block signals and implement a queue, or use a pipe like we do with
> indirect maps.
>
> @@ -1644,8 +1674,23 @@ int handle_mounts(char *path)
>  		kill(my_pid, SIGSTOP);
>
>  	while (ap.state != ST_SHUTDOWN) {
> -		if (handle_packet() && errno != EINTR)
> -			break;
> +		if (handle_packet() && errno != EINTR) {
>
> The checking of errno here is completely bogus.  You can simply get rid of
> it, I think.  (this is what I was referring to above, about the ioctl not
> always being run).

Yep. EINTR appears to be handled within the routines themselves.

I wonder if it was ever relavent?

>
> +			int ret, status = 0;
> +
> +			ret = ioctl(ap.ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status);
> +			/*
> +			 * If the ioctl fails assume the kernel doesn't have
> +			 * AUTOFS_IOC_ASKUMOUNT and just continue.
> +			 */
> +			if (!ret && status)
> +				break;
>
> Consider handling EINTR for the ioctl call.

EINTR is not listed as a valid error return for this function.
So shouldn't we expect this call to resume after a signal is delivered?

>
> +
> +			/* Failed shutdown returns to ready */
> +			warn("can't shutdown: filesystem %s still busy",
> +				     ap.path);
> +			ap.state = ST_READY;
>
> I think we should be blocking signals when modifying ap.state.
>
> +			alarm(ap.exp_runfreq);
> +		}
>  	}
>
>  	/* Mop up remaining kids */
>
>
> Okay, random tangent:
>
> static void sig_statemachine(int sig)
> {
> 	int save_errno = errno;
> 	enum states next = ap.state;
>
> 	in_interrupt++;
> 	switch (sig) {
> 	default:		/* all the "can't happen" signals */
> 		error("process %d got unexpected signal %d!", getpid(), sig);
> 		break;
> 		/* don't FALLTHROUGH */
> 	...
> 	}
>
> 	debug("sig %d switching from %d to %d", sig, ap.state, next);
>
> 	errno = save_errno;
> 	in_interrupt--;
> }
>

Ian

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Re: [patch, rfc] fix hung automounter issues
  2005-04-18 23:55     ` Jeff Moyer
  2005-04-19  2:56       ` Ian Kent
  2005-04-19 16:06       ` raven
@ 2005-04-24  6:56       ` raven
  2005-04-24  9:37         ` raven
  2 siblings, 1 reply; 16+ messages in thread
From: raven @ 2005-04-24  6:56 UTC (permalink / raw)
  To: Jeff Moyer; +Cc: autofs mailing list, Dan.Berrange

On Mon, 18 Apr 2005, Jeff Moyer wrote:

>
> Okay, I've reviewed all parts except for the actual logging functions.  I'm
> actually not convinced this is the right approach.  The overhead of setting
> up and tearing down the signal mask is quite expensive.  I fear that
> environments with a great number of mounts will fall over when debugging is
> turned on, and that is the very thing we are trying to save.  Perhaps it is
> worth another look at a separate logging process.
>

I agree.

The buffering approach is not the best way to handle this.

I had a look around and found that the BSD folks have a reentrant syslog. 
I found several posts on lists discussing the use of non-reentrant 
functions, including syslog, in signal handlers. The responses claimed 
that their reentrant syslog would work.

I grabed the source from OpenBSD and tweeked it a bit to get it working.
I've used the kernel printf code from the existing patch and checked all 
the other function calls in glibc. It looks like it may well be signal 
safe.

I've also tried to rationalise the signal setup and take down a bit.
I've also tried to address the things that you picked up previously.

So have a look at the attached patch.

Ian

diff -Nur autofs-4.1.4.orig/daemon/automount.c autofs-4.1.4/daemon/automount.c
--- autofs-4.1.4.orig/daemon/automount.c	2005-03-06 17:43:55.000000000 +0800
+++ autofs-4.1.4/daemon/automount.c	2005-04-24 13:43:54.000000000 +0800
@@ -5,7 +5,7 @@
   *
   *   Copyright 1997 Transmeta Corporation - All Rights Reserved
   *   Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
- *   Copyright 2001-2003 Ian Kent <raven@themaw.net>
+ *   Copyright 2001-2005 Ian Kent <raven@themaw.net>
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
@@ -28,7 +28,6 @@
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
-#include <syslog.h>
  #include <unistd.h>
  #include <sys/ioctl.h>
  #include <sys/types.h>
@@ -38,13 +37,32 @@
  #include <sys/poll.h>
  #include <linux/auto_fs4.h>

+#include "automount.h"
+
  #ifndef NDEBUG
-#define assert(x)	do { if (!(x)) { syslog(LOG_CRIT, __FILE__ ":%d: assertion failed: " #x, __LINE__); } } while(0)
+#define assert(x) 						    \
+	do { 							    \
+		if (!(x)) {					    \
+			crit(__FILE__ ":%d: assertion failed: " #x, \
+				__LINE__);			    \
+		}						    \
+	} while(0)
  #else
  #define assert(x)	do { } while(0)
  #endif

-#include "automount.h"
+#ifndef NDEBUG
+#define assert_r(context, x) 					   \
+	do { 							   \
+		if (!(x)) {					   \
+			crit_r(context,				   \
+				__FILE__ ":%d: assertion failed: ",\
+				__LINE__);			   \
+		}						   \
+	} while(0)
+#else
+#define assert_r(context, x)	do { } while(0)
+#endif

  const char *program;		/* Initialized with argv[0] */
  const char *version = VERSION_STRING;	/* Program version */
@@ -57,7 +75,7 @@
  int kproto_sub_version = 0;	/* Kernel protocol version used */

  static int submount = 0;
-
+
  int do_verbose = 0;		/* Verbose feedback option */
  int do_debug = 0;		/* Enable full debug output */

@@ -66,6 +84,9 @@
  sigset_t sigchld_mask;

  struct autofs_point ap;
+ 
+/* re-entrant syslog default context data */
+#define AUTOFS_SYSLOG_CONTEXT {-1, 0, 0, LOG_PID, (const char *)0, LOG_DAEMON, 0xff};

  volatile struct pending_mount *junk_mounts = NULL;

@@ -480,19 +501,25 @@

  static void nextstate(enum states next)
  {
+	static struct syslog_data syslog_context = AUTOFS_SYSLOG_CONTEXT;
+	static struct syslog_data *slc = &syslog_context;
+
  	if (write(ap.state_pipe[1], &next, sizeof(next)) != sizeof(next))
-		error("nextstate: write failed %m");
+		error_r(slc, "nextstate: write failed %m");
  }

  /* Deal with all the signal-driven events in the state machine */
  static void sig_statemachine(int sig)
  {
+	static struct syslog_data syslog_context = AUTOFS_SYSLOG_CONTEXT;
+	static struct syslog_data *slc = &syslog_context;
  	int save_errno = errno;
  	enum states next = ap.state;

  	switch (sig) {
  	default:		/* all the "can't happen" signals */
-		error("process %d got unexpected signal %d!", getpid(), sig);
+		error_r(slc, "process %d got unexpected signal %d!",
+			getpid(), sig);
  		break;
  		/* don't FALLTHROUGH */

@@ -503,33 +530,36 @@
  		break;

  	case SIGUSR1:
-		assert(ap.state == ST_READY);
+		assert_r(slc, ap.state == ST_READY);
  		nextstate(next = ST_PRUNE);
  		break;

  	case SIGALRM:
-		assert(ap.state == ST_READY);
+		assert_r(slc, ap.state == ST_READY);
  		nextstate(next = ST_EXPIRE);
  		break;

  	case SIGHUP:
-		assert(ap.state == ST_READY);
+		assert_r(slc, ap.state == ST_READY);
  		nextstate(next = ST_READMAP);
  		break;
  	}

-	debug("sig %d switching from %d to %d", sig, ap.state, next);
+	debug_r(slc, "sig %d switching from %d to %d", sig, ap.state, next);

  	errno = save_errno;
  }

  static int send_ready(unsigned int wait_queue_token)
  {
+	static struct syslog_data syslog_context = AUTOFS_SYSLOG_CONTEXT;
+	static struct syslog_data *slc = &syslog_context;
+
  	if (wait_queue_token == 0)
  		return 0;
-	debug("send_ready: token=%d\n", wait_queue_token);
+	debug_r(slc, "send_ready: token=%d\n", wait_queue_token);
  	if (ioctl(ap.ioctlfd, AUTOFS_IOC_READY, wait_queue_token) < 0) {
-		error("AUTOFS_IOC_READY: %m");
+		error_r(slc, "AUTOFS_IOC_READY: %m");
  		return 1;
  	}
  	return 0;
@@ -537,11 +567,14 @@

  static int send_fail(unsigned int wait_queue_token)
  {
+	static struct syslog_data syslog_context = AUTOFS_SYSLOG_CONTEXT;
+	static struct syslog_data *slc = &syslog_context;
+
  	if (wait_queue_token == 0)
  		return 0;
-	debug("send_fail: token=%d\n", wait_queue_token);
+	debug_r(slc, "send_fail: token=%d\n", wait_queue_token);
  	if (ioctl(ap.ioctlfd, AUTOFS_IOC_FAIL, wait_queue_token) < 0) {
-		syslog(LOG_ERR, "AUTOFS_IOC_FAIL: %m");
+		error_r(slc, "AUTOFS_IOC_FAIL: %m");
  		return 1;
  	}
  	return 0;
@@ -552,6 +585,8 @@
     result.  */
  static enum states handle_child(int hang)
  {
+	static struct syslog_data syslog_context = AUTOFS_SYSLOG_CONTEXT;
+	static struct syslog_data *slc = &syslog_context;
  	pid_t pid;
  	int status;
  	enum states next = ST_INVAL;
@@ -559,13 +594,13 @@
  	while ((pid = waitpid(-1, &status, hang ? 0 : WNOHANG)) > 0) {
  		struct pending_mount volatile *mt, *volatile *mtp;

-		debug("handle_child: got pid %d, sig %d (%d), stat %d\n",
+		debug_r(slc, "handle_child: got pid %d, sig %d (%d), stat %d",
  			pid, WIFSIGNALED(status),
  			WTERMSIG(status), WEXITSTATUS(status));

  		/* Check to see if expire process finished */
  		if (pid == ap.exp_process) {
-			int success, ret;
+			int success;

  			if (!WIFEXITED(status))
  				continue;
@@ -594,29 +629,23 @@

  			case ST_SHUTDOWN_PENDING:
  				next = ST_SHUTDOWN;
-				if (success) {
-					ret = ioctl(ap.ioctlfd,
-						AUTOFS_IOC_ASKUMOUNT, &status);
-					if (!ret) {
-						if (status)
-							break;
-					} else
-						break;
-				}
+				if (success)
+					break;

  				/* Failed shutdown returns to ready */
-				warn("can't shutdown: filesystem %s still busy",
-				     ap.path);
+				warn_r(slc,
+			           "can't shutdown: filesystem %s still busy",
+				   ap.path);
  				alarm(ap.exp_runfreq);
  				next = ST_READY;
  				break;

  			default:
-				error("bad state %d", ap.state);
+				error_r(slc, "bad state %d", ap.state);
  			}

  			if (next != ST_INVAL)
-				debug("sigchld: exp "
+				debug_r(slc, "sigchld: exp "
  				     "%d finished, switching from %d to %d",
  				     pid, ap.state, next);

@@ -632,7 +661,7 @@
  			if (!WIFEXITED(status) && !WIFSIGNALED(status))
  				break;

-			debug("sig_child: found pending iop pid %d: "
+			debug_r(slc, "sig_child: found pending iop pid %d: "
  			     "signalled %d (sig %d), exit status %d",
  				pid, WIFSIGNALED(status),
  				WTERMSIG(status), WEXITSTATUS(status));
@@ -673,10 +702,9 @@

  static int st_ready(void)
  {
-	debug("st_ready(): state = %d\n", ap.state);
+	debug("st_ready(): state = %d", ap.state);

  	ap.state = ST_READY;
-	sigprocmask(SIG_UNBLOCK, &lock_sigs, NULL);

  	return 0;
  }
@@ -796,6 +824,7 @@
  	default:
  		debug("expire_proc: exp_proc=%d", f);
  		ap.exp_process = f;
+		sigprocmask(SIG_SETMASK, &old, NULL);
  		return EXP_STARTED;
  	}
  }
@@ -804,10 +833,15 @@
  {
  	int status;

+	assert(ap.state == ST_READY);
+	ap.state = ST_READMAP;
+
  	status = ap.lookup->lookup_ghost(ap.path, ap.ghost, 0, ap.lookup->context);

  	debug("st_readmap: status %d\n", status);

+	ap.state = ST_READY;
+
  	/* If I don't exist in the map any more then exit */
  	if (status == LKP_FAIL)
  		return 0;
@@ -822,15 +856,11 @@
  	info("prep_shutdown: state = %d\n", ap.state);

  	assert(ap.state == ST_READY || ap.state == ST_EXPIRE);
+	ap.state = ST_SHUTDOWN_PENDING;

  	/* Turn off timeouts */
  	alarm(0);

-	/* Prevent any new mounts */
-	sigprocmask(SIG_SETMASK, &lock_sigs, NULL);
-
-	ap.state = ST_SHUTDOWN_PENDING;
-
  	/* Where're the boss, tell everyone to finish up */
  	if (getpid() == getpgrp())
  		signal_children(SIGUSR2);
@@ -844,8 +874,9 @@
  	case EXP_ERROR:
  	case EXP_PARTIAL:
  		/* It didn't work: return to ready */
+		ap.state = ST_READY;
  		alarm(ap.exp_runfreq);
-		return st_ready();
+		return 0;

  	case EXP_DONE:
  		/* All expired: go straight to exit */
@@ -853,8 +884,6 @@
  		return 1;

  	case EXP_STARTED:
-		/* Wait until expiry process finishes */
-		sigprocmask(SIG_SETMASK, &ready_sigs, NULL);
  		return 0;
  	}
  	return 1;
@@ -865,6 +894,7 @@
  	debug("st_prune(): state = %d\n", ap.state);

  	assert(ap.state == ST_READY);
+	ap.state = ST_PRUNE;

  	/* We're the boss, pass on the prune event */
  	if (getpid() == getpgrp()) 
@@ -878,11 +908,10 @@

  	case EXP_ERROR:
  	case EXP_PARTIAL:
+		ap.state = ST_READY;
  		return 1;

  	case EXP_STARTED:
-		ap.state = ST_PRUNE;
-		sigprocmask(SIG_SETMASK, &ready_sigs, NULL);
  		return 0;
  	}
  	return 1;
@@ -893,6 +922,7 @@
  	debug("st_expire(): state = %d\n", ap.state);

  	assert(ap.state == ST_READY);
+	ap.state = ST_EXPIRE;

  	switch (expire_proc(0)) {
  	case EXP_DONE:
@@ -902,12 +932,11 @@

  	case EXP_ERROR:
  	case EXP_PARTIAL:
+		ap.state = ST_READY;
  		alarm(ap.exp_runfreq);
  		return 1;

  	case EXP_STARTED:
-		ap.state = ST_EXPIRE;
-		sigprocmask(SIG_SETMASK, &ready_sigs, NULL);
  		return 0;
  	}
  	return 1;
@@ -947,7 +976,7 @@
  		if (poll(fds, 2, -1) == -1) {
  			if (errno == EINTR)
  				continue;
-			syslog(LOG_ERR, "get_pkt: poll failed: %m");
+			error("get_pkt: poll failed: %m");
  			return -1;
  		}

@@ -958,8 +987,7 @@
  			if (fullread(ap.state_pipe[0], &next_state, sizeof(next_state)))
  				continue;

-			sigprocmask(SIG_BLOCK, &lock_sigs, &old);
-
+			sigprocmask(SIG_BLOCK, &ready_sigs, &old);
  			if (next_state != ap.state) {
  				debug("get_pkt: state %d, next %d",
  					ap.state, next_state);
@@ -999,9 +1027,7 @@
  					      next_state);
  				}
  			}
-
-			if (ret)
-				sigprocmask(SIG_SETMASK, &old, NULL);
+			sigprocmask(SIG_SETMASK, &old, NULL);

  			if (ap.state == ST_SHUTDOWN)
  				return -1;
@@ -1484,24 +1510,25 @@

  /* Deal with the signals recieved by direct mount supervisor */
  static void sig_supervisor(int sig)
-{
+{ 
+	static struct syslog_data syslog_context = AUTOFS_SYSLOG_CONTEXT;
+	static struct syslog_data *slc = &syslog_context;
  	int save_errno = errno;

  	switch (sig) {
  	default:		/* all the signals not handled */
-		error("process %d got unexpected signal %d!", getpid(), sig);
+		error_r(slc, "process %d got unexpected signal %d!",
+			getpid(), sig);
  		return;
  		/* don't FALLTHROUGH */

  	case SIGTERM:
  	case SIGUSR2:
-		/* Tell everyone to finish up */
-		signal_children(sig);
+		ap.state = ST_SHUTDOWN_PENDING;
  		break;

  	case SIGUSR1:
-		/* Pass on the prune event and ignore self signal */
-		signal_children(sig);
+		ap.state = ST_PRUNE;
  		break;

  	case SIGCHLD:
@@ -1509,12 +1536,8 @@
  		break;

  	case SIGHUP:
-		ap.lookup->lookup_ghost(ap.path, ap.ghost, 0, ap.lookup->context);
-
  		/* Pass on the reread event and ignore self signal */
-		kill(0, SIGHUP);
-		discard_pending(SIGHUP);
-
+		ap.state = ST_READMAP;
  		break;
  	}
  	errno = save_errno;
@@ -1522,7 +1545,9 @@

  int supervisor(char *path)
  {
+	sigset_t olds;
  	unsigned int map = 0;
+	int ret;

  	ap.path = alloca(strlen(path) + 1);
  	strcpy(ap.path, path);
@@ -1536,8 +1561,46 @@
  		cleanup_exit(ap.path, 1);
  	}

+	ap.state = ST_READY;
  	setup_signals(sig_supervisor, sig_supervisor);

+	sigprocmask(SIG_BLOCK, &ready_sigs, &olds);
+	while (ap.state != ST_SHUTDOWN) {
+		switch (ap.state) {
+		case ST_READMAP:
+			ret = st_readmap();
+			if (!ret)
+				/* Warn but try to continue anyway */
+				warn("failed to read map");
+			signal_children(SIGHUP);
+			ap.state = ST_READY;
+			break;
+		case ST_SHUTDOWN_PENDING:
+			ret = signal_children(SIGUSR2);
+			if (!ret) {
+				ap.state = ST_SHUTDOWN;
+				sigprocmask(SIG_SETMASK, &olds, NULL);
+				continue;
+			}
+
+			/* Failed shutdown returns to ready */
+			warn("can't shutdown: filesystem %s still busy",
+				     ap.path);
+			ap.state = ST_READY;
+			break;
+		case ST_PRUNE:
+			/* Pass on the prune event and ignore self signal */
+			signal_children(SIGUSR1);
+			ap.state = ST_READY;
+			break;
+		default:
+			ap.state = ST_READY;
+			break;
+		}
+		sigsuspend(&olds);
+	}
+	sigprocmask(SIG_UNBLOCK, &ready_sigs, NULL);
+
  	while (waitpid(0, NULL, 0) > 0);

  	return 0;
@@ -1644,8 +1707,32 @@
  		kill(my_pid, SIGSTOP);

  	while (ap.state != ST_SHUTDOWN) {
-		if (handle_packet() && errno != EINTR)
-			break;
+		if (handle_packet()) {
+			sigset_t olds;
+			int ret, status = 0;
+
+			sigprocmask(SIG_BLOCK, &lock_sigs, &olds);
+			ret = ioctl(ap.ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status);
+			/*
+			 * If the ioctl fails assume the kernel doesn't have
+			 * AUTOFS_IOC_ASKUMOUNT and just continue.
+			 */
+			if (ret) {
+				sigprocmask(SIG_SETMASK, &olds, NULL);
+				break;
+			}
+			if (status) {
+				sigprocmask(SIG_SETMASK, &olds, NULL);
+				break;
+			}
+
+			/* Failed shutdown returns to ready */
+			warn("can't shutdown: filesystem %s still busy",
+					ap.path);
+			alarm(ap.exp_runfreq);
+			ap.state = ST_READY;
+			sigprocmask(SIG_SETMASK, &olds, NULL);
+		}
  	}

  	/* Mop up remaining kids */
diff -Nur autofs-4.1.4.orig/daemon/module.c autofs-4.1.4/daemon/module.c
--- autofs-4.1.4.orig/daemon/module.c	2004-01-30 00:01:22.000000000 +0800
+++ autofs-4.1.4/daemon/module.c	2005-04-23 19:45:36.000000000 +0800
@@ -14,7 +14,6 @@
   * ----------------------------------------------------------------------- */

  #include <stdio.h>
-#include <syslog.h>
  #include <dlfcn.h>
  #include <string.h>
  #include <stdlib.h>
diff -Nur autofs-4.1.4.orig/daemon/mount.c autofs-4.1.4/daemon/mount.c
--- autofs-4.1.4.orig/daemon/mount.c	2004-11-17 22:38:27.000000000 +0800
+++ autofs-4.1.4/daemon/mount.c	2005-04-23 19:45:48.000000000 +0800
@@ -19,7 +19,6 @@
   *
   * ----------------------------------------------------------------------- */

-#include <syslog.h>
  #include <stdlib.h>
  #include <string.h>
  #include "automount.h"
diff -Nur autofs-4.1.4.orig/daemon/spawn.c autofs-4.1.4/daemon/spawn.c
--- autofs-4.1.4.orig/daemon/spawn.c	2005-02-10 20:56:53.000000000 +0800
+++ autofs-4.1.4/daemon/spawn.c	2005-04-23 19:46:01.000000000 +0800
@@ -21,7 +21,6 @@
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
-#include <syslog.h>
  #include <sys/types.h>
  #include <unistd.h>
  #include <time.h>
diff -Nur autofs-4.1.4.orig/include/automount.h autofs-4.1.4/include/automount.h
--- autofs-4.1.4.orig/include/automount.h	2005-01-26 21:03:02.000000000 +0800
+++ autofs-4.1.4/include/automount.h	2005-04-23 21:54:01.000000000 +0800
@@ -15,6 +15,9 @@
  #include <time.h>
  #include "config.h"

+/* OpenBSD re-entrant syslog */
+#include "syslog.h"
+
  /* We MUST have the paths to mount(8) and umount(8) */
  #ifndef HAVE_MOUNT
  #error Failed to locate mount(8)!
@@ -283,24 +286,78 @@
  int allow_owner_mount(const char *);

  /* log notification */
-extern int do_verbose;
-extern int do_debug;
-
-#define info(msg, args...) 		\
-if (do_verbose || do_debug) 		\
-	syslog(LOG_INFO, msg, ##args);
-
-#define warn(msg, args...) 			\
-if (do_verbose || do_debug) 		\
-	syslog(LOG_WARNING, msg, ##args);
-
-#define error(msg, args...)	syslog(LOG_ERR, msg, ##args);
+extern int do_verbose;		/* Verbose feedback option */
+extern int do_debug;		/* Enable full debug output */

-#define crit(msg, args...)	syslog(LOG_CRIT, msg, ##args);
+/* Define non-reentrant logging macros */

-#define debug(msg, args...) 		\
-if (do_debug) 				\
-	syslog(LOG_DEBUG, msg, ##args);
+#define debug(msg, args...)				\
+do {							\
+	if (do_debug)					\
+		syslog(LOG_DEBUG, msg, ##args);		\
+} while (0)
+
+#define info(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		syslog(LOG_INFO, msg, ##args);		\
+} while (0)
+
+#define notice(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		syslog(LOG_NOTICE, msg, ##args);	\
+} while (0)
+
+#define warn(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		syslog(LOG_WARNING, msg, ##args);	\
+} while (0)
+
+#define error(msg, args...)				\
+do {							\
+	syslog(LOG_ERR, msg, ##args);			\
+} while (0)
+
+#define crit(msg, args...)				\
+do {							\
+	syslog(LOG_CRIT, msg, ##args);			\
+} while (0)
+
+#define alert(msg, args...)				\
+do {							\
+	syslog(LOG_ALERT, msg, ##args);			\
+} while (0)
+
+#define emerg(msg, args...)				\
+do {							\
+	syslog(LOG_EMERG, msg, ##args);			\
+} while (0)
+
+/* Define reentrant logging macros for signal handlers */
+
+#define debug_r(context, msg, args...)				\
+do {								\
+	if (do_debug)						\
+		syslog_r(LOG_DEBUG, context, msg, ##args);	\
+} while (0)
+
+#define warn_r(context, msg, args...)				\
+do {								\
+	if (do_verbose || do_debug)				\
+		syslog_r(LOG_WARNING, context, msg, ##args);	\
+} while (0)
+
+#define error_r(context, msg, args...)			\
+do {							\
+	syslog_r(LOG_ERR, context, msg, ##args);	\
+} while (0)
+
+#define crit_r(context, msg, args...)			\
+do {							\
+	syslog_r(LOG_CRIT, context, msg, ##args);	\
+} while (0)

  #endif

diff -Nur autofs-4.1.4.orig/include/syslog.h autofs-4.1.4/include/syslog.h
--- autofs-4.1.4.orig/include/syslog.h	1970-01-01 08:00:00.000000000 +0800
+++ autofs-4.1.4/include/syslog.h	2005-04-24 01:00:58.000000000 +0800
@@ -0,0 +1,201 @@
+/*	$OpenBSD: syslog.h,v 1.11 2003/08/24 01:27:07 avsm Exp $	*/
+/*	$NetBSD: syslog.h,v 1.14 1996/04/03 20:46:44 christos Exp $	*/
+
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ *
+ *	@(#)syslog.h	8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _SYS_SYSLOG_H_
+#define _SYS_SYSLOG_H_
+
+#include <features.h>
+#define __need___va_list
+#include <stdarg.h>
+
+#define	_PATH_LOG	"/dev/log"
+
+/*
+ * priorities/facilities are encoded into a single 32-bit quantity, where the
+ * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
+ * (0-big number).  Both the priorities and the facilities map roughly
+ * one-to-one to strings in the syslogd(8) source code.  This mapping is
+ * included in this file.
+ *
+ * priorities (these are ordered)
+ */
+#define	LOG_EMERG	0	/* system is unusable */
+#define	LOG_ALERT	1	/* action must be taken immediately */
+#define	LOG_CRIT	2	/* critical conditions */
+#define	LOG_ERR		3	/* error conditions */
+#define	LOG_WARNING	4	/* warning conditions */
+#define	LOG_NOTICE	5	/* normal but significant condition */
+#define	LOG_INFO	6	/* informational */
+#define	LOG_DEBUG	7	/* debug-level messages */
+
+#define	LOG_PRIMASK	0x07	/* mask to extract priority part (internal) */
+				/* extract priority */
+#define	LOG_PRI(p)	((p) & LOG_PRIMASK)
+#define	LOG_MAKEPRI(fac, pri)	(((fac) << 3) | (pri))
+
+#ifdef SYSLOG_NAMES
+#define	INTERNAL_NOPRI	0x10	/* the "no priority" priority */
+				/* mark "facility" */
+#define	INTERNAL_MARK	LOG_MAKEPRI(LOG_NFACILITIES, 0)
+typedef struct _code {
+	char	*c_name;
+	int	c_val;
+} CODE;
+
+CODE prioritynames[] = {
+	{ "alert",	LOG_ALERT },
+	{ "crit",	LOG_CRIT },
+	{ "debug",	LOG_DEBUG },
+	{ "emerg",	LOG_EMERG },
+	{ "err",	LOG_ERR },
+	{ "error",	LOG_ERR },		/* DEPRECATED */
+	{ "info",	LOG_INFO },
+	{ "none",	INTERNAL_NOPRI },	/* INTERNAL */
+	{ "notice",	LOG_NOTICE },
+	{ "panic", 	LOG_EMERG },		/* DEPRECATED */
+	{ "warn",	LOG_WARNING },		/* DEPRECATED */
+	{ "warning",	LOG_WARNING },
+	{ NULL,		-1 },
+};
+#endif
+
+/* facility codes */
+#define	LOG_KERN	(0<<3)	/* kernel messages */
+#define	LOG_USER	(1<<3)	/* random user-level messages */
+#define	LOG_MAIL	(2<<3)	/* mail system */
+#define	LOG_DAEMON	(3<<3)	/* system daemons */
+#define	LOG_AUTH	(4<<3)	/* security/authorization messages */
+#define	LOG_SYSLOG	(5<<3)	/* messages generated internally by syslogd */
+#define	LOG_LPR		(6<<3)	/* line printer subsystem */
+#define	LOG_NEWS	(7<<3)	/* network news subsystem */
+#define	LOG_UUCP	(8<<3)	/* UUCP subsystem */
+#define	LOG_CRON	(9<<3)	/* clock daemon */
+#define	LOG_AUTHPRIV	(10<<3)	/* security/authorization messages (private) */
+#define	LOG_FTP		(11<<3)	/* ftp daemon */
+
+	/* other codes through 15 reserved for system use */
+#define	LOG_LOCAL0	(16<<3)	/* reserved for local use */
+#define	LOG_LOCAL1	(17<<3)	/* reserved for local use */
+#define	LOG_LOCAL2	(18<<3)	/* reserved for local use */
+#define	LOG_LOCAL3	(19<<3)	/* reserved for local use */
+#define	LOG_LOCAL4	(20<<3)	/* reserved for local use */
+#define	LOG_LOCAL5	(21<<3)	/* reserved for local use */
+#define	LOG_LOCAL6	(22<<3)	/* reserved for local use */
+#define	LOG_LOCAL7	(23<<3)	/* reserved for local use */
+
+#define	LOG_NFACILITIES	24	/* current number of facilities */
+#define	LOG_FACMASK	0x03f8	/* mask to extract facility part */
+				/* facility of pri */
+#define	LOG_FAC(p)	(((p) & LOG_FACMASK) >> 3)
+
+#ifdef SYSLOG_NAMES
+CODE facilitynames[] = {
+	{ "auth",	LOG_AUTH },
+	{ "authpriv",	LOG_AUTHPRIV },
+	{ "cron", 	LOG_CRON },
+	{ "daemon",	LOG_DAEMON },
+	{ "ftp",	LOG_FTP },
+	{ "kern",	LOG_KERN },
+	{ "lpr",	LOG_LPR },
+	{ "mail",	LOG_MAIL },
+	{ "mark", 	INTERNAL_MARK },	/* INTERNAL */
+	{ "news",	LOG_NEWS },
+	{ "security",	LOG_AUTH },		/* DEPRECATED */
+	{ "syslog",	LOG_SYSLOG },
+	{ "user",	LOG_USER },
+	{ "uucp",	LOG_UUCP },
+	{ "local0",	LOG_LOCAL0 },
+	{ "local1",	LOG_LOCAL1 },
+	{ "local2",	LOG_LOCAL2 },
+	{ "local3",	LOG_LOCAL3 },
+	{ "local4",	LOG_LOCAL4 },
+	{ "local5",	LOG_LOCAL5 },
+	{ "local6",	LOG_LOCAL6 },
+	{ "local7",	LOG_LOCAL7 },
+	{ NULL,		-1 },
+};
+#endif
+
+/* Used by reentrant functions */
+
+struct syslog_data {
+	int	log_file;
+	int	connected;
+	int	opened;
+	int	log_stat;
+	const char 	*log_tag;
+	int 	log_fac;
+	int 	log_mask;
+};
+
+#define SYSLOG_DATA_INIT {-1, 0, 0, 0, (const char *)0, LOG_USER, 0xff}
+
+/*
+ * arguments to setlogmask.
+ */
+#define	LOG_MASK(pri)	(1 << (pri))		/* mask for one priority */
+#define	LOG_UPTO(pri)	((1 << ((pri)+1)) - 1)	/* all priorities through pri */
+
+/*
+ * Option flags for openlog.
+ *
+ * LOG_ODELAY no longer does anything.
+ * LOG_NDELAY is the inverse of what it used to be.
+ */
+#define	LOG_PID		0x01	/* log the pid with each message */
+#define	LOG_CONS	0x02	/* log on the console if errors in sending */
+#define	LOG_ODELAY	0x04	/* delay open until first syslog() (default) */
+#define	LOG_NDELAY	0x08	/* don't delay open */
+#define	LOG_NOWAIT	0x10	/* don't wait for console forks: DEPRECATED */
+#define	LOG_PERROR	0x20	/* log to stderr as well */
+
+__BEGIN_DECLS
+void	closelog(void);
+void	openlog(const char *__ident, int __option, int __facility);
+int	setlogmask(int __mask);
+void	syslog(int __pri, const char *__fmt, ...)
+		__attribute__((__format__(__printf__, 2, 3)));
+void	vsyslog(int __pri, const char *, __gnuc_va_list __ap)
+		__attribute__((__format__(__printf__, 2, 0)));
+void	closelog_r(struct syslog_data *__data);
+void	openlog_r(const char *__ident, int __option, int __facility, struct syslog_data *__data);
+int	setlogmask_r(int __mask, struct syslog_data *__data);
+void	syslog_r(int __pri, struct syslog_data *__data, const char *__fmt, ...)
+		__attribute__((__format__(__printf__, 3, 4)));
+void	vsyslog_r(int __pri, struct syslog_data *__data, const char *__fmt, __gnuc_va_list __ap)
+		__attribute__((__format__(__printf__, 3, 0)));
+__END_DECLS
+
+#endif /* !_SYS_SYSLOG_H_ */
+
diff -Nur autofs-4.1.4.orig/lib/Makefile autofs-4.1.4/lib/Makefile
--- autofs-4.1.4.orig/lib/Makefile	2005-01-09 17:16:43.000000000 +0800
+++ autofs-4.1.4/lib/Makefile	2005-04-23 18:48:53.000000000 +0800
@@ -9,10 +9,12 @@
  RPCGEN = /usr/bin/rpcgen
  RANLIB = /usr/bin/ranlib

-SRCS = cache.c listmount.c cat_path.c rpc_subs.c mounts.c lock.c
+SRCS = cache.c listmount.c cat_path.c rpc_subs.c mounts.c lock.c \
+	syslog.c vsprintf.c
  RPCS = mount.h mount_clnt.c mount_xdr.c
  OBJS = cache.o mount_clnt.o mount_xdr.o listmount.o \
-	cat_path.o rpc_subs.o mounts.o lock.o
+	cat_path.o rpc_subs.o mounts.o lock.o \
+	syslog.o vsprintf.o

  LIB = autofs.a

diff -Nur autofs-4.1.4.orig/lib/cache.c autofs-4.1.4/lib/cache.c
--- autofs-4.1.4.orig/lib/cache.c	2005-02-06 14:00:53.000000000 +0800
+++ autofs-4.1.4/lib/cache.c	2005-04-23 19:46:48.000000000 +0800
@@ -21,7 +21,6 @@
  #include <string.h>
  #include <unistd.h>
  #include <ctype.h>
-#include <syslog.h>
  #include <stdio.h>
  #include <fcntl.h>
  #include <sys/param.h>
diff -Nur autofs-4.1.4.orig/lib/cat_path.c autofs-4.1.4/lib/cat_path.c
--- autofs-4.1.4.orig/lib/cat_path.c	2005-04-06 23:14:23.000000000 +0800
+++ autofs-4.1.4/lib/cat_path.c	2005-04-23 19:47:59.000000000 +0800
@@ -16,7 +16,6 @@
  #include <alloca.h>
  #include <string.h>
  #include <limits.h>
-#include <syslog.h>
  #include <ctype.h>

  /*
diff -Nur autofs-4.1.4.orig/lib/mounts.c autofs-4.1.4/lib/mounts.c
--- autofs-4.1.4.orig/lib/mounts.c	2005-01-17 23:09:28.000000000 +0800
+++ autofs-4.1.4/lib/mounts.c	2005-04-23 19:48:31.000000000 +0800
@@ -16,7 +16,6 @@
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
-#include <syslog.h>
  #include <mntent.h>
  #include <limits.h>
  #include <sys/types.h>
diff -Nur autofs-4.1.4.orig/lib/syslog.c autofs-4.1.4/lib/syslog.c
--- autofs-4.1.4.orig/lib/syslog.c	1970-01-01 08:00:00.000000000 +0800
+++ autofs-4.1.4/lib/syslog.c	2005-04-24 14:29:37.000000000 +0800
@@ -0,0 +1,383 @@
+#ident "$Id$"
+/*
+ * Copyright (c) 1983, 1988, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <netdb.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#include "syslog.h"
+
+#define	TBUF_LEN	2048
+#define	FMT_LEN		1024
+#define	INTERNALLOG	LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
+
+#define	DEC()					\
+	do {					\
+		if (prlen < 0)			\
+			prlen = 0;		\
+		if (prlen >= tbuf_left)		\
+			prlen = tbuf_left - 1;	\
+		p += prlen;			\
+		tbuf_left -= prlen;		\
+	} while (0)
+
+/* Use our internal printf routines */
+extern int snprintf_int(char * buf, size_t size, const char * fmt, ...)
+	__attribute__ ((format (printf, 3, 4)));
+extern int vsnprintf_int(char *buf, size_t size, const char *fmt, va_list args)
+	__attribute__ ((format (printf, 3, 0)));
+
+static struct syslog_data sdata = SYSLOG_DATA_INIT;
+static int LogType = SOCK_DGRAM;	/* type of socket connection */
+
+extern char	*__progname;		/* Program name, from crt0. */
+
+static void	disconnectlog_r(struct syslog_data *);	/* disconnect from syslogd */
+static void	connectlog_r(struct syslog_data *);	/* (re)connect to syslogd */
+
+/*
+ * syslog, vsyslog --
+ *	print message on log file; output is intended for syslogd(8).
+ */
+void
+syslog(int pri, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vsyslog(pri, fmt, ap);
+	va_end(ap);
+}
+
+void
+vsyslog(int pri, const char *fmt, va_list ap)
+{
+	vsyslog_r(pri, &sdata, fmt, ap);
+}
+
+void
+openlog(const char *ident, int logstat, int logfac)
+{
+	openlog_r(ident, logstat, logfac, &sdata);
+}
+
+void
+closelog(void)
+{
+	closelog_r(&sdata);
+}
+
+/* setlogmask -- set the log mask level */
+int
+setlogmask(int pmask)
+{
+	return setlogmask_r(pmask, &sdata);
+}
+
+/* Reentrant version of syslog, i.e. syslog_r() */
+
+void
+syslog_r(int pri, struct syslog_data *data, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vsyslog_r(pri, data, fmt, ap);
+	va_end(ap);
+}
+
+void
+vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap)
+{
+	int cnt;
+	char ch, *p, *t;
+	time_t now;
+	int fd, saved_errno, error;
+	char *stdp = NULL, tbuf[TBUF_LEN], fmt_cpy[FMT_LEN];
+	int tbuf_left, fmt_left, prlen;
+
+	/* Check for invalid bits. */
+	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
+		if (data == &sdata) {
+			syslog(INTERNALLOG,
+			    "syslog: unknown facility/priority: %x", pri);
+		} else {
+			syslog_r(INTERNALLOG, data,
+			    "syslog_r: unknown facility/priority: %x", pri);
+		}
+		pri &= LOG_PRIMASK|LOG_FACMASK;
+	}
+
+	/* Check priority against setlogmask values. */
+	if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
+		return;
+
+	saved_errno = errno;
+
+	/* Set default facility if none specified. */
+	if ((pri & LOG_FACMASK) == 0)
+		pri |= data->log_fac;
+
+	/* If we have been called through syslog(), no need for reentrancy. */
+	if (data == &sdata)
+		(void)time(&now);
+
+	p = tbuf;
+	tbuf_left = TBUF_LEN;
+
+	prlen = snprintf_int(p, tbuf_left, "<%d>", pri);
+	DEC();
+
+	/* 
+	 * syslogd will expand time automagically for reentrant case, and
+	 * for normal case, just do like before
+	 */
+	if (data == &sdata) {
+		prlen = strftime(p, tbuf_left, "%h %e %T ", localtime(&now));
+		DEC();
+	}
+
+	if (data->log_stat & LOG_PERROR)
+		stdp = p;
+	if (data->log_tag == NULL)
+		data->log_tag = __progname;
+	if (data->log_tag != NULL) {
+		prlen = snprintf_int(p, tbuf_left, "%s", data->log_tag);
+		DEC();
+	}
+	if (data->log_stat & LOG_PID) {
+		prlen = snprintf_int(p, tbuf_left, "[%ld]", (long)getpid());
+		DEC();
+	}
+	if (data->log_tag != NULL) {
+		if (tbuf_left > 1) {
+			*p++ = ':';
+			tbuf_left--;
+		}
+		if (tbuf_left > 1) {
+			*p++ = ' ';
+			tbuf_left--;
+		}
+	}
+
+	/* strerror() is not reentrant */
+
+	for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) {
+		if (ch == '%' && fmt[1] == 'm') {
+			++fmt;
+			if (data == &sdata) {
+				prlen = snprintf_int(t, fmt_left, "%s",
+				    strerror(saved_errno)); 
+			} else {
+				prlen = snprintf_int(t, fmt_left, "Error %d",
+				    saved_errno); 
+			}
+			if (prlen < 0)
+				prlen = 0;
+			if (prlen >= fmt_left)
+				prlen = fmt_left - 1;
+			t += prlen;
+			fmt_left -= prlen;
+		} else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
+			*t++ = '%';
+			*t++ = '%';
+			fmt++;
+			fmt_left -= 2;
+		} else {
+			if (fmt_left > 1) {
+				*t++ = ch;
+				fmt_left--;
+			}
+		}
+	}
+	*t = '\0';
+
+	prlen = vsnprintf_int(p, tbuf_left, fmt_cpy, ap);
+	DEC();
+	cnt = p - tbuf;
+
+	/* Output to stderr if requested. */
+	if (data->log_stat & LOG_PERROR) {
+		struct iovec iov[2];
+
+		iov[0].iov_base = stdp;
+		iov[0].iov_len = cnt - (stdp - tbuf);
+		iov[1].iov_base = "\n";
+		iov[1].iov_len = 1;
+		(void)writev(STDERR_FILENO, iov, 2);
+	}
+
+	/* Get connected, output the message to the local logger. */
+	if (!data->opened)
+		openlog_r(data->log_tag, data->log_stat, 0, data);
+	connectlog_r(data);
+
+	/* If we have a SOCK_STREAM connection, also send ASCII NUL as
+	   a record terminator.  */
+	if (LogType == SOCK_STREAM)
+		cnt++;
+
+	/*
+	 * If the send() failed, there are two likely scenarios:
+	 *  1) syslogd was restarted
+	 *  2) /dev/log is out of socket buffer space
+	 * We attempt to reconnect to /dev/log to take care of
+	 * case #1 and keep send()ing data to cover case #2
+	 * to give syslogd a chance to empty its socket buffer.
+	 */
+	if ((error = send(data->log_file, tbuf, cnt, 0)) < 0) {
+		if (errno != ENOBUFS) {
+			disconnectlog_r(data);
+			connectlog_r(data);
+		}
+		do {
+			usleep(1);
+			if ((error = send(data->log_file, tbuf, cnt, 0)) >= 0)
+				break;
+		} while (errno == ENOBUFS);
+	}
+
+	/*
+	 * Output the message to the console; try not to block
+	 * as a blocking console should not stop other processes.
+	 * Make sure the error reported is the one from the syslogd failure.
+	 */
+	if (error == -1 && (data->log_stat & LOG_CONS) &&
+	    (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
+		struct iovec iov[2];
+ 
+		p = strchr(tbuf, '>') + 1;
+		iov[0].iov_base = p;
+		iov[0].iov_len = cnt - (p - tbuf);
+		iov[1].iov_base = "\r\n";
+		iov[1].iov_len = 2;
+		(void)writev(fd, iov, 2);
+		(void)close(fd);
+	}
+
+	if (data != &sdata)
+		closelog_r(data);
+}
+
+static void
+disconnectlog_r(struct syslog_data *data)
+{
+	/*
+	 * If the user closed the FD and opened another in the same slot,
+	 * that's their problem.  They should close it before calling on
+	 * system services.
+	 */
+	if (data->log_file != -1) {
+		close(data->log_file);
+		data->log_file = -1;
+	}
+	data->connected = 0;		/* retry connect */
+}
+
+static void
+connectlog_r(struct syslog_data *data)
+{
+	struct sockaddr_un SyslogAddr;	/* AF_UNIX address of local logger */
+
+again:
+	if (data->log_file == -1) {
+		if ((data->log_file = socket(AF_UNIX, LogType, 0)) == -1)
+			return;
+		(void)fcntl(data->log_file, F_SETFD, 1);
+	}
+	if (data->log_file != -1 && !data->connected) {
+		int old_errno;
+ 
+		memset(&SyslogAddr, '\0', sizeof(SyslogAddr));
+		SyslogAddr.sun_family = AF_UNIX;
+		strncpy(SyslogAddr.sun_path, _PATH_LOG,
+		    sizeof(SyslogAddr.sun_path));
+		old_errno = errno;
+		if (connect(data->log_file, (struct sockaddr *)&SyslogAddr,
+		    sizeof(SyslogAddr)) == -1) {
+			int save_errno = errno;
+			(void)close(data->log_file);
+			data->log_file = -1;
+			if (LogType == SOCK_DGRAM && save_errno == EPROTOTYPE) {
+				/* retry with SOCK_STREAM */
+				LogType = SOCK_STREAM;
+				errno = old_errno;
+				goto again;
+			}
+		} else
+			data->connected = 1;
+	}
+}
+
+void
+openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data)
+{
+	if (ident != NULL)
+		data->log_tag = ident;
+	data->log_stat = logstat;
+	if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
+		data->log_fac = logfac;
+
+	if (data->log_stat & LOG_NDELAY)	/* open immediately */
+		connectlog_r(data);
+
+	data->opened = 1;	/* ident and facility has been set */
+}
+
+void
+closelog_r(struct syslog_data *data)
+{
+	(void)close(data->log_file);
+	data->log_file = -1;
+	data->connected = 0;
+	data->log_tag = NULL;
+}
+
+/* setlogmask -- set the log mask level */
+int
+setlogmask_r(int pmask, struct syslog_data *data)
+{
+	int omask;
+
+	omask = data->log_mask;
+	if (pmask != 0)
+		data->log_mask = pmask;
+	return (omask);
+}
diff -Nur autofs-4.1.4.orig/lib/vsprintf.c autofs-4.1.4/lib/vsprintf.c
--- autofs-4.1.4.orig/lib/vsprintf.c	1970-01-01 08:00:00.000000000 +0800
+++ autofs-4.1.4/lib/vsprintf.c	2005-04-24 11:23:36.000000000 +0800
@@ -0,0 +1,619 @@
+#ident "$Id$"
+/*
+ *  Stolen from the linux kernel.
+ *
+ *  License: GPL
+ */
+/*------------------ Original Copyright -----------------*/
+/*
+ *  linux/lib/vsprintf.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
+/*
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
+ */
+
+/* 
+ * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
+ * - changed to provide snprintf and vsnprintf functions
+ * So Feb  1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de>
+ * - scnprintf and vscnprintf
+ */
+
+/* Also copied from: */
+
+/*
+ *  linux/lib/string.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * -  Added strsep() which will replace strtok() soon (because strsep() is
+ *    reentrant and should be faster). Use only strsep() in new code, please.
+ *
+ * * Sat Feb 09 2002, Jason Thomas <jason@topic.com.au>,
+ *                    Matthew Hawkins <matt@mh.dropbear.id.au>
+ * -  Kissed strtok() goodbye
+ */
+/*-------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#define BITS_PER_LONG	__WORDSIZE
+#define PAGE_SIZE	getpagesize()
+
+
+#if BITS_PER_LONG == 64
+
+# define do_div(n,base) ({					\
+	uint32_t __base = (base);				\
+	uint32_t __rem;						\
+	__rem = ((uint64_t)(n)) % __base;			\
+	(n) = ((uint64_t)(n)) / __base;				\
+	__rem;							\
+ })
+
+#elif BITS_PER_LONG == 32
+
+/* Not needed on 64bit architectures */
+uint32_t __div64_32(uint64_t *n, uint32_t base)
+{
+	uint64_t rem = *n;
+	uint64_t b = base;
+	uint64_t res, d = 1;
+	uint32_t high = rem >> 32;
+
+	/* Reduce the thing a bit first */
+	res = 0;
+	if (high >= base) {
+		high /= base;
+		res = (uint64_t) high << 32;
+		rem -= (uint64_t) (high*base) << 32;
+	}
+
+	while ((int64_t)b > 0 && b < rem) {
+		b = b+b;
+		d = d+d;
+	}
+
+	do {
+		if (rem >= b) {
+			rem -= b;
+			res += d;
+		}
+		b >>= 1;
+		d >>= 1;
+	} while (d);
+
+	*n = res;
+	return rem;
+}
+
+/* The unnecessary pointer compare is there
+ * to check for type safety (n must be 64bit)
+ */
+# define do_div(n,base) ({				\
+	uint32_t __base = (base);			\
+	uint32_t __rem;					\
+	(void)(((typeof((n)) *)0) == ((uint64_t *)0));	\
+	if (((n) >> 32) == 0) {				\
+		__rem = (uint32_t)(n) % __base;		\
+		(n) = (uint32_t)(n) / __base;		\
+	} else 						\
+		__rem = __div64_32(&(n), __base);	\
+	__rem;						\
+ })
+
+# else
+
+# error do_div() does not yet support the C64
+
+#endif /* BITS_PER_LONG */
+
+
+/**
+ * strnlen - Find the length of a length-limited string
+ * @s: The string to be sized
+ * @count: The maximum number of bytes to search
+ */
+size_t strnlen(const char * s, size_t count)
+{
+	const char *sc;
+
+	for (sc = s; count-- && *sc != '\0'; ++sc)
+		/* nothing */;
+	return sc - s;
+}
+
+/**
+ * simple_strtoul - convert a string to an unsigned long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
+{
+	unsigned long result = 0,value;
+
+	if (!base) {
+		base = 10;
+		if (*cp == '0') {
+			base = 8;
+			cp++;
+			if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
+				cp++;
+				base = 16;
+			}
+		}
+	} else if (base == 16) {
+		if (cp[0] == '0' && toupper(cp[1]) == 'X')
+			cp += 2;
+	}
+	while (isxdigit(*cp) &&
+	       (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
+		result = result*base + value;
+		cp++;
+	}
+	if (endp)
+		*endp = (char *)cp;
+	return result;
+}
+
+/**
+ * simple_strtol - convert a string to a signed long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long simple_strtol(const char *cp,char **endp,unsigned int base)
+{
+	if(*cp=='-')
+		return -simple_strtoul(cp+1,endp,base);
+	return simple_strtoul(cp,endp,base);
+}
+
+/**
+ * simple_strtoull - convert a string to an unsigned long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base)
+{
+	unsigned long long result = 0,value;
+
+	if (!base) {
+		base = 10;
+		if (*cp == '0') {
+			base = 8;
+			cp++;
+			if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
+				cp++;
+				base = 16;
+			}
+		}
+	} else if (base == 16) {
+		if (cp[0] == '0' && toupper(cp[1]) == 'X')
+			cp += 2;
+	}
+	while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
+	    ? toupper(*cp) : *cp)-'A'+10) < base) {
+		result = result*base + value;
+		cp++;
+	}
+	if (endp)
+		*endp = (char *)cp;
+	return result;
+}
+
+/**
+ * simple_strtoll - convert a string to a signed long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long long simple_strtoll(const char *cp,char **endp,unsigned int base)
+{
+	if(*cp=='-')
+		return -simple_strtoull(cp+1,endp,base);
+	return simple_strtoull(cp,endp,base);
+}
+
+static int skip_atoi(const char **s)
+{
+	int i=0;
+
+	while (isdigit(**s))
+		i = i*10 + *((*s)++) - '0';
+	return i;
+}
+
+#define ZEROPAD	1		/* pad with zero */
+#define SIGN	2		/* unsigned/signed long */
+#define PLUS	4		/* show plus */
+#define SPACE	8		/* space if plus */
+#define LEFT	16		/* left justified */
+#define SPECIAL	32		/* 0x */
+#define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */
+
+static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type)
+{
+	char c,sign,tmp[66];
+	const char *digits;
+	static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+	static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	int i;
+
+	digits = (type & LARGE) ? large_digits : small_digits;
+	if (type & LEFT)
+		type &= ~ZEROPAD;
+	if (base < 2 || base > 36)
+		return NULL;
+	c = (type & ZEROPAD) ? '0' : ' ';
+	sign = 0;
+	if (type & SIGN) {
+		if ((signed long long) num < 0) {
+			sign = '-';
+			num = - (signed long long) num;
+			size--;
+		} else if (type & PLUS) {
+			sign = '+';
+			size--;
+		} else if (type & SPACE) {
+			sign = ' ';
+			size--;
+		}
+	}
+	if (type & SPECIAL) {
+		if (base == 16)
+			size -= 2;
+		else if (base == 8)
+			size--;
+	}
+	i = 0;
+	if (num == 0)
+		tmp[i++]='0';
+	else while (num != 0)
+		tmp[i++] = digits[do_div(num,base)];
+	if (i > precision)
+		precision = i;
+	size -= precision;
+	if (!(type&(ZEROPAD+LEFT))) {
+		while(size-->0) {
+			if (buf <= end)
+				*buf = ' ';
+			++buf;
+		}
+	}
+	if (sign) {
+		if (buf <= end)
+			*buf = sign;
+		++buf;
+	}
+	if (type & SPECIAL) {
+		if (base==8) {
+			if (buf <= end)
+				*buf = '0';
+			++buf;
+		} else if (base==16) {
+			if (buf <= end)
+				*buf = '0';
+			++buf;
+			if (buf <= end)
+				*buf = digits[33];
+			++buf;
+		}
+	}
+	if (!(type & LEFT)) {
+		while (size-- > 0) {
+			if (buf <= end)
+				*buf = c;
+			++buf;
+		}
+	}
+	while (i < precision--) {
+		if (buf <= end)
+			*buf = '0';
+		++buf;
+	}
+	while (i-- > 0) {
+		if (buf <= end)
+			*buf = tmp[i];
+		++buf;
+	}
+	while (size-- > 0) {
+		if (buf <= end)
+			*buf = ' ';
+		++buf;
+	}
+	return buf;
+}
+
+/**
+ * vsnprintf_int - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * The return value is the number of characters which would
+ * be generated for the given input, excluding the trailing
+ * '\0', as per ISO C99. If you want to have the exact
+ * number of characters written into @buf as return value
+ * (not including the trailing '\0'), use vscnprintf. If the
+ * return is greater than or equal to @size, the resulting
+ * string is truncated.
+ *
+ * Call this function if you are already dealing with a va_list.
+ * You probably want snprintf instead.
+ */
+int vsnprintf_int(char *buf, size_t size, const char *fmt, va_list args)
+{
+	int len;
+	unsigned long long num;
+	int i, base;
+	char *str, *end, c;
+	const char *s;
+
+	int flags;		/* flags to number() */
+
+	int field_width;	/* width of output field */
+	int precision;		/* min. # of digits for integers; max
+				   number of chars for from string */
+	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
+				/* 'z' support added 23/7/1999 S.H.    */
+				/* 'z' changed to 'Z' --davidm 1/25/99 */
+
+	/* Reject out-of-range values early */
+	if ((int) size < 0)
+		return 0;
+
+	str = buf;
+	end = buf + size - 1;
+
+	if (end < buf - 1) {
+		end = ((void *) -1);
+		size = end - buf + 1;
+	}
+
+	for (; *fmt ; ++fmt) {
+		if (*fmt != '%') {
+			if (str <= end)
+				*str = *fmt;
+			++str;
+			continue;
+		}
+
+		/* process flags */
+		flags = 0;
+		repeat:
+			++fmt;		/* this also skips first '%' */
+			switch (*fmt) {
+				case '-': flags |= LEFT; goto repeat;
+				case '+': flags |= PLUS; goto repeat;
+				case ' ': flags |= SPACE; goto repeat;
+				case '#': flags |= SPECIAL; goto repeat;
+				case '0': flags |= ZEROPAD; goto repeat;
+			}
+
+		/* get field width */
+		field_width = -1;
+		if (isdigit(*fmt))
+			field_width = skip_atoi(&fmt);
+		else if (*fmt == '*') {
+			++fmt;
+			/* it's the next argument */
+			field_width = va_arg(args, int);
+			if (field_width < 0) {
+				field_width = -field_width;
+				flags |= LEFT;
+			}
+		}
+
+		/* get the precision */
+		precision = -1;
+		if (*fmt == '.') {
+			++fmt; 
+			if (isdigit(*fmt))
+				precision = skip_atoi(&fmt);
+			else if (*fmt == '*') {
+				++fmt;
+				/* it's the next argument */
+				precision = va_arg(args, int);
+			}
+			if (precision < 0)
+				precision = 0;
+		}
+
+		/* get the conversion qualifier */
+		qualifier = -1;
+		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
+		    *fmt =='Z' || *fmt == 'z') {
+			qualifier = *fmt;
+			++fmt;
+			if (qualifier == 'l' && *fmt == 'l') {
+				qualifier = 'L';
+				++fmt;
+			}
+		}
+
+		/* default base */
+		base = 10;
+
+		switch (*fmt) {
+			case 'c':
+				if (!(flags & LEFT)) {
+					while (--field_width > 0) {
+						if (str <= end)
+							*str = ' ';
+						++str;
+					}
+				}
+				c = (unsigned char) va_arg(args, int);
+				if (str <= end)
+					*str = c;
+				++str;
+				while (--field_width > 0) {
+					if (str <= end)
+						*str = ' ';
+					++str;
+				}
+				continue;
+
+			case 's':
+				s = va_arg(args, char *);
+				if ((unsigned long)s < PAGE_SIZE)
+					s = "<NULL>";
+
+				len = strnlen(s, precision);
+
+				if (!(flags & LEFT)) {
+					while (len < field_width--) {
+						if (str <= end)
+							*str = ' ';
+						++str;
+					}
+				}
+				for (i = 0; i < len; ++i) {
+					if (str <= end)
+						*str = *s;
+					++str; ++s;
+				}
+				while (len < field_width--) {
+					if (str <= end)
+						*str = ' ';
+					++str;
+				}
+				continue;
+
+			case 'p':
+				if (field_width == -1) {
+					field_width = 2*sizeof(void *);
+					flags |= ZEROPAD;
+				}
+				str = number(str, end,
+						(unsigned long) va_arg(args, void *),
+						16, field_width, precision, flags);
+				continue;
+
+
+			case 'n':
+				/* FIXME:
+				* What does C99 say about the overflow case here? */
+				if (qualifier == 'l') {
+					long * ip = va_arg(args, long *);
+					*ip = (str - buf);
+				} else if (qualifier == 'Z' || qualifier == 'z') {
+					size_t * ip = va_arg(args, size_t *);
+					*ip = (str - buf);
+				} else {
+					int * ip = va_arg(args, int *);
+					*ip = (str - buf);
+				}
+				continue;
+
+			case '%':
+				if (str <= end)
+					*str = '%';
+				++str;
+				continue;
+
+				/* integer number formats - set up the flags and "break" */
+			case 'o':
+				base = 8;
+				break;
+
+			case 'X':
+				flags |= LARGE;
+			case 'x':
+				base = 16;
+				break;
+
+			case 'd':
+			case 'i':
+				flags |= SIGN;
+			case 'u':
+				break;
+
+			default:
+				if (str <= end)
+					*str = '%';
+				++str;
+				if (*fmt) {
+					if (str <= end)
+						*str = *fmt;
+					++str;
+				} else {
+					--fmt;
+				}
+				continue;
+		}
+		if (qualifier == 'L')
+			num = va_arg(args, long long);
+		else if (qualifier == 'l') {
+			num = va_arg(args, unsigned long);
+			if (flags & SIGN)
+				num = (signed long) num;
+		} else if (qualifier == 'Z' || qualifier == 'z') {
+			num = va_arg(args, size_t);
+		} else if (qualifier == 'h') {
+			num = (unsigned short) va_arg(args, int);
+			if (flags & SIGN)
+				num = (signed short) num;
+		} else {
+			num = va_arg(args, unsigned int);
+			if (flags & SIGN)
+				num = (signed int) num;
+		}
+		str = number(str, end, num, base,
+				field_width, precision, flags);
+	}
+	if (str <= end)
+		*str = '\0';
+	else if (size > 0)
+		/* don't write out a null byte if the buf size is zero */
+		*end = '\0';
+	/* the trailing null byte doesn't count towards the total
+	* ++str;
+	*/
+	return str-buf;
+}
+
+/**
+ * snprintf_int - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @...: Arguments for the format string
+ *
+ * The return value is the number of characters which would be
+ * generated for the given input, excluding the trailing null,
+ * as per ISO C99.  If the return is greater than or equal to
+ * @size, the resulting string is truncated.
+ */
+int snprintf_int(char * buf, size_t size, const char *fmt, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, fmt);
+	i=vsnprintf_int(buf,size,fmt,args);
+	va_end(args);
+	return i;
+}
+

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Re: [patch, rfc] fix hung automounter issues
  2005-04-24  6:56       ` raven
@ 2005-04-24  9:37         ` raven
  0 siblings, 0 replies; 16+ messages in thread
From: raven @ 2005-04-24  9:37 UTC (permalink / raw)
  To: Jeff Moyer; +Cc: autofs mailing list, Dan.Berrange

On Sun, 24 Apr 2005 raven@themaw.net wrote:

> On Mon, 18 Apr 2005, Jeff Moyer wrote:
>
>> 
>>  Okay, I've reviewed all parts except for the actual logging functions. 
>>  I'm
>>  actually not convinced this is the right approach.  The overhead of 
>>  setting
>>  up and tearing down the signal mask is quite expensive.  I fear that
>>  environments with a great number of mounts will fall over when debugging 
>>  is
>>  turned on, and that is the very thing we are trying to save.  Perhaps it 
>>  is
>>  worth another look at a separate logging process.
>> 
>
> I agree.
>
> The buffering approach is not the best way to handle this.
>
> I had a look around and found that the BSD folks have a reentrant syslog. I 
> found several posts on lists discussing the use of non-reentrant functions, 
> including syslog, in signal handlers. The responses claimed that their 
> reentrant syslog would work.
>
> I grabed the source from OpenBSD and tweeked it a bit to get it working.
> I've used the kernel printf code from the existing patch and checked all the 
> other function calls in glibc. It looks like it may well be signal safe.
>
> I've also tried to rationalise the signal setup and take down a bit.
> I've also tried to address the things that you picked up previously.
>
> So have a look at the attached patch.
>
> Ian

Sorry, did it again.
The patch is contaminated with white space.
Try this one instead please.

diff -Nur autofs-4.1.4.orig/daemon/automount.c autofs-4.1.4/daemon/automount.c
--- autofs-4.1.4.orig/daemon/automount.c	2005-03-06 17:43:55.000000000 +0800
+++ autofs-4.1.4/daemon/automount.c	2005-04-24 13:43:54.000000000 +0800
@@ -5,7 +5,7 @@
   *
   *   Copyright 1997 Transmeta Corporation - All Rights Reserved
   *   Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
- *   Copyright 2001-2003 Ian Kent <raven@themaw.net>
+ *   Copyright 2001-2005 Ian Kent <raven@themaw.net>
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
@@ -28,7 +28,6 @@
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
-#include <syslog.h>
  #include <unistd.h>
  #include <sys/ioctl.h>
  #include <sys/types.h>
@@ -38,13 +37,32 @@
  #include <sys/poll.h>
  #include <linux/auto_fs4.h>

+#include "automount.h"
+
  #ifndef NDEBUG
-#define assert(x)	do { if (!(x)) { syslog(LOG_CRIT, __FILE__ ":%d: assertion failed: " #x, __LINE__); } } while(0)
+#define assert(x) 						    \
+	do { 							    \
+		if (!(x)) {					    \
+			crit(__FILE__ ":%d: assertion failed: " #x, \
+				__LINE__);			    \
+		}						    \
+	} while(0)
  #else
  #define assert(x)	do { } while(0)
  #endif

-#include "automount.h"
+#ifndef NDEBUG
+#define assert_r(context, x) 					   \
+	do { 							   \
+		if (!(x)) {					   \
+			crit_r(context,				   \
+				__FILE__ ":%d: assertion failed: ",\
+				__LINE__);			   \
+		}						   \
+	} while(0)
+#else
+#define assert_r(context, x)	do { } while(0)
+#endif

  const char *program;		/* Initialized with argv[0] */
  const char *version = VERSION_STRING;	/* Program version */
@@ -57,7 +75,7 @@
  int kproto_sub_version = 0;	/* Kernel protocol version used */

  static int submount = 0;
-
+
  int do_verbose = 0;		/* Verbose feedback option */
  int do_debug = 0;		/* Enable full debug output */

@@ -66,6 +84,9 @@
  sigset_t sigchld_mask;

  struct autofs_point ap;
+ 
+/* re-entrant syslog default context data */
+#define AUTOFS_SYSLOG_CONTEXT {-1, 0, 0, LOG_PID, (const char *)0, LOG_DAEMON, 0xff};

  volatile struct pending_mount *junk_mounts = NULL;

@@ -480,19 +501,25 @@

  static void nextstate(enum states next)
  {
+	static struct syslog_data syslog_context = AUTOFS_SYSLOG_CONTEXT;
+	static struct syslog_data *slc = &syslog_context;
+
  	if (write(ap.state_pipe[1], &next, sizeof(next)) != sizeof(next))
-		error("nextstate: write failed %m");
+		error_r(slc, "nextstate: write failed %m");
  }

  /* Deal with all the signal-driven events in the state machine */
  static void sig_statemachine(int sig)
  {
+	static struct syslog_data syslog_context = AUTOFS_SYSLOG_CONTEXT;
+	static struct syslog_data *slc = &syslog_context;
  	int save_errno = errno;
  	enum states next = ap.state;

  	switch (sig) {
  	default:		/* all the "can't happen" signals */
-		error("process %d got unexpected signal %d!", getpid(), sig);
+		error_r(slc, "process %d got unexpected signal %d!",
+			getpid(), sig);
  		break;
  		/* don't FALLTHROUGH */

@@ -503,33 +530,36 @@
  		break;

  	case SIGUSR1:
-		assert(ap.state == ST_READY);
+		assert_r(slc, ap.state == ST_READY);
  		nextstate(next = ST_PRUNE);
  		break;

  	case SIGALRM:
-		assert(ap.state == ST_READY);
+		assert_r(slc, ap.state == ST_READY);
  		nextstate(next = ST_EXPIRE);
  		break;

  	case SIGHUP:
-		assert(ap.state == ST_READY);
+		assert_r(slc, ap.state == ST_READY);
  		nextstate(next = ST_READMAP);
  		break;
  	}

-	debug("sig %d switching from %d to %d", sig, ap.state, next);
+	debug_r(slc, "sig %d switching from %d to %d", sig, ap.state, next);

  	errno = save_errno;
  }

  static int send_ready(unsigned int wait_queue_token)
  {
+	static struct syslog_data syslog_context = AUTOFS_SYSLOG_CONTEXT;
+	static struct syslog_data *slc = &syslog_context;
+
  	if (wait_queue_token == 0)
  		return 0;
-	debug("send_ready: token=%d\n", wait_queue_token);
+	debug_r(slc, "send_ready: token=%d\n", wait_queue_token);
  	if (ioctl(ap.ioctlfd, AUTOFS_IOC_READY, wait_queue_token) < 0) {
-		error("AUTOFS_IOC_READY: %m");
+		error_r(slc, "AUTOFS_IOC_READY: %m");
  		return 1;
  	}
  	return 0;
@@ -537,11 +567,14 @@

  static int send_fail(unsigned int wait_queue_token)
  {
+	static struct syslog_data syslog_context = AUTOFS_SYSLOG_CONTEXT;
+	static struct syslog_data *slc = &syslog_context;
+
  	if (wait_queue_token == 0)
  		return 0;
-	debug("send_fail: token=%d\n", wait_queue_token);
+	debug_r(slc, "send_fail: token=%d\n", wait_queue_token);
  	if (ioctl(ap.ioctlfd, AUTOFS_IOC_FAIL, wait_queue_token) < 0) {
-		syslog(LOG_ERR, "AUTOFS_IOC_FAIL: %m");
+		error_r(slc, "AUTOFS_IOC_FAIL: %m");
  		return 1;
  	}
  	return 0;
@@ -552,6 +585,8 @@
     result.  */
  static enum states handle_child(int hang)
  {
+	static struct syslog_data syslog_context = AUTOFS_SYSLOG_CONTEXT;
+	static struct syslog_data *slc = &syslog_context;
  	pid_t pid;
  	int status;
  	enum states next = ST_INVAL;
@@ -559,13 +594,13 @@
  	while ((pid = waitpid(-1, &status, hang ? 0 : WNOHANG)) > 0) {
  		struct pending_mount volatile *mt, *volatile *mtp;

-		debug("handle_child: got pid %d, sig %d (%d), stat %d\n",
+		debug_r(slc, "handle_child: got pid %d, sig %d (%d), stat %d",
  			pid, WIFSIGNALED(status),
  			WTERMSIG(status), WEXITSTATUS(status));

  		/* Check to see if expire process finished */
  		if (pid == ap.exp_process) {
-			int success, ret;
+			int success;

  			if (!WIFEXITED(status))
  				continue;
@@ -594,29 +629,23 @@

  			case ST_SHUTDOWN_PENDING:
  				next = ST_SHUTDOWN;
-				if (success) {
-					ret = ioctl(ap.ioctlfd,
-						AUTOFS_IOC_ASKUMOUNT, &status);
-					if (!ret) {
-						if (status)
-							break;
-					} else
-						break;
-				}
+				if (success)
+					break;

  				/* Failed shutdown returns to ready */
-				warn("can't shutdown: filesystem %s still busy",
-				     ap.path);
+				warn_r(slc,
+			           "can't shutdown: filesystem %s still busy",
+				   ap.path);
  				alarm(ap.exp_runfreq);
  				next = ST_READY;
  				break;

  			default:
-				error("bad state %d", ap.state);
+				error_r(slc, "bad state %d", ap.state);
  			}

  			if (next != ST_INVAL)
-				debug("sigchld: exp "
+				debug_r(slc, "sigchld: exp "
  				     "%d finished, switching from %d to %d",
  				     pid, ap.state, next);

@@ -632,7 +661,7 @@
  			if (!WIFEXITED(status) && !WIFSIGNALED(status))
  				break;

-			debug("sig_child: found pending iop pid %d: "
+			debug_r(slc, "sig_child: found pending iop pid %d: "
  			     "signalled %d (sig %d), exit status %d",
  				pid, WIFSIGNALED(status),
  				WTERMSIG(status), WEXITSTATUS(status));
@@ -673,10 +702,9 @@

  static int st_ready(void)
  {
-	debug("st_ready(): state = %d\n", ap.state);
+	debug("st_ready(): state = %d", ap.state);

  	ap.state = ST_READY;
-	sigprocmask(SIG_UNBLOCK, &lock_sigs, NULL);

  	return 0;
  }
@@ -796,6 +824,7 @@
  	default:
  		debug("expire_proc: exp_proc=%d", f);
  		ap.exp_process = f;
+		sigprocmask(SIG_SETMASK, &old, NULL);
  		return EXP_STARTED;
  	}
  }
@@ -804,10 +833,15 @@
  {
  	int status;

+	assert(ap.state == ST_READY);
+	ap.state = ST_READMAP;
+
  	status = ap.lookup->lookup_ghost(ap.path, ap.ghost, 0, ap.lookup->context);

  	debug("st_readmap: status %d\n", status);

+	ap.state = ST_READY;
+
  	/* If I don't exist in the map any more then exit */
  	if (status == LKP_FAIL)
  		return 0;
@@ -822,15 +856,11 @@
  	info("prep_shutdown: state = %d\n", ap.state);

  	assert(ap.state == ST_READY || ap.state == ST_EXPIRE);
+	ap.state = ST_SHUTDOWN_PENDING;

  	/* Turn off timeouts */
  	alarm(0);

-	/* Prevent any new mounts */
-	sigprocmask(SIG_SETMASK, &lock_sigs, NULL);
-
-	ap.state = ST_SHUTDOWN_PENDING;
-
  	/* Where're the boss, tell everyone to finish up */
  	if (getpid() == getpgrp())
  		signal_children(SIGUSR2);
@@ -844,8 +874,9 @@
  	case EXP_ERROR:
  	case EXP_PARTIAL:
  		/* It didn't work: return to ready */
+		ap.state = ST_READY;
  		alarm(ap.exp_runfreq);
-		return st_ready();
+		return 0;

  	case EXP_DONE:
  		/* All expired: go straight to exit */
@@ -853,8 +884,6 @@
  		return 1;

  	case EXP_STARTED:
-		/* Wait until expiry process finishes */
-		sigprocmask(SIG_SETMASK, &ready_sigs, NULL);
  		return 0;
  	}
  	return 1;
@@ -865,6 +894,7 @@
  	debug("st_prune(): state = %d\n", ap.state);

  	assert(ap.state == ST_READY);
+	ap.state = ST_PRUNE;

  	/* We're the boss, pass on the prune event */
  	if (getpid() == getpgrp()) 
@@ -878,11 +908,10 @@

  	case EXP_ERROR:
  	case EXP_PARTIAL:
+		ap.state = ST_READY;
  		return 1;

  	case EXP_STARTED:
-		ap.state = ST_PRUNE;
-		sigprocmask(SIG_SETMASK, &ready_sigs, NULL);
  		return 0;
  	}
  	return 1;
@@ -893,6 +922,7 @@
  	debug("st_expire(): state = %d\n", ap.state);

  	assert(ap.state == ST_READY);
+	ap.state = ST_EXPIRE;

  	switch (expire_proc(0)) {
  	case EXP_DONE:
@@ -902,12 +932,11 @@

  	case EXP_ERROR:
  	case EXP_PARTIAL:
+		ap.state = ST_READY;
  		alarm(ap.exp_runfreq);
  		return 1;

  	case EXP_STARTED:
-		ap.state = ST_EXPIRE;
-		sigprocmask(SIG_SETMASK, &ready_sigs, NULL);
  		return 0;
  	}
  	return 1;
@@ -947,7 +976,7 @@
  		if (poll(fds, 2, -1) == -1) {
  			if (errno == EINTR)
  				continue;
-			syslog(LOG_ERR, "get_pkt: poll failed: %m");
+			error("get_pkt: poll failed: %m");
  			return -1;
  		}

@@ -958,8 +987,7 @@
  			if (fullread(ap.state_pipe[0], &next_state, sizeof(next_state)))
  				continue;

-			sigprocmask(SIG_BLOCK, &lock_sigs, &old);
-
+			sigprocmask(SIG_BLOCK, &ready_sigs, &old);
  			if (next_state != ap.state) {
  				debug("get_pkt: state %d, next %d",
  					ap.state, next_state);
@@ -999,9 +1027,7 @@
  					      next_state);
  				}
  			}
-
-			if (ret)
-				sigprocmask(SIG_SETMASK, &old, NULL);
+			sigprocmask(SIG_SETMASK, &old, NULL);

  			if (ap.state == ST_SHUTDOWN)
  				return -1;
@@ -1484,24 +1510,25 @@

  /* Deal with the signals recieved by direct mount supervisor */
  static void sig_supervisor(int sig)
-{
+{ 
+	static struct syslog_data syslog_context = AUTOFS_SYSLOG_CONTEXT;
+	static struct syslog_data *slc = &syslog_context;
  	int save_errno = errno;

  	switch (sig) {
  	default:		/* all the signals not handled */
-		error("process %d got unexpected signal %d!", getpid(), sig);
+		error_r(slc, "process %d got unexpected signal %d!",
+			getpid(), sig);
  		return;
  		/* don't FALLTHROUGH */

  	case SIGTERM:
  	case SIGUSR2:
-		/* Tell everyone to finish up */
-		signal_children(sig);
+		ap.state = ST_SHUTDOWN_PENDING;
  		break;

  	case SIGUSR1:
-		/* Pass on the prune event and ignore self signal */
-		signal_children(sig);
+		ap.state = ST_PRUNE;
  		break;

  	case SIGCHLD:
@@ -1509,12 +1536,8 @@
  		break;

  	case SIGHUP:
-		ap.lookup->lookup_ghost(ap.path, ap.ghost, 0, ap.lookup->context);
-
  		/* Pass on the reread event and ignore self signal */
-		kill(0, SIGHUP);
-		discard_pending(SIGHUP);
-
+		ap.state = ST_READMAP;
  		break;
  	}
  	errno = save_errno;
@@ -1522,7 +1545,9 @@

  int supervisor(char *path)
  {
+	sigset_t olds;
  	unsigned int map = 0;
+	int ret;

  	ap.path = alloca(strlen(path) + 1);
  	strcpy(ap.path, path);
@@ -1536,8 +1561,46 @@
  		cleanup_exit(ap.path, 1);
  	}

+	ap.state = ST_READY;
  	setup_signals(sig_supervisor, sig_supervisor);

+	sigprocmask(SIG_BLOCK, &ready_sigs, &olds);
+	while (ap.state != ST_SHUTDOWN) {
+		switch (ap.state) {
+		case ST_READMAP:
+			ret = st_readmap();
+			if (!ret)
+				/* Warn but try to continue anyway */
+				warn("failed to read map");
+			signal_children(SIGHUP);
+			ap.state = ST_READY;
+			break;
+		case ST_SHUTDOWN_PENDING:
+			ret = signal_children(SIGUSR2);
+			if (!ret) {
+				ap.state = ST_SHUTDOWN;
+				sigprocmask(SIG_SETMASK, &olds, NULL);
+				continue;
+			}
+
+			/* Failed shutdown returns to ready */
+			warn("can't shutdown: filesystem %s still busy",
+				     ap.path);
+			ap.state = ST_READY;
+			break;
+		case ST_PRUNE:
+			/* Pass on the prune event and ignore self signal */
+			signal_children(SIGUSR1);
+			ap.state = ST_READY;
+			break;
+		default:
+			ap.state = ST_READY;
+			break;
+		}
+		sigsuspend(&olds);
+	}
+	sigprocmask(SIG_UNBLOCK, &ready_sigs, NULL);
+
  	while (waitpid(0, NULL, 0) > 0);

  	return 0;
@@ -1644,8 +1707,32 @@
  		kill(my_pid, SIGSTOP);

  	while (ap.state != ST_SHUTDOWN) {
-		if (handle_packet() && errno != EINTR)
-			break;
+		if (handle_packet()) {
+			sigset_t olds;
+			int ret, status = 0;
+
+			sigprocmask(SIG_BLOCK, &lock_sigs, &olds);
+			ret = ioctl(ap.ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status);
+			/*
+			 * If the ioctl fails assume the kernel doesn't have
+			 * AUTOFS_IOC_ASKUMOUNT and just continue.
+			 */
+			if (ret) {
+				sigprocmask(SIG_SETMASK, &olds, NULL);
+				break;
+			}
+			if (status) {
+				sigprocmask(SIG_SETMASK, &olds, NULL);
+				break;
+			}
+
+			/* Failed shutdown returns to ready */
+			warn("can't shutdown: filesystem %s still busy",
+					ap.path);
+			alarm(ap.exp_runfreq);
+			ap.state = ST_READY;
+			sigprocmask(SIG_SETMASK, &olds, NULL);
+		}
  	}

  	/* Mop up remaining kids */
diff -Nur autofs-4.1.4.orig/daemon/module.c autofs-4.1.4/daemon/module.c
--- autofs-4.1.4.orig/daemon/module.c	2004-01-30 00:01:22.000000000 +0800
+++ autofs-4.1.4/daemon/module.c	2005-04-23 19:45:36.000000000 +0800
@@ -14,7 +14,6 @@
   * ----------------------------------------------------------------------- */

  #include <stdio.h>
-#include <syslog.h>
  #include <dlfcn.h>
  #include <string.h>
  #include <stdlib.h>
diff -Nur autofs-4.1.4.orig/daemon/mount.c autofs-4.1.4/daemon/mount.c
--- autofs-4.1.4.orig/daemon/mount.c	2004-11-17 22:38:27.000000000 +0800
+++ autofs-4.1.4/daemon/mount.c	2005-04-23 19:45:48.000000000 +0800
@@ -19,7 +19,6 @@
   *
   * ----------------------------------------------------------------------- */

-#include <syslog.h>
  #include <stdlib.h>
  #include <string.h>
  #include "automount.h"
diff -Nur autofs-4.1.4.orig/daemon/spawn.c autofs-4.1.4/daemon/spawn.c
--- autofs-4.1.4.orig/daemon/spawn.c	2005-02-10 20:56:53.000000000 +0800
+++ autofs-4.1.4/daemon/spawn.c	2005-04-23 19:46:01.000000000 +0800
@@ -21,7 +21,6 @@
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
-#include <syslog.h>
  #include <sys/types.h>
  #include <unistd.h>
  #include <time.h>
diff -Nur autofs-4.1.4.orig/include/automount.h autofs-4.1.4/include/automount.h
--- autofs-4.1.4.orig/include/automount.h	2005-01-26 21:03:02.000000000 +0800
+++ autofs-4.1.4/include/automount.h	2005-04-23 21:54:01.000000000 +0800
@@ -15,6 +15,9 @@
  #include <time.h>
  #include "config.h"

+/* OpenBSD re-entrant syslog */
+#include "syslog.h"
+
  /* We MUST have the paths to mount(8) and umount(8) */
  #ifndef HAVE_MOUNT
  #error Failed to locate mount(8)!
@@ -283,24 +286,78 @@
  int allow_owner_mount(const char *);

  /* log notification */
-extern int do_verbose;
-extern int do_debug;
-
-#define info(msg, args...) 		\
-if (do_verbose || do_debug) 		\
-	syslog(LOG_INFO, msg, ##args);
-
-#define warn(msg, args...) 			\
-if (do_verbose || do_debug) 		\
-	syslog(LOG_WARNING, msg, ##args);
-
-#define error(msg, args...)	syslog(LOG_ERR, msg, ##args);
+extern int do_verbose;		/* Verbose feedback option */
+extern int do_debug;		/* Enable full debug output */

-#define crit(msg, args...)	syslog(LOG_CRIT, msg, ##args);
+/* Define non-reentrant logging macros */

-#define debug(msg, args...) 		\
-if (do_debug) 				\
-	syslog(LOG_DEBUG, msg, ##args);
+#define debug(msg, args...)				\
+do {							\
+	if (do_debug)					\
+		syslog(LOG_DEBUG, msg, ##args);		\
+} while (0)
+
+#define info(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		syslog(LOG_INFO, msg, ##args);		\
+} while (0)
+
+#define notice(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		syslog(LOG_NOTICE, msg, ##args);	\
+} while (0)
+
+#define warn(msg, args...)				\
+do {							\
+	if (do_verbose || do_debug)			\
+		syslog(LOG_WARNING, msg, ##args);	\
+} while (0)
+
+#define error(msg, args...)				\
+do {							\
+	syslog(LOG_ERR, msg, ##args);			\
+} while (0)
+
+#define crit(msg, args...)				\
+do {							\
+	syslog(LOG_CRIT, msg, ##args);			\
+} while (0)
+
+#define alert(msg, args...)				\
+do {							\
+	syslog(LOG_ALERT, msg, ##args);			\
+} while (0)
+
+#define emerg(msg, args...)				\
+do {							\
+	syslog(LOG_EMERG, msg, ##args);			\
+} while (0)
+
+/* Define reentrant logging macros for signal handlers */
+
+#define debug_r(context, msg, args...)				\
+do {								\
+	if (do_debug)						\
+		syslog_r(LOG_DEBUG, context, msg, ##args);	\
+} while (0)
+
+#define warn_r(context, msg, args...)				\
+do {								\
+	if (do_verbose || do_debug)				\
+		syslog_r(LOG_WARNING, context, msg, ##args);	\
+} while (0)
+
+#define error_r(context, msg, args...)			\
+do {							\
+	syslog_r(LOG_ERR, context, msg, ##args);	\
+} while (0)
+
+#define crit_r(context, msg, args...)			\
+do {							\
+	syslog_r(LOG_CRIT, context, msg, ##args);	\
+} while (0)

  #endif

diff -Nur autofs-4.1.4.orig/include/syslog.h autofs-4.1.4/include/syslog.h
--- autofs-4.1.4.orig/include/syslog.h	1970-01-01 08:00:00.000000000 +0800
+++ autofs-4.1.4/include/syslog.h	2005-04-24 01:00:58.000000000 +0800
@@ -0,0 +1,201 @@
+/*	$OpenBSD: syslog.h,v 1.11 2003/08/24 01:27:07 avsm Exp $	*/
+/*	$NetBSD: syslog.h,v 1.14 1996/04/03 20:46:44 christos Exp $	*/
+
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ *
+ *	@(#)syslog.h	8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _SYS_SYSLOG_H_
+#define _SYS_SYSLOG_H_
+
+#include <features.h>
+#define __need___va_list
+#include <stdarg.h>
+
+#define	_PATH_LOG	"/dev/log"
+
+/*
+ * priorities/facilities are encoded into a single 32-bit quantity, where the
+ * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
+ * (0-big number).  Both the priorities and the facilities map roughly
+ * one-to-one to strings in the syslogd(8) source code.  This mapping is
+ * included in this file.
+ *
+ * priorities (these are ordered)
+ */
+#define	LOG_EMERG	0	/* system is unusable */
+#define	LOG_ALERT	1	/* action must be taken immediately */
+#define	LOG_CRIT	2	/* critical conditions */
+#define	LOG_ERR		3	/* error conditions */
+#define	LOG_WARNING	4	/* warning conditions */
+#define	LOG_NOTICE	5	/* normal but significant condition */
+#define	LOG_INFO	6	/* informational */
+#define	LOG_DEBUG	7	/* debug-level messages */
+
+#define	LOG_PRIMASK	0x07	/* mask to extract priority part (internal) */
+				/* extract priority */
+#define	LOG_PRI(p)	((p) & LOG_PRIMASK)
+#define	LOG_MAKEPRI(fac, pri)	(((fac) << 3) | (pri))
+
+#ifdef SYSLOG_NAMES
+#define	INTERNAL_NOPRI	0x10	/* the "no priority" priority */
+				/* mark "facility" */
+#define	INTERNAL_MARK	LOG_MAKEPRI(LOG_NFACILITIES, 0)
+typedef struct _code {
+	char	*c_name;
+	int	c_val;
+} CODE;
+
+CODE prioritynames[] = {
+	{ "alert",	LOG_ALERT },
+	{ "crit",	LOG_CRIT },
+	{ "debug",	LOG_DEBUG },
+	{ "emerg",	LOG_EMERG },
+	{ "err",	LOG_ERR },
+	{ "error",	LOG_ERR },		/* DEPRECATED */
+	{ "info",	LOG_INFO },
+	{ "none",	INTERNAL_NOPRI },	/* INTERNAL */
+	{ "notice",	LOG_NOTICE },
+	{ "panic", 	LOG_EMERG },		/* DEPRECATED */
+	{ "warn",	LOG_WARNING },		/* DEPRECATED */
+	{ "warning",	LOG_WARNING },
+	{ NULL,		-1 },
+};
+#endif
+
+/* facility codes */
+#define	LOG_KERN	(0<<3)	/* kernel messages */
+#define	LOG_USER	(1<<3)	/* random user-level messages */
+#define	LOG_MAIL	(2<<3)	/* mail system */
+#define	LOG_DAEMON	(3<<3)	/* system daemons */
+#define	LOG_AUTH	(4<<3)	/* security/authorization messages */
+#define	LOG_SYSLOG	(5<<3)	/* messages generated internally by syslogd */
+#define	LOG_LPR		(6<<3)	/* line printer subsystem */
+#define	LOG_NEWS	(7<<3)	/* network news subsystem */
+#define	LOG_UUCP	(8<<3)	/* UUCP subsystem */
+#define	LOG_CRON	(9<<3)	/* clock daemon */
+#define	LOG_AUTHPRIV	(10<<3)	/* security/authorization messages (private) */
+#define	LOG_FTP		(11<<3)	/* ftp daemon */
+
+	/* other codes through 15 reserved for system use */
+#define	LOG_LOCAL0	(16<<3)	/* reserved for local use */
+#define	LOG_LOCAL1	(17<<3)	/* reserved for local use */
+#define	LOG_LOCAL2	(18<<3)	/* reserved for local use */
+#define	LOG_LOCAL3	(19<<3)	/* reserved for local use */
+#define	LOG_LOCAL4	(20<<3)	/* reserved for local use */
+#define	LOG_LOCAL5	(21<<3)	/* reserved for local use */
+#define	LOG_LOCAL6	(22<<3)	/* reserved for local use */
+#define	LOG_LOCAL7	(23<<3)	/* reserved for local use */
+
+#define	LOG_NFACILITIES	24	/* current number of facilities */
+#define	LOG_FACMASK	0x03f8	/* mask to extract facility part */
+				/* facility of pri */
+#define	LOG_FAC(p)	(((p) & LOG_FACMASK) >> 3)
+
+#ifdef SYSLOG_NAMES
+CODE facilitynames[] = {
+	{ "auth",	LOG_AUTH },
+	{ "authpriv",	LOG_AUTHPRIV },
+	{ "cron", 	LOG_CRON },
+	{ "daemon",	LOG_DAEMON },
+	{ "ftp",	LOG_FTP },
+	{ "kern",	LOG_KERN },
+	{ "lpr",	LOG_LPR },
+	{ "mail",	LOG_MAIL },
+	{ "mark", 	INTERNAL_MARK },	/* INTERNAL */
+	{ "news",	LOG_NEWS },
+	{ "security",	LOG_AUTH },		/* DEPRECATED */
+	{ "syslog",	LOG_SYSLOG },
+	{ "user",	LOG_USER },
+	{ "uucp",	LOG_UUCP },
+	{ "local0",	LOG_LOCAL0 },
+	{ "local1",	LOG_LOCAL1 },
+	{ "local2",	LOG_LOCAL2 },
+	{ "local3",	LOG_LOCAL3 },
+	{ "local4",	LOG_LOCAL4 },
+	{ "local5",	LOG_LOCAL5 },
+	{ "local6",	LOG_LOCAL6 },
+	{ "local7",	LOG_LOCAL7 },
+	{ NULL,		-1 },
+};
+#endif
+
+/* Used by reentrant functions */
+
+struct syslog_data {
+	int	log_file;
+	int	connected;
+	int	opened;
+	int	log_stat;
+	const char 	*log_tag;
+	int 	log_fac;
+	int 	log_mask;
+};
+
+#define SYSLOG_DATA_INIT {-1, 0, 0, 0, (const char *)0, LOG_USER, 0xff}
+
+/*
+ * arguments to setlogmask.
+ */
+#define	LOG_MASK(pri)	(1 << (pri))		/* mask for one priority */
+#define	LOG_UPTO(pri)	((1 << ((pri)+1)) - 1)	/* all priorities through pri */
+
+/*
+ * Option flags for openlog.
+ *
+ * LOG_ODELAY no longer does anything.
+ * LOG_NDELAY is the inverse of what it used to be.
+ */
+#define	LOG_PID		0x01	/* log the pid with each message */
+#define	LOG_CONS	0x02	/* log on the console if errors in sending */
+#define	LOG_ODELAY	0x04	/* delay open until first syslog() (default) */
+#define	LOG_NDELAY	0x08	/* don't delay open */
+#define	LOG_NOWAIT	0x10	/* don't wait for console forks: DEPRECATED */
+#define	LOG_PERROR	0x20	/* log to stderr as well */
+
+__BEGIN_DECLS
+void	closelog(void);
+void	openlog(const char *__ident, int __option, int __facility);
+int	setlogmask(int __mask);
+void	syslog(int __pri, const char *__fmt, ...)
+		__attribute__((__format__(__printf__, 2, 3)));
+void	vsyslog(int __pri, const char *, __gnuc_va_list __ap)
+		__attribute__((__format__(__printf__, 2, 0)));
+void	closelog_r(struct syslog_data *__data);
+void	openlog_r(const char *__ident, int __option, int __facility, struct syslog_data *__data);
+int	setlogmask_r(int __mask, struct syslog_data *__data);
+void	syslog_r(int __pri, struct syslog_data *__data, const char *__fmt, ...)
+		__attribute__((__format__(__printf__, 3, 4)));
+void	vsyslog_r(int __pri, struct syslog_data *__data, const char *__fmt, __gnuc_va_list __ap)
+		__attribute__((__format__(__printf__, 3, 0)));
+__END_DECLS
+
+#endif /* !_SYS_SYSLOG_H_ */
+
diff -Nur autofs-4.1.4.orig/lib/Makefile autofs-4.1.4/lib/Makefile
--- autofs-4.1.4.orig/lib/Makefile	2005-01-09 17:16:43.000000000 +0800
+++ autofs-4.1.4/lib/Makefile	2005-04-23 18:48:53.000000000 +0800
@@ -9,10 +9,12 @@
  RPCGEN = /usr/bin/rpcgen
  RANLIB = /usr/bin/ranlib

-SRCS = cache.c listmount.c cat_path.c rpc_subs.c mounts.c lock.c
+SRCS = cache.c listmount.c cat_path.c rpc_subs.c mounts.c lock.c \
+	syslog.c vsprintf.c
  RPCS = mount.h mount_clnt.c mount_xdr.c
  OBJS = cache.o mount_clnt.o mount_xdr.o listmount.o \
-	cat_path.o rpc_subs.o mounts.o lock.o
+	cat_path.o rpc_subs.o mounts.o lock.o \
+	syslog.o vsprintf.o

  LIB = autofs.a

diff -Nur autofs-4.1.4.orig/lib/cache.c autofs-4.1.4/lib/cache.c
--- autofs-4.1.4.orig/lib/cache.c	2005-02-06 14:00:53.000000000 +0800
+++ autofs-4.1.4/lib/cache.c	2005-04-23 19:46:48.000000000 +0800
@@ -21,7 +21,6 @@
  #include <string.h>
  #include <unistd.h>
  #include <ctype.h>
-#include <syslog.h>
  #include <stdio.h>
  #include <fcntl.h>
  #include <sys/param.h>
diff -Nur autofs-4.1.4.orig/lib/cat_path.c autofs-4.1.4/lib/cat_path.c
--- autofs-4.1.4.orig/lib/cat_path.c	2005-04-06 23:14:23.000000000 +0800
+++ autofs-4.1.4/lib/cat_path.c	2005-04-23 19:47:59.000000000 +0800
@@ -16,7 +16,6 @@
  #include <alloca.h>
  #include <string.h>
  #include <limits.h>
-#include <syslog.h>
  #include <ctype.h>

  /*
diff -Nur autofs-4.1.4.orig/lib/mounts.c autofs-4.1.4/lib/mounts.c
--- autofs-4.1.4.orig/lib/mounts.c	2005-01-17 23:09:28.000000000 +0800
+++ autofs-4.1.4/lib/mounts.c	2005-04-23 19:48:31.000000000 +0800
@@ -16,7 +16,6 @@
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
-#include <syslog.h>
  #include <mntent.h>
  #include <limits.h>
  #include <sys/types.h>
diff -Nur autofs-4.1.4.orig/lib/syslog.c autofs-4.1.4/lib/syslog.c
--- autofs-4.1.4.orig/lib/syslog.c	1970-01-01 08:00:00.000000000 +0800
+++ autofs-4.1.4/lib/syslog.c	2005-04-24 14:29:37.000000000 +0800
@@ -0,0 +1,383 @@
+#ident "$Id$"
+/*
+ * Copyright (c) 1983, 1988, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <netdb.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#include "syslog.h"
+
+#define	TBUF_LEN	2048
+#define	FMT_LEN		1024
+#define	INTERNALLOG	LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
+
+#define	DEC()					\
+	do {					\
+		if (prlen < 0)			\
+			prlen = 0;		\
+		if (prlen >= tbuf_left)		\
+			prlen = tbuf_left - 1;	\
+		p += prlen;			\
+		tbuf_left -= prlen;		\
+	} while (0)
+
+/* Use our internal printf routines */
+extern int snprintf_int(char * buf, size_t size, const char * fmt, ...)
+	__attribute__ ((format (printf, 3, 4)));
+extern int vsnprintf_int(char *buf, size_t size, const char *fmt, va_list args)
+	__attribute__ ((format (printf, 3, 0)));
+
+static struct syslog_data sdata = SYSLOG_DATA_INIT;
+static int LogType = SOCK_DGRAM;	/* type of socket connection */
+
+extern char	*__progname;		/* Program name, from crt0. */
+
+static void	disconnectlog_r(struct syslog_data *);	/* disconnect from syslogd */
+static void	connectlog_r(struct syslog_data *);	/* (re)connect to syslogd */
+
+/*
+ * syslog, vsyslog --
+ *	print message on log file; output is intended for syslogd(8).
+ */
+void
+syslog(int pri, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vsyslog(pri, fmt, ap);
+	va_end(ap);
+}
+
+void
+vsyslog(int pri, const char *fmt, va_list ap)
+{
+	vsyslog_r(pri, &sdata, fmt, ap);
+}
+
+void
+openlog(const char *ident, int logstat, int logfac)
+{
+	openlog_r(ident, logstat, logfac, &sdata);
+}
+
+void
+closelog(void)
+{
+	closelog_r(&sdata);
+}
+
+/* setlogmask -- set the log mask level */
+int
+setlogmask(int pmask)
+{
+	return setlogmask_r(pmask, &sdata);
+}
+
+/* Reentrant version of syslog, i.e. syslog_r() */
+
+void
+syslog_r(int pri, struct syslog_data *data, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vsyslog_r(pri, data, fmt, ap);
+	va_end(ap);
+}
+
+void
+vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap)
+{
+	int cnt;
+	char ch, *p, *t;
+	time_t now;
+	int fd, saved_errno, error;
+	char *stdp = NULL, tbuf[TBUF_LEN], fmt_cpy[FMT_LEN];
+	int tbuf_left, fmt_left, prlen;
+
+	/* Check for invalid bits. */
+	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
+		if (data == &sdata) {
+			syslog(INTERNALLOG,
+			    "syslog: unknown facility/priority: %x", pri);
+		} else {
+			syslog_r(INTERNALLOG, data,
+			    "syslog_r: unknown facility/priority: %x", pri);
+		}
+		pri &= LOG_PRIMASK|LOG_FACMASK;
+	}
+
+	/* Check priority against setlogmask values. */
+	if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
+		return;
+
+	saved_errno = errno;
+
+	/* Set default facility if none specified. */
+	if ((pri & LOG_FACMASK) == 0)
+		pri |= data->log_fac;
+
+	/* If we have been called through syslog(), no need for reentrancy. */
+	if (data == &sdata)
+		(void)time(&now);
+
+	p = tbuf;
+	tbuf_left = TBUF_LEN;
+
+	prlen = snprintf_int(p, tbuf_left, "<%d>", pri);
+	DEC();
+
+	/* 
+	 * syslogd will expand time automagically for reentrant case, and
+	 * for normal case, just do like before
+	 */
+	if (data == &sdata) {
+		prlen = strftime(p, tbuf_left, "%h %e %T ", localtime(&now));
+		DEC();
+	}
+
+	if (data->log_stat & LOG_PERROR)
+		stdp = p;
+	if (data->log_tag == NULL)
+		data->log_tag = __progname;
+	if (data->log_tag != NULL) {
+		prlen = snprintf_int(p, tbuf_left, "%s", data->log_tag);
+		DEC();
+	}
+	if (data->log_stat & LOG_PID) {
+		prlen = snprintf_int(p, tbuf_left, "[%ld]", (long)getpid());
+		DEC();
+	}
+	if (data->log_tag != NULL) {
+		if (tbuf_left > 1) {
+			*p++ = ':';
+			tbuf_left--;
+		}
+		if (tbuf_left > 1) {
+			*p++ = ' ';
+			tbuf_left--;
+		}
+	}
+
+	/* strerror() is not reentrant */
+
+	for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) {
+		if (ch == '%' && fmt[1] == 'm') {
+			++fmt;
+			if (data == &sdata) {
+				prlen = snprintf_int(t, fmt_left, "%s",
+				    strerror(saved_errno)); 
+			} else {
+				prlen = snprintf_int(t, fmt_left, "Error %d",
+				    saved_errno); 
+			}
+			if (prlen < 0)
+				prlen = 0;
+			if (prlen >= fmt_left)
+				prlen = fmt_left - 1;
+			t += prlen;
+			fmt_left -= prlen;
+		} else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
+			*t++ = '%';
+			*t++ = '%';
+			fmt++;
+			fmt_left -= 2;
+		} else {
+			if (fmt_left > 1) {
+				*t++ = ch;
+				fmt_left--;
+			}
+		}
+	}
+	*t = '\0';
+
+	prlen = vsnprintf_int(p, tbuf_left, fmt_cpy, ap);
+	DEC();
+	cnt = p - tbuf;
+
+	/* Output to stderr if requested. */
+	if (data->log_stat & LOG_PERROR) {
+		struct iovec iov[2];
+
+		iov[0].iov_base = stdp;
+		iov[0].iov_len = cnt - (stdp - tbuf);
+		iov[1].iov_base = "\n";
+		iov[1].iov_len = 1;
+		(void)writev(STDERR_FILENO, iov, 2);
+	}
+
+	/* Get connected, output the message to the local logger. */
+	if (!data->opened)
+		openlog_r(data->log_tag, data->log_stat, 0, data);
+	connectlog_r(data);
+
+	/* If we have a SOCK_STREAM connection, also send ASCII NUL as
+	   a record terminator.  */
+	if (LogType == SOCK_STREAM)
+		cnt++;
+
+	/*
+	 * If the send() failed, there are two likely scenarios:
+	 *  1) syslogd was restarted
+	 *  2) /dev/log is out of socket buffer space
+	 * We attempt to reconnect to /dev/log to take care of
+	 * case #1 and keep send()ing data to cover case #2
+	 * to give syslogd a chance to empty its socket buffer.
+	 */
+	if ((error = send(data->log_file, tbuf, cnt, 0)) < 0) {
+		if (errno != ENOBUFS) {
+			disconnectlog_r(data);
+			connectlog_r(data);
+		}
+		do {
+			usleep(1);
+			if ((error = send(data->log_file, tbuf, cnt, 0)) >= 0)
+				break;
+		} while (errno == ENOBUFS);
+	}
+
+	/*
+	 * Output the message to the console; try not to block
+	 * as a blocking console should not stop other processes.
+	 * Make sure the error reported is the one from the syslogd failure.
+	 */
+	if (error == -1 && (data->log_stat & LOG_CONS) &&
+	    (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
+		struct iovec iov[2];
+ 
+		p = strchr(tbuf, '>') + 1;
+		iov[0].iov_base = p;
+		iov[0].iov_len = cnt - (p - tbuf);
+		iov[1].iov_base = "\r\n";
+		iov[1].iov_len = 2;
+		(void)writev(fd, iov, 2);
+		(void)close(fd);
+	}
+
+	if (data != &sdata)
+		closelog_r(data);
+}
+
+static void
+disconnectlog_r(struct syslog_data *data)
+{
+	/*
+	 * If the user closed the FD and opened another in the same slot,
+	 * that's their problem.  They should close it before calling on
+	 * system services.
+	 */
+	if (data->log_file != -1) {
+		close(data->log_file);
+		data->log_file = -1;
+	}
+	data->connected = 0;		/* retry connect */
+}
+
+static void
+connectlog_r(struct syslog_data *data)
+{
+	struct sockaddr_un SyslogAddr;	/* AF_UNIX address of local logger */
+
+again:
+	if (data->log_file == -1) {
+		if ((data->log_file = socket(AF_UNIX, LogType, 0)) == -1)
+			return;
+		(void)fcntl(data->log_file, F_SETFD, 1);
+	}
+	if (data->log_file != -1 && !data->connected) {
+		int old_errno;
+ 
+		memset(&SyslogAddr, '\0', sizeof(SyslogAddr));
+		SyslogAddr.sun_family = AF_UNIX;
+		strncpy(SyslogAddr.sun_path, _PATH_LOG,
+		    sizeof(SyslogAddr.sun_path));
+		old_errno = errno;
+		if (connect(data->log_file, (struct sockaddr *)&SyslogAddr,
+		    sizeof(SyslogAddr)) == -1) {
+			int save_errno = errno;
+			(void)close(data->log_file);
+			data->log_file = -1;
+			if (LogType == SOCK_DGRAM && save_errno == EPROTOTYPE) {
+				/* retry with SOCK_STREAM */
+				LogType = SOCK_STREAM;
+				errno = old_errno;
+				goto again;
+			}
+		} else
+			data->connected = 1;
+	}
+}
+
+void
+openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data)
+{
+	if (ident != NULL)
+		data->log_tag = ident;
+	data->log_stat = logstat;
+	if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
+		data->log_fac = logfac;
+
+	if (data->log_stat & LOG_NDELAY)	/* open immediately */
+		connectlog_r(data);
+
+	data->opened = 1;	/* ident and facility has been set */
+}
+
+void
+closelog_r(struct syslog_data *data)
+{
+	(void)close(data->log_file);
+	data->log_file = -1;
+	data->connected = 0;
+	data->log_tag = NULL;
+}
+
+/* setlogmask -- set the log mask level */
+int
+setlogmask_r(int pmask, struct syslog_data *data)
+{
+	int omask;
+
+	omask = data->log_mask;
+	if (pmask != 0)
+		data->log_mask = pmask;
+	return (omask);
+}
diff -Nur autofs-4.1.4.orig/lib/vsprintf.c autofs-4.1.4/lib/vsprintf.c
--- autofs-4.1.4.orig/lib/vsprintf.c	1970-01-01 08:00:00.000000000 +0800
+++ autofs-4.1.4/lib/vsprintf.c	2005-04-24 11:23:36.000000000 +0800
@@ -0,0 +1,619 @@
+#ident "$Id$"
+/*
+ *  Stolen from the linux kernel.
+ *
+ *  License: GPL
+ */
+/*------------------ Original Copyright -----------------*/
+/*
+ *  linux/lib/vsprintf.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
+/*
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
+ */
+
+/* 
+ * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
+ * - changed to provide snprintf and vsnprintf functions
+ * So Feb  1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de>
+ * - scnprintf and vscnprintf
+ */
+
+/* Also copied from: */
+
+/*
+ *  linux/lib/string.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * -  Added strsep() which will replace strtok() soon (because strsep() is
+ *    reentrant and should be faster). Use only strsep() in new code, please.
+ *
+ * * Sat Feb 09 2002, Jason Thomas <jason@topic.com.au>,
+ *                    Matthew Hawkins <matt@mh.dropbear.id.au>
+ * -  Kissed strtok() goodbye
+ */
+/*-------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#define BITS_PER_LONG	__WORDSIZE
+#define PAGE_SIZE	getpagesize()
+
+
+#if BITS_PER_LONG == 64
+
+# define do_div(n,base) ({					\
+	uint32_t __base = (base);				\
+	uint32_t __rem;						\
+	__rem = ((uint64_t)(n)) % __base;			\
+	(n) = ((uint64_t)(n)) / __base;				\
+	__rem;							\
+ })
+
+#elif BITS_PER_LONG == 32
+
+/* Not needed on 64bit architectures */
+uint32_t __div64_32(uint64_t *n, uint32_t base)
+{
+	uint64_t rem = *n;
+	uint64_t b = base;
+	uint64_t res, d = 1;
+	uint32_t high = rem >> 32;
+
+	/* Reduce the thing a bit first */
+	res = 0;
+	if (high >= base) {
+		high /= base;
+		res = (uint64_t) high << 32;
+		rem -= (uint64_t) (high*base) << 32;
+	}
+
+	while ((int64_t)b > 0 && b < rem) {
+		b = b+b;
+		d = d+d;
+	}
+
+	do {
+		if (rem >= b) {
+			rem -= b;
+			res += d;
+		}
+		b >>= 1;
+		d >>= 1;
+	} while (d);
+
+	*n = res;
+	return rem;
+}
+
+/* The unnecessary pointer compare is there
+ * to check for type safety (n must be 64bit)
+ */
+# define do_div(n,base) ({				\
+	uint32_t __base = (base);			\
+	uint32_t __rem;					\
+	(void)(((typeof((n)) *)0) == ((uint64_t *)0));	\
+	if (((n) >> 32) == 0) {				\
+		__rem = (uint32_t)(n) % __base;		\
+		(n) = (uint32_t)(n) / __base;		\
+	} else 						\
+		__rem = __div64_32(&(n), __base);	\
+	__rem;						\
+ })
+
+# else
+
+# error do_div() does not yet support the C64
+
+#endif /* BITS_PER_LONG */
+
+
+/**
+ * strnlen - Find the length of a length-limited string
+ * @s: The string to be sized
+ * @count: The maximum number of bytes to search
+ */
+size_t strnlen(const char * s, size_t count)
+{
+	const char *sc;
+
+	for (sc = s; count-- && *sc != '\0'; ++sc)
+		/* nothing */;
+	return sc - s;
+}
+
+/**
+ * simple_strtoul - convert a string to an unsigned long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
+{
+	unsigned long result = 0,value;
+
+	if (!base) {
+		base = 10;
+		if (*cp == '0') {
+			base = 8;
+			cp++;
+			if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
+				cp++;
+				base = 16;
+			}
+		}
+	} else if (base == 16) {
+		if (cp[0] == '0' && toupper(cp[1]) == 'X')
+			cp += 2;
+	}
+	while (isxdigit(*cp) &&
+	       (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
+		result = result*base + value;
+		cp++;
+	}
+	if (endp)
+		*endp = (char *)cp;
+	return result;
+}
+
+/**
+ * simple_strtol - convert a string to a signed long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long simple_strtol(const char *cp,char **endp,unsigned int base)
+{
+	if(*cp=='-')
+		return -simple_strtoul(cp+1,endp,base);
+	return simple_strtoul(cp,endp,base);
+}
+
+/**
+ * simple_strtoull - convert a string to an unsigned long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base)
+{
+	unsigned long long result = 0,value;
+
+	if (!base) {
+		base = 10;
+		if (*cp == '0') {
+			base = 8;
+			cp++;
+			if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
+				cp++;
+				base = 16;
+			}
+		}
+	} else if (base == 16) {
+		if (cp[0] == '0' && toupper(cp[1]) == 'X')
+			cp += 2;
+	}
+	while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
+	    ? toupper(*cp) : *cp)-'A'+10) < base) {
+		result = result*base + value;
+		cp++;
+	}
+	if (endp)
+		*endp = (char *)cp;
+	return result;
+}
+
+/**
+ * simple_strtoll - convert a string to a signed long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long long simple_strtoll(const char *cp,char **endp,unsigned int base)
+{
+	if(*cp=='-')
+		return -simple_strtoull(cp+1,endp,base);
+	return simple_strtoull(cp,endp,base);
+}
+
+static int skip_atoi(const char **s)
+{
+	int i=0;
+
+	while (isdigit(**s))
+		i = i*10 + *((*s)++) - '0';
+	return i;
+}
+
+#define ZEROPAD	1		/* pad with zero */
+#define SIGN	2		/* unsigned/signed long */
+#define PLUS	4		/* show plus */
+#define SPACE	8		/* space if plus */
+#define LEFT	16		/* left justified */
+#define SPECIAL	32		/* 0x */
+#define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */
+
+static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type)
+{
+	char c,sign,tmp[66];
+	const char *digits;
+	static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+	static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	int i;
+
+	digits = (type & LARGE) ? large_digits : small_digits;
+	if (type & LEFT)
+		type &= ~ZEROPAD;
+	if (base < 2 || base > 36)
+		return NULL;
+	c = (type & ZEROPAD) ? '0' : ' ';
+	sign = 0;
+	if (type & SIGN) {
+		if ((signed long long) num < 0) {
+			sign = '-';
+			num = - (signed long long) num;
+			size--;
+		} else if (type & PLUS) {
+			sign = '+';
+			size--;
+		} else if (type & SPACE) {
+			sign = ' ';
+			size--;
+		}
+	}
+	if (type & SPECIAL) {
+		if (base == 16)
+			size -= 2;
+		else if (base == 8)
+			size--;
+	}
+	i = 0;
+	if (num == 0)
+		tmp[i++]='0';
+	else while (num != 0)
+		tmp[i++] = digits[do_div(num,base)];
+	if (i > precision)
+		precision = i;
+	size -= precision;
+	if (!(type&(ZEROPAD+LEFT))) {
+		while(size-->0) {
+			if (buf <= end)
+				*buf = ' ';
+			++buf;
+		}
+	}
+	if (sign) {
+		if (buf <= end)
+			*buf = sign;
+		++buf;
+	}
+	if (type & SPECIAL) {
+		if (base==8) {
+			if (buf <= end)
+				*buf = '0';
+			++buf;
+		} else if (base==16) {
+			if (buf <= end)
+				*buf = '0';
+			++buf;
+			if (buf <= end)
+				*buf = digits[33];
+			++buf;
+		}
+	}
+	if (!(type & LEFT)) {
+		while (size-- > 0) {
+			if (buf <= end)
+				*buf = c;
+			++buf;
+		}
+	}
+	while (i < precision--) {
+		if (buf <= end)
+			*buf = '0';
+		++buf;
+	}
+	while (i-- > 0) {
+		if (buf <= end)
+			*buf = tmp[i];
+		++buf;
+	}
+	while (size-- > 0) {
+		if (buf <= end)
+			*buf = ' ';
+		++buf;
+	}
+	return buf;
+}
+
+/**
+ * vsnprintf_int - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * The return value is the number of characters which would
+ * be generated for the given input, excluding the trailing
+ * '\0', as per ISO C99. If you want to have the exact
+ * number of characters written into @buf as return value
+ * (not including the trailing '\0'), use vscnprintf. If the
+ * return is greater than or equal to @size, the resulting
+ * string is truncated.
+ *
+ * Call this function if you are already dealing with a va_list.
+ * You probably want snprintf instead.
+ */
+int vsnprintf_int(char *buf, size_t size, const char *fmt, va_list args)
+{
+	int len;
+	unsigned long long num;
+	int i, base;
+	char *str, *end, c;
+	const char *s;
+
+	int flags;		/* flags to number() */
+
+	int field_width;	/* width of output field */
+	int precision;		/* min. # of digits for integers; max
+				   number of chars for from string */
+	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
+				/* 'z' support added 23/7/1999 S.H.    */
+				/* 'z' changed to 'Z' --davidm 1/25/99 */
+
+	/* Reject out-of-range values early */
+	if ((int) size < 0)
+		return 0;
+
+	str = buf;
+	end = buf + size - 1;
+
+	if (end < buf - 1) {
+		end = ((void *) -1);
+		size = end - buf + 1;
+	}
+
+	for (; *fmt ; ++fmt) {
+		if (*fmt != '%') {
+			if (str <= end)
+				*str = *fmt;
+			++str;
+			continue;
+		}
+
+		/* process flags */
+		flags = 0;
+		repeat:
+			++fmt;		/* this also skips first '%' */
+			switch (*fmt) {
+				case '-': flags |= LEFT; goto repeat;
+				case '+': flags |= PLUS; goto repeat;
+				case ' ': flags |= SPACE; goto repeat;
+				case '#': flags |= SPECIAL; goto repeat;
+				case '0': flags |= ZEROPAD; goto repeat;
+			}
+
+		/* get field width */
+		field_width = -1;
+		if (isdigit(*fmt))
+			field_width = skip_atoi(&fmt);
+		else if (*fmt == '*') {
+			++fmt;
+			/* it's the next argument */
+			field_width = va_arg(args, int);
+			if (field_width < 0) {
+				field_width = -field_width;
+				flags |= LEFT;
+			}
+		}
+
+		/* get the precision */
+		precision = -1;
+		if (*fmt == '.') {
+			++fmt; 
+			if (isdigit(*fmt))
+				precision = skip_atoi(&fmt);
+			else if (*fmt == '*') {
+				++fmt;
+				/* it's the next argument */
+				precision = va_arg(args, int);
+			}
+			if (precision < 0)
+				precision = 0;
+		}
+
+		/* get the conversion qualifier */
+		qualifier = -1;
+		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
+		    *fmt =='Z' || *fmt == 'z') {
+			qualifier = *fmt;
+			++fmt;
+			if (qualifier == 'l' && *fmt == 'l') {
+				qualifier = 'L';
+				++fmt;
+			}
+		}
+
+		/* default base */
+		base = 10;
+
+		switch (*fmt) {
+			case 'c':
+				if (!(flags & LEFT)) {
+					while (--field_width > 0) {
+						if (str <= end)
+							*str = ' ';
+						++str;
+					}
+				}
+				c = (unsigned char) va_arg(args, int);
+				if (str <= end)
+					*str = c;
+				++str;
+				while (--field_width > 0) {
+					if (str <= end)
+						*str = ' ';
+					++str;
+				}
+				continue;
+
+			case 's':
+				s = va_arg(args, char *);
+				if ((unsigned long)s < PAGE_SIZE)
+					s = "<NULL>";
+
+				len = strnlen(s, precision);
+
+				if (!(flags & LEFT)) {
+					while (len < field_width--) {
+						if (str <= end)
+							*str = ' ';
+						++str;
+					}
+				}
+				for (i = 0; i < len; ++i) {
+					if (str <= end)
+						*str = *s;
+					++str; ++s;
+				}
+				while (len < field_width--) {
+					if (str <= end)
+						*str = ' ';
+					++str;
+				}
+				continue;
+
+			case 'p':
+				if (field_width == -1) {
+					field_width = 2*sizeof(void *);
+					flags |= ZEROPAD;
+				}
+				str = number(str, end,
+						(unsigned long) va_arg(args, void *),
+						16, field_width, precision, flags);
+				continue;
+
+
+			case 'n':
+				/* FIXME:
+				* What does C99 say about the overflow case here? */
+				if (qualifier == 'l') {
+					long * ip = va_arg(args, long *);
+					*ip = (str - buf);
+				} else if (qualifier == 'Z' || qualifier == 'z') {
+					size_t * ip = va_arg(args, size_t *);
+					*ip = (str - buf);
+				} else {
+					int * ip = va_arg(args, int *);
+					*ip = (str - buf);
+				}
+				continue;
+
+			case '%':
+				if (str <= end)
+					*str = '%';
+				++str;
+				continue;
+
+				/* integer number formats - set up the flags and "break" */
+			case 'o':
+				base = 8;
+				break;
+
+			case 'X':
+				flags |= LARGE;
+			case 'x':
+				base = 16;
+				break;
+
+			case 'd':
+			case 'i':
+				flags |= SIGN;
+			case 'u':
+				break;
+
+			default:
+				if (str <= end)
+					*str = '%';
+				++str;
+				if (*fmt) {
+					if (str <= end)
+						*str = *fmt;
+					++str;
+				} else {
+					--fmt;
+				}
+				continue;
+		}
+		if (qualifier == 'L')
+			num = va_arg(args, long long);
+		else if (qualifier == 'l') {
+			num = va_arg(args, unsigned long);
+			if (flags & SIGN)
+				num = (signed long) num;
+		} else if (qualifier == 'Z' || qualifier == 'z') {
+			num = va_arg(args, size_t);
+		} else if (qualifier == 'h') {
+			num = (unsigned short) va_arg(args, int);
+			if (flags & SIGN)
+				num = (signed short) num;
+		} else {
+			num = va_arg(args, unsigned int);
+			if (flags & SIGN)
+				num = (signed int) num;
+		}
+		str = number(str, end, num, base,
+				field_width, precision, flags);
+	}
+	if (str <= end)
+		*str = '\0';
+	else if (size > 0)
+		/* don't write out a null byte if the buf size is zero */
+		*end = '\0';
+	/* the trailing null byte doesn't count towards the total
+	* ++str;
+	*/
+	return str-buf;
+}
+
+/**
+ * snprintf_int - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @...: Arguments for the format string
+ *
+ * The return value is the number of characters which would be
+ * generated for the given input, excluding the trailing null,
+ * as per ISO C99.  If the return is greater than or equal to
+ * @size, the resulting string is truncated.
+ */
+int snprintf_int(char * buf, size_t size, const char *fmt, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, fmt);
+	i=vsnprintf_int(buf,size,fmt,args);
+	va_end(args);
+	return i;
+}
+

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2005-04-24  9:37 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-04-14 12:55 [patch, rfc] fix hung automounter issues Jeff Moyer
2005-04-15  1:59 ` Ian Kent
2005-04-15 11:58   ` Jeff Moyer
2005-04-16  4:46     ` raven
2005-04-18 13:26 ` raven
2005-04-18 13:38   ` raven
2005-04-18 13:45     ` Jeff Moyer
2005-04-18 17:15       ` raven
2005-04-18 17:19         ` Jeff Moyer
2005-04-18 23:55     ` Jeff Moyer
2005-04-19  2:56       ` Ian Kent
2005-04-19 16:06       ` raven
2005-04-24  6:56       ` raven
2005-04-24  9:37         ` raven
2005-04-19  0:08   ` Jeff Moyer
2005-04-19  2:35     ` Ian Kent

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.