syslogd: optional support for dropping dups. closes bug 436.

diff --git a/include/usage.h b/include/usage.h
index 3ef4211..6b54a12 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -3686,22 +3686,24 @@
        "[OPTION]..."
 #define syslogd_full_usage \
        "System logging utility.\n" \
-       "Note that this version of syslogd ignores /etc/syslog.conf." \
-       "\n\nOptions:" \
-       "\n	-n		Run in foreground" \
-       "\n	-O FILE		Log to given file (default=/var/log/messages)" \
-       "\n	-l n		Set local log level" \
-       "\n	-S		Smaller logging output" \
+       "Note that this version of syslogd ignores /etc/syslog.conf.\n" \
+     "\nOptions:" \
+     "\n	-n		Run in foreground" \
+     "\n	-O FILE		Log to given file (default=/var/log/messages)" \
+     "\n	-l n		Set local log level" \
+     "\n	-S		Smaller logging output" \
 	USE_FEATURE_ROTATE_LOGFILE( \
-       "\n	-s SIZE		Max size (KB) before rotate (default=200KB, 0=off)" \
-       "\n	-b NUM		Number of rotated logs to keep (default=1, max=99, 0=purge)") \
+     "\n	-s SIZE		Max size (KB) before rotate (default=200KB, 0=off)" \
+     "\n	-b NUM		Number of rotated logs to keep (default=1, max=99, 0=purge)") \
 	USE_FEATURE_REMOTE_LOG( \
-       "\n	-R HOST[:PORT]	Log to IP or hostname on PORT (default PORT=514/UDP)" \
-       "\n	-L		Log locally and via network (default is network only if -R)") \
+     "\n	-R HOST[:PORT]	Log to IP or hostname on PORT (default PORT=514/UDP)" \
+     "\n	-L		Log locally and via network (default is network only if -R)") \
+	USE_FEATURE_SYSLOGD_DUP( \
+     "\n	-D		Drop duplicates") \
 	USE_FEATURE_IPC_SYSLOG( \
-       "\n	-C[size(KiB)]	Log to shared mem buffer (read it using logread)")
+     "\n	-C[size(KiB)]	Log to shared mem buffer (read it using logread)")
 	/* NB: -Csize shouldn't have space (because size is optional) */
-/*     "\n	-m MIN		Minutes between MARK lines (default=20, 0=off)" */
+/*   "\n	-m MIN		Minutes between MARK lines (default=20, 0=off)" */
 #define syslogd_example_usage \
        "$ syslogd -R masterlog:514\n" \
        "$ syslogd -R 192.168.1.1:601\n"
diff --git a/sysklogd/Config.in b/sysklogd/Config.in
index 78097ee..45f86ed 100644
--- a/sysklogd/Config.in
+++ b/sysklogd/Config.in
@@ -42,6 +42,14 @@
 	  measure to prevent system logs from being tampered with
 	  by an intruder.
 
+config FEATURE_SYSLOGD_DUP
+	bool "Support -D (drop dups) option"
+	default n
+	depends on SYSLOGD
+	help
+	  Option -D instructs syslogd to drop consecutive messages
+	  which are totally the same.
+
 config FEATURE_IPC_SYSLOG
 	bool "Circular Buffer support"
 	default n
diff --git a/sysklogd/syslogd.c b/sysklogd/syslogd.c
index 0d004bc..e54ade7 100644
--- a/sysklogd/syslogd.c
+++ b/sysklogd/syslogd.c
@@ -101,7 +101,7 @@
 	char *hostname;
 
 	/* We recv into recvbuf... */
-	char recvbuf[MAX_READ];
+	char recvbuf[MAX_READ * (1 + ENABLE_FEATURE_SYSLOGD_DUP)];
 	/* ...then copy to parsebuf, escaping control chars */
 	/* (can grow x2 max) */
 	char parsebuf[MAX_READ*2];
@@ -152,6 +152,7 @@
 	USE_FEATURE_REMOTE_LOG(    OPTBIT_remote     ,)	// -R
 	USE_FEATURE_REMOTE_LOG(    OPTBIT_locallog   ,)	// -L
 	USE_FEATURE_IPC_SYSLOG(    OPTBIT_circularlog,)	// -C
+	USE_FEATURE_SYSLOGD_DUP(   OPTBIT_dup        ,)	// -D
 
 	OPT_mark        = 1 << OPTBIT_mark    ,
 	OPT_nofork      = 1 << OPTBIT_nofork  ,
