[PATCH] Merge FreeBSD RELENG_4 syslogd changes

Xin LI delphij at frontfree.net
Sun Jul 4 00:40:32 PDT 2004


There was several changes since DragonFly's fork from RELENG_4:

syslog.conf.5 (1.16.2.11 -> 1.16.2.13)
syslogd.8 (1.22.2.16 -> 1.22.2.17)
syslogd.c (1.59.2.26 -> 1.59.2.29)

For detailed change comments please consult FreeBSD's cvs commitlog.
Generally speaking, these changes improves security, cleans up the
code, and give administrators the ablity to have some log files
not to do sync after every changes, which is more friendly for busy
mail servers, etc.

I have attached a patchset to bring DragonFly's syslogd in sync with
FreeBSD RELENG_4's. These changes are essentially the same with FreeBSD's
changeset, as these code were not changed since the fork. Please consider
to commit these changes before 1.0-RELEASE.

Cheers,
-- 
Xin LI <delphij frontfree net>	http://www.delphij.net/
See complete headers for GPG key and other information.

Index: usr.sbin/syslogd/syslog.conf.5
===================================================================
RCS file: /home/dcvs/src/usr.sbin/syslogd/syslog.conf.5,v
retrieving revision 1.2
diff -u -r1.2 syslog.conf.5
--- usr.sbin/syslogd/syslog.conf.5	17 Jun 2003 04:30:03 -0000	1.2
+++ usr.sbin/syslogd/syslog.conf.5	4 Jul 2004 07:24:52 -0000
@@ -30,7 +30,7 @@
 .\" SUCH DAMAGE.
 .\"
 .\"     @(#)syslog.conf.5	8.1 (Berkeley) 6/9/93
-.\" $FreeBSD: src/usr.sbin/syslogd/syslog.conf.5,v 1.16.2.11 2003/03/12 22:08:15 trhodes Exp $
+.\" $FreeBSD: src/usr.sbin/syslogd/syslog.conf.5,v 1.16.2.13 2004/06/29 10:07:35 dwmalone Exp $
 .\" $DragonFly: src/usr.sbin/syslogd/syslog.conf.5,v 1.2 2003/06/17 04:30:03 dillon Exp $
 .\"
 .Dd June 9, 1993
@@ -197,6 +197,15 @@
 .Ql !-prog
 specification will match any message but the ones from that
 program.
+Multiple programs may be listed, separated by commas:
+.Ql !prog1,prog2
+matches messages from either program, while
+.Ql !-prog1,prog2
+matches all messages but those from
+.Ql prog1
+or
+.Ql prog2 .
+.Pp
 A
 .Em hostname
 specification of the form
@@ -216,6 +225,9 @@
 If the hostname is given as
 .Ql @ ,
 the local hostname will be used.
+As for program specifications, multiple comma-seprarated
+values may be specified for hostname specifications.
+.Pp
 A
 .Em program
 or
@@ -307,6 +319,22 @@
 .It
 A pathname (beginning with a leading slash).
 Selected messages are appended to the file.
+.Pp
+To ensure that kernel messages are written to disk promptly,
+.Nm
+calls
+.Xr fsync 2
+after writing messages from the kernel.
+Other messages are not synced explicitly.
+You may prefix a pathname with the minus sign,
+.Dq - ,
+to forego syncing the specified file after every kernel message.
+Note that you might lose information if the system crashes
+immediately following a write attempt.
+Nevertheless, using the
+.Dq -
+option may improve performance,
+especially if the kernel is logging many messages.
 .It
 A hostname (preceded by an at
 .Pq Dq @
@@ -411,6 +439,10 @@
 
 # Log all writes to /dev/console to a separate file.
 console.*						/var/log/console.log
+
+# Log ipfw messages without syncing after every message.
+!ipfw
+*.*							-/var/log/ipfw
 .Ed
 .Sh IMPLEMENTATION NOTES
 The
Index: usr.sbin/syslogd/syslogd.8
===================================================================
RCS file: /home/dcvs/src/usr.sbin/syslogd/syslogd.8,v
retrieving revision 1.2
diff -u -r1.2 syslogd.8
--- usr.sbin/syslogd/syslogd.8	17 Jun 2003 04:30:03 -0000	1.2
+++ usr.sbin/syslogd/syslogd.8	4 Jul 2004 07:26:00 -0000
@@ -30,7 +30,7 @@
 .\" SUCH DAMAGE.
 .\"
 .\"     @(#)syslogd.8	8.1 (Berkeley) 6/6/93
-.\" $FreeBSD: src/usr.sbin/syslogd/syslogd.8,v 1.22.2.16 2003/03/12 22:08:15 trhodes Exp $
+.\" $FreeBSD: src/usr.sbin/syslogd/syslogd.8,v 1.22.2.17 2004/06/29 10:07:35 dwmalone Exp $
 .\" $DragonFly: src/usr.sbin/syslogd/syslogd.8,v 1.2 2003/06/17 04:30:03 dillon Exp $
 .\"
 .Dd November 24, 2001
@@ -260,7 +260,7 @@
 .Sq Aq 5 .
 This priority code should map into the priorities defined in the
 include file
-.Aq Pa sys/syslog.h .
+.In sys/syslog.h .
 .Pp
 For security reasons,
 .Nm
Index: usr.sbin/syslogd/syslogd.c
===================================================================
RCS file: /home/dcvs/src/usr.sbin/syslogd/syslogd.c,v
retrieving revision 1.2
diff -u -r1.2 syslogd.c
--- usr.sbin/syslogd/syslogd.c	17 Jun 2003 04:30:03 -0000	1.2
+++ usr.sbin/syslogd/syslogd.c	4 Jul 2004 07:28:09 -0000
@@ -32,7 +32,7 @@
  *
  * @(#) Copyright (c) 1983, 1988, 1993, 1994 The Regents of the University of California.  All rights reserved.
  * @(#)syslogd.c	8.3 (Berkeley) 4/4/94
- * $FreeBSD: src/usr.sbin/syslogd/syslogd.c,v 1.59.2.26 2003/05/20 17:13:53 gshapiro Exp $
+ * $FreeBSD: src/usr.sbin/syslogd/syslogd.c,v 1.59.2.29 2004/06/29 10:07:35 dwmalone Exp $
  * $DragonFly: src/usr.sbin/syslogd/syslogd.c,v 1.2 2003/06/17 04:30:03 dillon Exp $
  */
 
@@ -49,7 +49,7 @@
  *
  * Defined Constants:
  *
- * MAXLINE -- the maximimum line length that can be handled.
+ * MAXLINE -- the maximum line length that can be handled.
  * DEFUPRI -- the default priority for user messages
  * DEFSPRI -- the default priority for kernel messages
  *
@@ -67,7 +67,7 @@
 #define DEFUPRI		(LOG_USER|LOG_NOTICE)
 #define DEFSPRI		(LOG_KERN|LOG_CRIT)
 #define TIMERINTVL	30		/* interval for checking flush, mark */
-#define TTYMSGTIME	1		/* timed out passed to ttymsg */
+#define TTYMSGTIME	1		/* timeout passed to ttymsg */
 
 #include <sys/param.h>
 #include <sys/ioctl.h>
@@ -140,6 +140,8 @@
 /*
  * This structure represents the files that will have log
  * copies printed.
+ * We require f_file to be valid if f_type is F_FILE, F_CONSOLE, F_TTY
+ * or if f_type if F_PIPE and f_pid > 0.
  */
 
 struct filed {
@@ -174,6 +176,8 @@
 	int	f_prevlen;			/* length of f_prevline */
 	int	f_prevcount;			/* repetition cnt of prevline */
 	u_int	f_repeatcount;			/* number of "repeated" msgs */
+	int	f_flags;			/* file-specific flags */
+#define FFLAG_SYNC 0x01
 };
 
 /*
@@ -253,7 +257,7 @@
 static int	Debug;		/* debug flag */
 static int	resolve = 1;	/* resolve hostname */
 static char	LocalHostName[MAXHOSTNAMELEN];	/* our hostname */
-static char	*LocalDomain;	/* our local domain name */
+static const char *LocalDomain;	/* our local domain name */
 static int	*finet;		/* Internet datagram socket */
 static int	fklog = -1;	/* /dev/klog */
 static int	Initialized;	/* set when we have initialized ourselves */
@@ -297,6 +301,7 @@
 static void	logmsg(int, const char *, const char *, int);
 static void	log_deadchild(pid_t, int, const char *);
 static void	markit(void);
+static int	skip_message(const char *, const char *, int);
 static void	printline(const char *, char *);
 static void	printsys(char *);
 static int	p_open(const char *, pid_t *);
@@ -359,6 +364,8 @@
 			KeepKernFac = 1;
 			break;
 		case 'l':
+			if (strlen(optarg) >= sizeof(sunx.sun_path))
+				errx(1, "%s path too long, exiting", optarg);
 			if (nfunix < MAXFUNIX)
 				funixn[nfunix++] = optarg;
 			else
@@ -372,6 +379,8 @@
 			resolve = 0;
 			break;
 		case 'p':		/* path */
+			if (strlen(optarg) >= sizeof(sunx.sun_path))
+				errx(1, "%s path too long, exiting", optarg);
 			funixn[0] = optarg;
 			break;
 		case 'P':		/* path for alt. PID */
@@ -386,7 +395,6 @@
 		case 'v':		/* log facility and priority */
 		  	LogFacPri++;
 			break;
-		case '?':
 		default:
 			usage();
 		}
@@ -630,24 +638,31 @@
 static void
 printline(const char *hname, char *msg)
 {
+	char *p, *q;
+	long n;
 	int c, pri;
-	char *p, *q, line[MAXLINE + 1];
+	char line[MAXLINE + 1];
 
 	/* test for special codes */
-	pri = DEFUPRI;
 	p = msg;
+	pri = DEFUPRI;
 	if (*p == '<') {
-		pri = 0;
-		while (isdigit(*++p))
-			pri = 10 * pri + (*p - '0');
-		if (*p == '>')
-			++p;
+		errno = 0;
+		n = strtol(p + 1, &q, 10);
+		if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) {
+			p = q + 1;
+			pri = n;
+		}
 	}
 	if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
 		pri = DEFUPRI;
 
-	/* don't allow users to log kernel messages */
-	if (LOG_FAC(pri) == LOG_KERN && !KeepKernFac)
+	/*
+	 * Don't allow users to log kernel messages.
+	 * NOTE: since LOG_KERN == 0 this will also match
+	 *       messages with no facility specified.
+	 */
+	if ((pri & LOG_FACMASK) == LOG_KERN && !KeepKernFac)
 		pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
 
 	q = line;
@@ -719,24 +734,31 @@
  * Take a raw input line from /dev/klog, format similar to syslog().
  */
 static void
-printsys(char *p)
+printsys(char *msg)
 {
-	int pri, flags;
+	char *p, *q;
+	long n;
+	int flags, isprintf, pri;
 
 	flags = ISKERNEL | SYNC_FILE | ADDDATE;	/* fsync after write */
+	p = msg;
 	pri = DEFSPRI;
+	isprintf = 1;
 	if (*p == '<') {
-		pri = 0;
-		while (isdigit(*++p))
-			pri = 10 * pri + (*p - '0');
-		if (*p == '>')
-			++p;
-		if ((pri & LOG_FACMASK) == LOG_CONSOLE)
-			flags |= IGN_CONS;
-	} else {
-		/* kernel printf's come out on console */
-		flags |= IGN_CONS;
+		errno = 0;
+		n = strtol(p + 1, &q, 10);
+		if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) {
+			p = q + 1;
+			pri = n;
+			isprintf = 0;
+		}
 	}
+	/*
+	 * Kernel printf's and LOG_CONSOLE messages have been displayed
+	 * on the console already.
+	 */
+	if (isprintf || (pri & LOG_FACMASK) == LOG_CONSOLE)
+		flags |= IGN_CONS;
 	if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
 		pri = DEFSPRI;
 	logmsg(pri, p, LocalHostName, flags);
@@ -745,6 +767,50 @@
 static time_t	now;
 
 /*
+ * Match a program or host name against a specification.
+ * Return a non-0 value if the message must be ignored
+ * based on the specification.
+ */
+static int
+skip_message(const char *name, const char *spec, int checkcase) {
+	const char *s;
+	char prev, next;
+	int exclude = 0;
+	/* Behaviour on explicit match */
+
+	if (spec == NULL)
+		return 0;
+	switch (*spec) {
+	case '-':
+		exclude = 1;
+		/*FALLTHROUGH*/
+	case '+':
+		spec++;
+		break;
+	default:
+		break;
+	}
+	if (checkcase)
+		s = strstr (spec, name);
+	else
+		s = strcasestr (spec, name);
+
+	if (s != NULL) {
+		prev = (s == spec ? ',' : *(s - 1));
+		next = *(s + strlen (name));
+
+		if (prev == ',' && (next == '\0' || next == ','))
+			/* Explicit match: skip iff the spec is an
+			   exclusive one. */
+			return exclude;
+	}
+
+	/* No explicit match for this name: skip the message iff
+	   the spec is an inclusive one. */
+	return !exclude;
+}
+
+/*
  * Log a message to the appropriate log files, users, etc. based on
  * the priority.
  */
@@ -794,7 +860,8 @@
 
 	/* extract program name */
 	for (i = 0; i < NAME_MAX; i++) {
-		if (!isprint(msg[i]) || msg[i] == ':' || msg[i] == '[')
+		if (!isprint(msg[i]) || msg[i] == ':' || msg[i] == '[' ||
+		    msg[i] == '/')
 			break;
 		prog[i] = msg[i];
 	}
@@ -831,34 +898,12 @@
 			continue;
 
 		/* skip messages with the incorrect hostname */
-		if (f->f_host)
-			switch (f->f_host[0]) {
-			case '+':
-				if (strcasecmp(from, f->f_host + 1) != 0)
-					continue;
-				break;
-			case '-':
-				if (strcasecmp(from, f->f_host + 1) == 0)
-					continue;
-				break;
-			}
+		if (skip_message(from, f->f_host, 0))
+			continue;
 
 		/* skip messages with the incorrect program name */
-		if (f->f_program)
-			switch (f->f_program[0]) {
-			case '+':
-				if (strcmp(prog, f->f_program + 1) != 0)
-					continue;
-				break;
-			case '-':
-				if (strcmp(prog, f->f_program + 1) == 0)
-					continue;
-				break;
-			default:
-				if (strcmp(prog, f->f_program) != 0)
-					continue;
-				break;
-			}
+		if (skip_message(prog, f->f_program, 1))
+			continue;
 
 		/* skip message to console if it has already been printed */
 		if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
@@ -923,6 +968,7 @@
 	struct addrinfo *r;
 	int i, l, lsent = 0;
 	char line[MAXLINE + 1], repbuf[80], greetings[200], *wmsg = NULL;
+	char nul[] = "", space[] = " ", lf[] = "\n", crlf[] = "\r\n";
 	const char *msgret;
 
 	v = iov;
@@ -933,14 +979,14 @@
 		    f->f_prevhost, ctime(&now));
 		if (v->iov_len > 0)
 			v++;
-		v->iov_base = "";
+		v->iov_base = nul;
 		v->iov_len = 0;
 		v++;
 	} else {
 		v->iov_base = f->f_lasttime;
 		v->iov_len = 15;
 		v++;
-		v->iov_base = " ";
+		v->iov_base = space;
 		v->iov_len = 1;
 		v++;
 	}