@@ -163,13 +164,15 @@
 	OPT_remotelog   = USE_FEATURE_REMOTE_LOG(    (1 << OPTBIT_remote     )) + 0,
 	OPT_locallog    = USE_FEATURE_REMOTE_LOG(    (1 << OPTBIT_locallog   )) + 0,
 	OPT_circularlog = USE_FEATURE_IPC_SYSLOG(    (1 << OPTBIT_circularlog)) + 0,
+	OPT_dup         = USE_FEATURE_SYSLOGD_DUP(   (1 << OPTBIT_dup        )) + 0,
 };
 #define OPTION_STR "m:nO:l:S" \
 	USE_FEATURE_ROTATE_LOGFILE("s:" ) \
 	USE_FEATURE_ROTATE_LOGFILE("b:" ) \
 	USE_FEATURE_REMOTE_LOG(    "R:" ) \
 	USE_FEATURE_REMOTE_LOG(    "L"  ) \
-	USE_FEATURE_IPC_SYSLOG(    "C::")
+	USE_FEATURE_IPC_SYSLOG(    "C::") \
+	USE_FEATURE_SYSLOGD_DUP(   "D"  )
 #define OPTION_DECL *opt_m, *opt_l \
 	USE_FEATURE_ROTATE_LOGFILE(,*opt_s) \
 	USE_FEATURE_ROTATE_LOGFILE(,*opt_b) \
@@ -538,6 +541,13 @@
 static void do_syslogd(void)
 {
 	int sock_fd;
+#if ENABLE_FEATURE_SYSLOGD_DUP
+	int last_sz = -1;
+	char *last_buf;
+	char *recvbuf = G.recvbuf;
+#else
+#define recvbuf (G.recvbuf)
+#endif
 
 	/* Set up signal handlers */
 	bb_signals(0
@@ -561,8 +571,16 @@
 
 	for (;;) {
 		size_t sz;
+
+#if ENABLE_FEATURE_SYSLOGD_DUP
+		last_buf = recvbuf;
+		if (recvbuf == G.recvbuf)
+			recvbuf = G.recvbuf + MAX_READ;
+		else
+			recvbuf = G.recvbuf;
+#endif
  read_again:
-		sz = safe_read(sock_fd, G.recvbuf, MAX_READ - 1);
+		sz = safe_read(sock_fd, recvbuf, MAX_READ - 1);
 		if (sz < 0)
 			bb_perror_msg_and_die("read from /dev/log");
 
@@ -577,11 +595,16 @@
 			 * IOW: newline is passed verbatim!
 			 * I take it to mean that it's syslogd's job
 			 * to make those look identical in the log files. */
-			if (G.recvbuf[sz-1] != '\0' && G.recvbuf[sz-1] != '\n')
+			if (recvbuf[sz-1] != '\0' && recvbuf[sz-1] != '\n')
 				break;
 			sz--;
 		}
-		/* TODO: maybe suppress duplicates? */
+#if ENABLE_FEATURE_SYSLOGD_DUP
+		if ((option_mask32 & OPT_dup) && (sz == last_sz))
+			if (memcmp(last_buf, recvbuf, sz) == 0)
+				continue;
+		last_sz = sz;
+#endif
 #if ENABLE_FEATURE_REMOTE_LOG
 		/* We are not modifying log messages in any way before send */
 		/* Remote site cannot trust _us_ anyway and need to do validation again */
@@ -593,18 +616,18 @@
 			}
 			/* Stock syslogd sends it '\n'-terminated
 			 * over network, mimic that */
-			G.recvbuf[sz] = '\n';
+			recvbuf[sz] = '\n';
 			/* send message to remote logger, ignore possible error */
 			/* TODO: on some errors, close and set G.remoteFD to -1
 			 * so that DNS resolution and connect is retried? */
-			sendto(G.remoteFD, G.recvbuf, sz+1, MSG_DONTWAIT,
+			sendto(G.remoteFD, recvbuf, sz+1, MSG_DONTWAIT,
 				    &G.remoteAddr->u.sa, G.remoteAddr->len);
  no_luck: ;
 		}
 #endif
 		if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog)) {
-			G.recvbuf[sz] = '\0'; /* ensure it *is* NUL terminated */
-			split_escape_and_log(G.recvbuf, sz);
+			recvbuf[sz] = '\0'; /* ensure it *is* NUL terminated */
+			split_escape_and_log(recvbuf, sz);
 		}
 	} /* for (;;) */
 }