@@ -982,7 +1028,7 @@
 		v->iov_base = fp_buf;
 		v->iov_len = strlen(fp_buf);
 	} else {
-	        v->iov_base="";
+	        v->iov_base = nul;
 		v->iov_len = 0;
 	}
 	v++;
@@ -990,7 +1036,7 @@
 	v->iov_base = f->f_prevhost;
 	v->iov_len = strlen(v->iov_base);
 	v++;
-	v->iov_base = " ";
+	v->iov_base = space;
 	v->iov_len = 1;
 	v++;
 
@@ -1026,11 +1072,12 @@
 		if (strcasecmp(f->f_prevhost, LocalHostName))
 			l = snprintf(line, sizeof line - 1,
 			    "<%d>%.15s Forwarded from %s: %s",
-			    f->f_prevpri, iov[0].iov_base, f->f_prevhost,
-			    iov[5].iov_base);
+			    f->f_prevpri, (char *)iov[0].iov_base,
+			    f->f_prevhost, (char *)iov[5].iov_base);
 		else
 			l = snprintf(line, sizeof line - 1, "<%d>%.15s %s",
-			     f->f_prevpri, iov[0].iov_base, iov[5].iov_base);
+			     f->f_prevpri, (char *)iov[0].iov_base,
+			    (char *)iov[5].iov_base);
 		if (l < 0)
 			l = 0;
 		else if (l > MAXLINE)
@@ -1073,8 +1120,7 @@
 				/* case ENOBUFS: */
 				/* case ECONNREFUSED: */
 				default:
-					dprintf("removing entry\n", e);
-					(void)close(f->f_file);
+					dprintf("removing entry\n");
 					f->f_type = F_UNUSED;
 					break;
 				}
@@ -1084,7 +1130,7 @@
 
 	case F_FILE:
 		dprintf(" %s\n", f->f_un.f_fname);
-		v->iov_base = "\n";
+		v->iov_base = lf;
 		v->iov_len = 1;
 		if (writev(f->f_file, iov, 7) < 0) {
 			int e = errno;
@@ -1092,13 +1138,13 @@
 			f->f_type = F_UNUSED;
 			errno = e;
 			logerror(f->f_un.f_fname);
-		} else if (flags & SYNC_FILE)
+		} else if ((flags & SYNC_FILE) && (f->f_flags & FFLAG_SYNC))
 			(void)fsync(f->f_file);
 		break;
 
 	case F_PIPE:
 		dprintf(" %s\n", f->f_un.f_pipe.f_pname);
-		v->iov_base = "\n";
+		v->iov_base = lf;
 		v->iov_len = 1;
 		if (f->f_un.f_pipe.f_pid == 0) {
 			if ((f->f_file = p_open(f->f_un.f_pipe.f_pname,
@@ -1129,7 +1175,7 @@
 
 	case F_TTY:
 		dprintf(" %s%s\n", _PATH_DEV, f->f_un.f_fname);
-		v->iov_base = "\r\n";
+		v->iov_base = crlf;
 		v->iov_len = 2;
 
 		errno = 0;	/* ttymsg() only sometimes returns an errno */
@@ -1142,7 +1188,7 @@
 	case F_USERS:
 	case F_WALL:
 		dprintf("\n");
-		v->iov_base = "\r\n";
+		v->iov_base = crlf;
 		v->iov_len = 2;
 		wallmsg(f, iov);
 		break;
@@ -1179,7 +1225,9 @@
 	while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
 		if (ut.ut_name[0] == '\0')
 			continue;
-		(void)strlcpy(line, ut.ut_line, sizeof(line));
+		/* We must use strncpy since ut_* may not be NUL terminated. */
+		strncpy(line, ut.ut_line, sizeof(line) - 1);
+		line[sizeof(line) - 1] = '\0';
 		if (f->f_type == F_WALL) {
 			if ((p = ttymsg(iov, 7, line, TTYMSGTIME)) != NULL) {
 				errno = 0;	/* already in msg */
@@ -1300,7 +1348,12 @@
 logerror(const char *type)
 {
 	char buf[512];
+	static int recursed = 0;
 
+	/* If there's an error while trying to log an error, give up. */
+	if (recursed)
+		return;
+	recursed++;
 	if (errno)
 		(void)snprintf(buf,
 		    sizeof buf, "syslogd: %s: %s", type, strerror(errno));
@@ -1309,6 +1362,7 @@
 	errno = 0;
 	dprintf("%s\n", buf);
 	logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
+	recursed--;
 }
 
 static void
@@ -1325,8 +1379,10 @@
 		/* flush any pending output */
 		if (f->f_prevcount)
 			fprintlog(f, 0, (char *)NULL);
-		if (f->f_type == F_PIPE)
+		if (f->f_type == F_PIPE && f->f_un.f_pipe.f_pid > 0) {
 			(void)close(f->f_file);
+			f->f_un.f_pipe.f_pid = 0;
+		}
 	}
 	Initialized = was_initialized;
 	if (signo) {
@@ -1391,10 +1447,11 @@
 			(void)close(f->f_file);
 			break;
 		case F_PIPE:
-			(void)close(f->f_file);
-			if (f->f_un.f_pipe.f_pid > 0)
+			if (f->f_un.f_pipe.f_pid > 0) {
+				(void)close(f->f_file);
 				deadq_enter(f->f_un.f_pipe.f_pid,
 					    f->f_un.f_pipe.f_pname);
+			}
 			f->f_un.f_pipe.f_pid = 0;
 			break;
 		}
@@ -1457,7 +1514,8 @@
 			if (*p == '@')
 				p = LocalHostName;
 			for (i = 1; i < MAXHOSTNAMELEN - 1; i++) {
-				if (!isalnum(*p) && *p != '.' && *p != '-')
+				if (!isalnum(*p) && *p != '.' && *p != '-'
+                                    && *p != ',')
 					break;
 				host[i] = *p++;
 			}
@@ -1479,9 +1537,8 @@
 			prog[i] = 0;
 			continue;
 		}
-		for (p = strchr(cline, '\0'); isspace(*--p);)
-			continue;
-		*++p = '\0';
+		for (i = strlen(cline) - 1; i >= 0 && isspace(cline[i]); i--)
+			cline[i] = '\0';
 		f = (struct filed *)calloc(1, sizeof(*f));
 		if (f == NULL) {
 			logerror("calloc");
@@ -1555,7 +1612,7 @@
 cfline(const char *line, struct filed *f, const char *prog, const char *host)
 {
 	struct addrinfo hints, *res;
-	int error, i, pri;
+	int error, i, pri, syncfile;
 	const char *p, *q;
 	char *bp;
 	char buf[MAXLINE], ebuf[100];
@@ -1649,6 +1706,10 @@
 			pri = LOG_PRIMASK + 1;
 			pri_cmp = PRI_LT | PRI_EQ | PRI_GT;
 		} else {
+			/* Ignore trailing spaces. */
+			for (i = strlen(buf) - 1; i >= 0 && buf[i] == ' '; i--)
+				buf[i] = '\0';
+
 			pri = decode(buf, prioritynames);
 			if (pri < 0) {
 				(void)snprintf(ebuf, sizeof ebuf,
@@ -1699,6 +1760,12 @@
 	while (*p == '\t' || *p == ' ')
 		p++;
 
+	if (*p == '-') {
+		syncfile = 0;
+		p++;
+	} else
+		syncfile = 1;
+
 	switch (*p) {
 	case '@':
 		(void)strlcpy(f->f_un.f_forw.f_hname, ++p,
@@ -1722,6 +1789,8 @@
 			logerror(p);
 			break;
 		}
+		if (syncfile)
+			f->f_flags |= FFLAG_SYNC;
 		if (isatty(f->f_file)) {
 			if (strcmp(p, ctty) == 0)
 				f->f_type = F_CONSOLE;
@@ -2209,9 +2278,10 @@
  * opposed to a FILE *.
  */
 static int
-p_open(const char *prog, pid_t *pid)
+p_open(const char *prog, pid_t *rpid)
 {
 	int pfd[2], nulldesc, i;
+	pid_t pid;
 	sigset_t omask, mask;
 	char *argv[4]; /* sh -c cmd NULL */
 	char errmsg[200];
@@ -2226,7 +2296,7 @@
 	sigaddset(&mask, SIGALRM);
 	sigaddset(&mask, SIGHUP);
 	sigprocmask(SIG_BLOCK, &mask, &omask);
-	switch ((*pid = fork())) {
+	switch ((pid = fork())) {
 	case -1:
 		sigprocmask(SIG_SETMASK, &omask, 0);
 		close(nulldesc);
@@ -2284,9 +2354,10 @@
 		(void)snprintf(errmsg, sizeof errmsg,
 			       "Warning: cannot change pipe to PID %d to "
 			       "non-blocking behaviour.",
-			       (int)*pid);
+			       (int)pid);
 		logerror(errmsg);
 	}
+	*rpid = pid;
 	return (pfd[1]);
 }
 
Attachment:
pgp00000.pgp
-------------- next part --------------
A non-text attachment was scrubbed...
Name: pgp00000.pgp
Type: application/octet-stream
Size: 187 bytes
Desc: "Description: PGP signature"
URL: <http://lists.dragonflybsd.org/pipermail/submit/attachments/20040704/4390c9d1/attachment-0016.obj>


More information about the Submit mailing list