From 561c800476941a00fc1ae7444c66ab5c0c32f6f2 Mon Sep 17 00:00:00 2001
From: chrfranke <chrfranke@4ea69e1a-61f1-4043-bf83-b5c94c648137>
Date: Wed, 7 Apr 2004 11:17:08 +0000
Subject: [PATCH] Combine several syslog output lines into one single event log
 entry.

git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk@1645 4ea69e1a-61f1-4043-bf83-b5c94c648137
---
 sm5/CHANGELOG                 |   5 +-
 sm5/os_win32/syslog_win32.c   | 400 +++++++++++++++++++++++++---------
 sm5/os_win32/syslog_win32.cpp | 400 +++++++++++++++++++++++++---------
 sm5/os_win32/syslogevt.c      |   8 +-
 sm5/os_win32/syslogevt.mc     | 129 ++++++++++-
 5 files changed, 718 insertions(+), 224 deletions(-)

diff --git a/sm5/CHANGELOG b/sm5/CHANGELOG
index 45b104568..b6802396b 100644
--- a/sm5/CHANGELOG
+++ b/sm5/CHANGELOG
@@ -1,6 +1,6 @@
 CHANGELOG for smartmontools
 
-$Id: CHANGELOG,v 1.391 2004/04/07 10:11:34 chrfranke Exp $
+$Id: CHANGELOG,v 1.392 2004/04/07 11:17:07 chrfranke Exp $
 
 The most recent version of this file is:
 http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/CHANGELOG?sortby=date&view=markup
@@ -27,6 +27,9 @@ NOTES FOR FUTURE RELEASES: see TODO file.
 
 <ADDITIONS TO THE CHANGE LOG SHOULD BE ADDED JUST BELOW HERE, PLEASE>
 
+  [CF] Win32/native smartd: Added thread to combine several syslog output
+       lines into one single event log entry.
+
   [CF] Win32 smartd: Added DEVICESCAN for SCSI/ASPI devices.
 
   [GG] Use gethostbyname() the get the DNS domain since getdomainname() 
diff --git a/sm5/os_win32/syslog_win32.c b/sm5/os_win32/syslog_win32.c
index 0d9a96c0e..832854f8a 100644
--- a/sm5/os_win32/syslog_win32.c
+++ b/sm5/os_win32/syslog_win32.c
@@ -24,89 +24,84 @@
 // file "<ident>.log", stdout, stderr, "<ident>[1-5].log".
 
 
+#include "syslog.h"
+
+#include <stdlib.h>
 #include <stdio.h>
 #include <time.h>
 #include <errno.h>
+#include <process.h> // getpid()
 
-#include "syslog.h"
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h> // RegisterEventSourceA(), ReportEventA(), ...
 
-const char *syslog_win32_c_cvsid = "$Id: syslog_win32.c,v 1.3 2004/03/26 19:30:58 chrfranke Exp $"
+const char *syslog_win32_c_cvsid = "$Id: syslog_win32.c,v 1.4 2004/04/07 11:17:08 chrfranke Exp $"
 SYSLOG_H_CVSID;
 
 #ifdef _MSC_VER
 // MSVC
-#include <process.h> // getpid()
 #define snprintf _snprintf
 #define vsnprintf _vsnprintf
-#else
-// MinGW
-#include <unistd.h> // getpid()
 #endif
 
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h> // RegisterEventSourceA(), ReportEventA(), ...
-
 #define ARGUSED(x) ((void)(x))
 
 
-// Only this event message id is used
-// It should be identical to MSG_SYSLOG in "syslogevt.h"
-// (generated by "mc" from "syslogevt.mc")
-#define MSG_SYSLOG                       0x00000000L
+#ifndef _MT
+//MT runtime not necessary, because thread uses no unsafe lib functions
+//#error Program must be linked with multithreaded runtime library
+#endif
 
 
-static char sl_ident[100];
-static char sl_logpath[sizeof(sl_ident) + sizeof("0.log")-1];
-static FILE * sl_logfile;
-static HANDLE sl_hevtsrc;
+#ifdef TESTEVT
+// Redirect event log to stdout for testing
 
+static BOOL Test_ReportEventA(HANDLE h, WORD type, WORD cat, DWORD id, PSID usid,
+						WORD nstrings, WORD datasize, LPCTSTR * strings, LPVOID data)
+{
+	int i;
+	printf("%u %lu:%s", type, id, nstrings != 1?"\n":"");
+	for (i = 0; i < nstrings; i++)
+		printf(" \"%s\"\n", strings[i]);
+	fflush(stdout);
+	return TRUE;
+}
 
-void openlog(const char *ident, int logopt, int facility)
+HANDLE Test_RegisterEventSourceA(LPCTSTR server, LPCTSTR source)
 {
-	strncpy(sl_ident, ident, sizeof(sl_ident)-1);
+	return (HANDLE)42;
+}
 
-	if (sl_logpath[0] || sl_logfile || sl_hevtsrc)
-		return; // Already open
+#define ReportEventA Test_ReportEventA
+#define RegisterEventSourceA Test_RegisterEventSourceA
+#endif // TESTEVT
 
-	if (facility == LOG_LOCAL0) // "ident.log"
-		strcat(strcpy(sl_logpath, sl_ident), ".log");
-	else if (facility == LOG_LOCAL1) // stdout
-		sl_logfile = stdout;
-	else if (facility == LOG_LOCAL2) // stderr
-		sl_logfile = stderr;
-	else if (LOG_LOCAL2 < facility && facility <= LOG_LOCAL7) { // "ident[1-5].log"
-		snprintf(sl_logpath, sizeof(sl_logpath)-1, "%s%d.log",
-			sl_ident, LOG_FAC(facility)-LOG_FAC(LOG_LOCAL2));
-	}
-	else // Assume LOG_DAEMON, use event log if possible, else "ident.log"
-	if (!(sl_hevtsrc = RegisterEventSourceA(NULL/*localhost*/, sl_ident))) {
-		// Cannot open => Use logfile
-		long err = GetLastError();
-		strcat(strcpy(sl_logpath, sl_ident), ".log");
-		if (GetVersion() & 0x80000000)
-			fprintf(stderr, "%s: No event log on Win9x/ME, writing to %s\n",
-				sl_ident, sl_logpath);
-		else
-			fprintf(stderr, "%s: Cannot register event source (Error=%ld), writing to %s\n",
-				sl_ident, err, sl_logpath);
-	}
-	//assert(sl_logpath[0] || sl_logfile || sl_hevtsrc);
 
-	// logopt==LOG_PID assumed
-	ARGUSED(logopt);
-}
+// Event message ids,
+// should be identical to MSG_SYSLOG* in "syslogevt.h"
+// (generated by "mc" from "syslogevt.mc")
+#define MSG_SYSLOG                       0x00000000L
+#define MSG_SYSLOG_01                    0x00000001L
+// ...
+#define MSG_SYSLOG_10                    0x0000000AL
 
+static char sl_ident[100];
+static char sl_logpath[sizeof(sl_ident) + sizeof("0.log")-1];
+static FILE * sl_logfile;
+static char sl_pidstr[16];
+static HANDLE sl_hevtsrc;
 
-void closelog()
-{
-#if 0
-	if (sl_hevtsrc) {
-		DeregisterEventSource(sl_hevtsrc); sl_hevtsrc = 0;
-	}
-#else
-	// Leave event message source open to prevent losing messages during shutdown
-#endif
-}
+
+// Ring buffer for event log output via thread
+#define MAXLINES 10
+#define LINELEN 200
+
+static HANDLE evt_hthread;
+static char evt_lines[MAXLINES][LINELEN+1];
+static int evt_priorities[MAXLINES];
+static volatile int evt_timeout;
+static int evt_index_in, evt_index_out;
+static HANDLE evt_wait_in, evt_wait_out;
 
 
 // Map syslog priority to event type
@@ -134,7 +129,7 @@ static const char * pri2text(int priority)
 	switch (priority) {
 		case LOG_EMERG:   return "EMERG";
 		case LOG_ALERT:   return "ALERT";
-		case LOG_CRIT:    return "CRIT ";
+		case LOG_CRIT:    return "CRIT";
 		default:
 		case LOG_ERR:     return "ERROR";
 		case LOG_WARNING: return "Warn";
@@ -145,50 +140,234 @@ static const char * pri2text(int priority)
 }
 
 
-// Format the log message
-// logfmt=0 => Event Log, logfmt=1 => Logfile
+// Output cnt events from ring buffer
 
-static int format_msg(char * buffer, int bufsiz, int logfmt,
-                      int priority, const char * message, va_list args)
+static void report_events(int cnt)
 {
-	int pid, n;
-	int len = 0;
-	if (logfmt) {
-		time_t now = time((time_t*)0);
-		n = strftime (buffer, bufsiz-1, "%Y-%m-%d %H:%M:%S ", localtime(&now));
-		if (n == 0)
-			return -1;
-		len = strlen(buffer);
-	}
-	// Assume logopt = LOG_PID 
-	pid = getpid();
-	if (pid < 0) // ugly Win9x/ME pid
-		n = snprintf(buffer+len, bufsiz-1-len-30, "%s[0x%X]: ",  sl_ident, pid);
-	else
-		n = snprintf(buffer+len, bufsiz-1-len-30, "%s[%d]: ",  sl_ident, pid);
-	if (n < 0)
+	const char * msgs[3+MAXLINES];
+
+	int i, pri;
+	if (cnt <= 0)
+		return;
+	if (cnt > MAXLINES)
+		cnt = MAXLINES;
+
+	pri = evt_priorities[evt_index_out];
+
+	msgs[0] = sl_ident;
+	msgs[1] = sl_pidstr;
+	msgs[2] = pri2text(pri);
+	for (i = 0; i < cnt; i++) {
+		//assert(evt_priorities[evt_index_out] == pri);
+		msgs[3+i] = evt_lines[evt_index_out];
+		if (++evt_index_out >= MAXLINES)
+			evt_index_out = 0;
+	}
+	ReportEventA(sl_hevtsrc,
+		pri2evtype(pri), // type
+		0, MSG_SYSLOG+cnt,    // category, message id
+		NULL,                 // no security id
+		(WORD)(3+cnt), 0,     // 3+cnt strings, ...
+		msgs, NULL);          // ...          , no data
+}
+
+
+// Thread to combine several syslog lines into one event log entry
+
+static ULONG WINAPI event_logger_thread(LPVOID arg)
+{
+	int cnt;
+	ARGUSED(arg);
+
+	cnt = 0;
+	for (;;) {
+		// Wait for first line ...
+		int prior, i, rest;
+		if (cnt == 0) {
+			if (WaitForSingleObject(evt_wait_out, (evt_timeout? INFINITE : 0)) != WAIT_OBJECT_0)
+				break;
+			cnt = 1;
+		}
+
+		// ... wait some time for more lines with same prior
+		i = evt_index_out;
+		prior = evt_priorities[i];
+		rest = 0;
+		while (cnt < MAXLINES) {
+			long timeout =
+				evt_timeout * ((1000L * (MAXLINES-cnt+1))/MAXLINES);
+			if (WaitForSingleObject(evt_wait_out, timeout) != WAIT_OBJECT_0)
+				break;
+			if (++i >= MAXLINES)
+				i = 0;
+			if (evt_priorities[i] != prior) {
+				rest = 1;
+				break;
+			}
+			cnt++;
+		}
+
+		// Output all in one event log entry
+		report_events(cnt);
+
+		// Signal space
+		if (!ReleaseSemaphore(evt_wait_in, cnt, NULL))
+			break;
+		cnt = rest;
+	}
+	return 0;
+}
+
+
+static void on_exit_event_logger(void)
+{
+	// Output lines immediate if exiting
+	evt_timeout = 0; 
+	// Wait for thread to finish
+	WaitForSingleObject(evt_hthread, 1000L);
+	CloseHandle(evt_hthread);
+#if 0
+	if (sl_hevtsrc) {
+		DeregisterEventSource(sl_hevtsrc); sl_hevtsrc = 0;
+	}
+#else
+	// Leave event message source open to prevent losing messages during shutdown
+#endif
+}
+
+
+static int start_event_logger()
+{
+	DWORD tid;
+	evt_timeout = 1;
+	if (   !(evt_wait_in  = CreateSemaphore(NULL,  MAXLINES, MAXLINES, NULL))
+		|| !(evt_wait_out = CreateSemaphore(NULL,         0, MAXLINES, NULL))) {
+		fprintf(stderr,"CreateSemaphore failed, Error=%ld\n", GetLastError());
 		return -1;
-	len += n;
-	// Append pri
-	if (logfmt) {
-		n = snprintf(buffer+len, bufsiz-1-len-20, "%-5s: ", pri2text(priority));
-		if (n < 0)
-			return -1;
-		len += n;
 	}
-	// Format message
-	n = vsnprintf(buffer+len, bufsiz-1-len-1, message, args);
-	if (n < 0)
+	if (!(evt_hthread = CreateThread(NULL, 0, event_logger_thread, NULL, 0, &tid))) {
+		fprintf(stderr,"CreateThread failed, Error=%ld\n", GetLastError());
 		return -1;
-	len += n;
-	// Remove trailing EOLs
-	while (buffer[len-1] == '\n')
-		len--;
-	if (logfmt)
-		buffer[len++] = '\n';
-	// TODO: Format multiline messages
-	buffer[len] = 0;
-	return len;
+	}
+	atexit(on_exit_event_logger);
+	return 0;
+}
+
+
+// Write lines to event log ring buffer
+
+static void write_event_log(int priority, const char * lines)
+{
+	int cnt = 0;
+	int i;
+	for (i = 0; lines[i]; i++) {
+		int len = 0;
+		while (lines[i+len] && lines[i+len] != '\n')
+			len++;
+			;
+		if (len > 0) {
+			// Wait for space
+			if (WaitForSingleObject(evt_wait_in, INFINITE) != WAIT_OBJECT_0)
+				return;
+			// Copy line
+			evt_priorities[evt_index_in] = priority;
+			memcpy(evt_lines[evt_index_in], lines+i, (len < LINELEN ? len : LINELEN));
+			if (len < LINELEN)
+				evt_lines[evt_index_in][len] = 0;
+			if (++evt_index_in >= MAXLINES)
+				evt_index_in = 0;
+			// Signal avail if ring buffer full
+			if (++cnt >= MAXLINES) {
+				ReleaseSemaphore(evt_wait_out, cnt, NULL);
+				cnt = 0;
+			}
+			i += len;
+		}
+		if (!lines[i])
+			break;
+	}
+
+	// Signal avail
+	if (cnt > 0)
+		ReleaseSemaphore(evt_wait_out, cnt, NULL);
+	Sleep(1);
+}
+
+
+// Write lines to logfile
+
+static void write_logfile(FILE * f, int priority, const char * lines)
+{
+	time_t now; char stamp[sizeof("2004-04-04 10:00:00")+13];
+	int i;
+
+	now = time((time_t*)0);
+	if (!strftime(stamp, sizeof(stamp)-1, "%Y-%m-%d %H:%M:%S", localtime(&now)))
+		strcpy(stamp,"?");
+
+	for (i = 0; lines[i]; i++) {
+		int len = 0;
+		while (lines[i+len] && lines[i+len] != '\n')
+			len++;
+		if (len > 0) {
+			fprintf(f, "%s %s[%s]: %-5s: ",
+				stamp, sl_ident, sl_pidstr, pri2text(priority));
+			fwrite(lines+i, len, 1, f);
+			fputc('\n', f);
+			i += len;
+		}
+		if (!lines[i])
+			break;
+	}
+}
+
+
+void openlog(const char *ident, int logopt, int facility)
+{
+	int pid;
+	if (sl_logpath[0] || sl_logfile || sl_hevtsrc)
+		return; // Already open
+
+	strncpy(sl_ident, ident, sizeof(sl_ident)-1);
+	// logopt==LOG_PID assumed
+	ARGUSED(logopt);
+	pid = getpid();
+	if (snprintf(sl_pidstr, sizeof(sl_pidstr)-1, (pid >= 0 ? "%d" : "0x%X"), pid) < 0)
+		strcpy(sl_pidstr,"?");
+
+	if (facility == LOG_LOCAL0) // "ident.log"
+		strcat(strcpy(sl_logpath, sl_ident), ".log");
+	else if (facility == LOG_LOCAL1) // stdout
+		sl_logfile = stdout;
+	else if (facility == LOG_LOCAL2) // stderr
+		sl_logfile = stderr;
+	else if (LOG_LOCAL2 < facility && facility <= LOG_LOCAL7) { // "ident[1-5].log"
+		snprintf(sl_logpath, sizeof(sl_logpath)-1, "%s%d.log",
+			sl_ident, LOG_FAC(facility)-LOG_FAC(LOG_LOCAL2));
+	}
+	else // Assume LOG_DAEMON, use event log if possible, else "ident.log"
+	if (!(sl_hevtsrc = RegisterEventSourceA(NULL/*localhost*/, sl_ident))) {
+		// Cannot open => Use logfile
+		long err = GetLastError();
+		strcat(strcpy(sl_logpath, sl_ident), ".log");
+		if (GetVersion() & 0x80000000)
+			fprintf(stderr, "%s: No event log on Win9x/ME, writing to %s\n",
+				sl_ident, sl_logpath);
+		else
+			fprintf(stderr, "%s: Cannot register event source (Error=%ld), writing to %s\n",
+				sl_ident, err, sl_logpath);
+	}
+	else {
+		// Start event log thread
+		start_event_logger();
+	}
+	//assert(sl_logpath[0] || sl_logfile || sl_hevtsrc);
+
+}
+
+
+void closelog()
+{
 }
 
 
@@ -201,22 +380,16 @@ void vsyslog(int priority, const char * message, va_list args)
 		message = "Internal error: \"%%m\" in log message";
 
 	// Format message
-	if (format_msg(buffer, sizeof(buffer), !sl_hevtsrc, priority, message, args) < 0)
-		strcpy(buffer, "Internal Error: buffer overflow\n");
+	if (vsnprintf(buffer, sizeof(buffer)-1, message, args) < 0)
+		strcpy(buffer, "Internal Error: buffer overflow");
 
 	if (sl_hevtsrc) {
 		// Write to event log
-		const char * msgs[1] = { buffer };
-		ReportEventA(sl_hevtsrc,
-			pri2evtype(priority), // type
-			0, MSG_SYSLOG,        // category, message id
-			NULL,                 // no security id
-			1   , 0,              // 1 string, ...
-			msgs, NULL);          // ...     , no data
+		write_event_log(priority, buffer);
 	}
 	else if (sl_logfile) {
 		// Write to stdout/err
-		fputs(buffer, sl_logfile);
+		write_logfile(sl_logfile, priority, buffer);
 		fflush(sl_logfile);
 	}
 	else if (sl_logpath[0]) {
@@ -224,13 +397,14 @@ void vsyslog(int priority, const char * message, va_list args)
 		FILE * f;
 		if (!(f = fopen(sl_logpath, "a")))
 			return;
-		fputs(buffer, f);
+		write_logfile(f, priority, buffer);
 		fclose(f);
 	}
 }
 
 
 #ifdef TEST
+// Test program
 
 void syslog(int priority, const char *message, ...)
 {
@@ -242,10 +416,18 @@ void syslog(int priority, const char *message, ...)
 
 int main(int argc, char* argv[])
 {
-	openlog(argc < 2 ? "test" : argv[1], LOG_PID, LOG_DAEMON);
+	int i;
+	openlog(argc < 2 ? "test" : argv[1], LOG_PID, (argc < 3 ? LOG_DAEMON : LOG_LOCAL1));
 	syslog(LOG_INFO,    "Info\n");
 	syslog(LOG_WARNING, "Warning %d\n\n", 42);
 	syslog(LOG_ERR,     "Error %s", "Fatal");
+	for (i = 0; i < 100; i++) {
+		char buf[LINELEN];
+		if (i % 13 == 0)
+			Sleep(1000L);
+		sprintf(buf, "Log Line %d\n", i);
+		syslog(i % 17 ? LOG_INFO : LOG_ERR, buf);
+	}
 	closelog();
 	return 0;
 }
diff --git a/sm5/os_win32/syslog_win32.cpp b/sm5/os_win32/syslog_win32.cpp
index 2b7fdbd94..2bb4da9ae 100644
--- a/sm5/os_win32/syslog_win32.cpp
+++ b/sm5/os_win32/syslog_win32.cpp
@@ -24,89 +24,84 @@
 // file "<ident>.log", stdout, stderr, "<ident>[1-5].log".
 
 
+#include "syslog.h"
+
+#include <stdlib.h>
 #include <stdio.h>
 #include <time.h>
 #include <errno.h>
+#include <process.h> // getpid()
 
-#include "syslog.h"
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h> // RegisterEventSourceA(), ReportEventA(), ...
 
-const char *syslog_win32_c_cvsid = "$Id: syslog_win32.cpp,v 1.3 2004/03/26 19:30:58 chrfranke Exp $"
+const char *syslog_win32_c_cvsid = "$Id: syslog_win32.cpp,v 1.4 2004/04/07 11:17:08 chrfranke Exp $"
 SYSLOG_H_CVSID;
 
 #ifdef _MSC_VER
 // MSVC
-#include <process.h> // getpid()
 #define snprintf _snprintf
 #define vsnprintf _vsnprintf
-#else
-// MinGW
-#include <unistd.h> // getpid()
 #endif
 
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h> // RegisterEventSourceA(), ReportEventA(), ...
-
 #define ARGUSED(x) ((void)(x))
 
 
-// Only this event message id is used
-// It should be identical to MSG_SYSLOG in "syslogevt.h"
-// (generated by "mc" from "syslogevt.mc")
-#define MSG_SYSLOG                       0x00000000L
+#ifndef _MT
+//MT runtime not necessary, because thread uses no unsafe lib functions
+//#error Program must be linked with multithreaded runtime library
+#endif
 
 
-static char sl_ident[100];
-static char sl_logpath[sizeof(sl_ident) + sizeof("0.log")-1];
-static FILE * sl_logfile;
-static HANDLE sl_hevtsrc;
+#ifdef TESTEVT
+// Redirect event log to stdout for testing
 
+static BOOL Test_ReportEventA(HANDLE h, WORD type, WORD cat, DWORD id, PSID usid,
+						WORD nstrings, WORD datasize, LPCTSTR * strings, LPVOID data)
+{
+	int i;
+	printf("%u %lu:%s", type, id, nstrings != 1?"\n":"");
+	for (i = 0; i < nstrings; i++)
+		printf(" \"%s\"\n", strings[i]);
+	fflush(stdout);
+	return TRUE;
+}
 
-void openlog(const char *ident, int logopt, int facility)
+HANDLE Test_RegisterEventSourceA(LPCTSTR server, LPCTSTR source)
 {
-	strncpy(sl_ident, ident, sizeof(sl_ident)-1);
+	return (HANDLE)42;
+}
 
-	if (sl_logpath[0] || sl_logfile || sl_hevtsrc)
-		return; // Already open
+#define ReportEventA Test_ReportEventA
+#define RegisterEventSourceA Test_RegisterEventSourceA
+#endif // TESTEVT
 
-	if (facility == LOG_LOCAL0) // "ident.log"
-		strcat(strcpy(sl_logpath, sl_ident), ".log");
-	else if (facility == LOG_LOCAL1) // stdout
-		sl_logfile = stdout;
-	else if (facility == LOG_LOCAL2) // stderr
-		sl_logfile = stderr;
-	else if (LOG_LOCAL2 < facility && facility <= LOG_LOCAL7) { // "ident[1-5].log"
-		snprintf(sl_logpath, sizeof(sl_logpath)-1, "%s%d.log",
-			sl_ident, LOG_FAC(facility)-LOG_FAC(LOG_LOCAL2));
-	}
-	else // Assume LOG_DAEMON, use event log if possible, else "ident.log"
-	if (!(sl_hevtsrc = RegisterEventSourceA(NULL/*localhost*/, sl_ident))) {
-		// Cannot open => Use logfile
-		long err = GetLastError();
-		strcat(strcpy(sl_logpath, sl_ident), ".log");
-		if (GetVersion() & 0x80000000)
-			fprintf(stderr, "%s: No event log on Win9x/ME, writing to %s\n",
-				sl_ident, sl_logpath);
-		else
-			fprintf(stderr, "%s: Cannot register event source (Error=%ld), writing to %s\n",
-				sl_ident, err, sl_logpath);
-	}
-	//assert(sl_logpath[0] || sl_logfile || sl_hevtsrc);
 
-	// logopt==LOG_PID assumed
-	ARGUSED(logopt);
-}
+// Event message ids,
+// should be identical to MSG_SYSLOG* in "syslogevt.h"
+// (generated by "mc" from "syslogevt.mc")
+#define MSG_SYSLOG                       0x00000000L
+#define MSG_SYSLOG_01                    0x00000001L
+// ...
+#define MSG_SYSLOG_10                    0x0000000AL
 
+static char sl_ident[100];
+static char sl_logpath[sizeof(sl_ident) + sizeof("0.log")-1];
+static FILE * sl_logfile;
+static char sl_pidstr[16];
+static HANDLE sl_hevtsrc;
 
-void closelog()
-{
-#if 0
-	if (sl_hevtsrc) {
-		DeregisterEventSource(sl_hevtsrc); sl_hevtsrc = 0;
-	}
-#else
-	// Leave event message source open to prevent losing messages during shutdown
-#endif
-}
+
+// Ring buffer for event log output via thread
+#define MAXLINES 10
+#define LINELEN 200
+
+static HANDLE evt_hthread;
+static char evt_lines[MAXLINES][LINELEN+1];
+static int evt_priorities[MAXLINES];
+static volatile int evt_timeout;
+static int evt_index_in, evt_index_out;
+static HANDLE evt_wait_in, evt_wait_out;
 
 
 // Map syslog priority to event type
@@ -134,7 +129,7 @@ static const char * pri2text(int priority)
 	switch (priority) {
 		case LOG_EMERG:   return "EMERG";
 		case LOG_ALERT:   return "ALERT";
-		case LOG_CRIT:    return "CRIT ";
+		case LOG_CRIT:    return "CRIT";
 		default:
 		case LOG_ERR:     return "ERROR";
 		case LOG_WARNING: return "Warn";
@@ -145,50 +140,234 @@ static const char * pri2text(int priority)
 }
 
 
-// Format the log message
-// logfmt=0 => Event Log, logfmt=1 => Logfile
+// Output cnt events from ring buffer
 
-static int format_msg(char * buffer, int bufsiz, int logfmt,
-                      int priority, const char * message, va_list args)
+static void report_events(int cnt)
 {
-	int pid, n;
-	int len = 0;
-	if (logfmt) {
-		time_t now = time((time_t*)0);
-		n = strftime (buffer, bufsiz-1, "%Y-%m-%d %H:%M:%S ", localtime(&now));
-		if (n == 0)
-			return -1;
-		len = strlen(buffer);
-	}
-	// Assume logopt = LOG_PID 
-	pid = getpid();
-	if (pid < 0) // ugly Win9x/ME pid
-		n = snprintf(buffer+len, bufsiz-1-len-30, "%s[0x%X]: ",  sl_ident, pid);
-	else
-		n = snprintf(buffer+len, bufsiz-1-len-30, "%s[%d]: ",  sl_ident, pid);
-	if (n < 0)
+	const char * msgs[3+MAXLINES];
+
+	int i, pri;
+	if (cnt <= 0)
+		return;
+	if (cnt > MAXLINES)
+		cnt = MAXLINES;
+
+	pri = evt_priorities[evt_index_out];
+
+	msgs[0] = sl_ident;
+	msgs[1] = sl_pidstr;
+	msgs[2] = pri2text(pri);
+	for (i = 0; i < cnt; i++) {
+		//assert(evt_priorities[evt_index_out] == pri);
+		msgs[3+i] = evt_lines[evt_index_out];
+		if (++evt_index_out >= MAXLINES)
+			evt_index_out = 0;
+	}
+	ReportEventA(sl_hevtsrc,
+		pri2evtype(pri), // type
+		0, MSG_SYSLOG+cnt,    // category, message id
+		NULL,                 // no security id
+		(WORD)(3+cnt), 0,     // 3+cnt strings, ...
+		msgs, NULL);          // ...          , no data
+}
+
+
+// Thread to combine several syslog lines into one event log entry
+
+static ULONG WINAPI event_logger_thread(LPVOID arg)
+{
+	int cnt;
+	ARGUSED(arg);
+
+	cnt = 0;
+	for (;;) {
+		// Wait for first line ...
+		int prior, i, rest;
+		if (cnt == 0) {
+			if (WaitForSingleObject(evt_wait_out, (evt_timeout? INFINITE : 0)) != WAIT_OBJECT_0)
+				break;
+			cnt = 1;
+		}
+
+		// ... wait some time for more lines with same prior
+		i = evt_index_out;
+		prior = evt_priorities[i];
+		rest = 0;
+		while (cnt < MAXLINES) {
+			long timeout =
+				evt_timeout * ((1000L * (MAXLINES-cnt+1))/MAXLINES);
+			if (WaitForSingleObject(evt_wait_out, timeout) != WAIT_OBJECT_0)
+				break;
+			if (++i >= MAXLINES)
+				i = 0;
+			if (evt_priorities[i] != prior) {
+				rest = 1;
+				break;
+			}
+			cnt++;
+		}
+
+		// Output all in one event log entry
+		report_events(cnt);
+
+		// Signal space
+		if (!ReleaseSemaphore(evt_wait_in, cnt, NULL))
+			break;
+		cnt = rest;
+	}
+	return 0;
+}
+
+
+static void on_exit_event_logger(void)
+{
+	// Output lines immediate if exiting
+	evt_timeout = 0; 
+	// Wait for thread to finish
+	WaitForSingleObject(evt_hthread, 1000L);
+	CloseHandle(evt_hthread);
+#if 0
+	if (sl_hevtsrc) {
+		DeregisterEventSource(sl_hevtsrc); sl_hevtsrc = 0;
+	}
+#else
+	// Leave event message source open to prevent losing messages during shutdown
+#endif
+}
+
+
+static int start_event_logger()
+{
+	DWORD tid;
+	evt_timeout = 1;
+	if (   !(evt_wait_in  = CreateSemaphore(NULL,  MAXLINES, MAXLINES, NULL))
+		|| !(evt_wait_out = CreateSemaphore(NULL,         0, MAXLINES, NULL))) {
+		fprintf(stderr,"CreateSemaphore failed, Error=%ld\n", GetLastError());
 		return -1;
-	len += n;
-	// Append pri
-	if (logfmt) {
-		n = snprintf(buffer+len, bufsiz-1-len-20, "%-5s: ", pri2text(priority));
-		if (n < 0)
-			return -1;
-		len += n;
 	}
-	// Format message
-	n = vsnprintf(buffer+len, bufsiz-1-len-1, message, args);
-	if (n < 0)
+	if (!(evt_hthread = CreateThread(NULL, 0, event_logger_thread, NULL, 0, &tid))) {
+		fprintf(stderr,"CreateThread failed, Error=%ld\n", GetLastError());
 		return -1;
-	len += n;
-	// Remove trailing EOLs
-	while (buffer[len-1] == '\n')
-		len--;
-	if (logfmt)
-		buffer[len++] = '\n';
-	// TODO: Format multiline messages
-	buffer[len] = 0;
-	return len;
+	}
+	atexit(on_exit_event_logger);
+	return 0;
+}
+
+
+// Write lines to event log ring buffer
+
+static void write_event_log(int priority, const char * lines)
+{
+	int cnt = 0;
+	int i;
+	for (i = 0; lines[i]; i++) {
+		int len = 0;
+		while (lines[i+len] && lines[i+len] != '\n')
+			len++;
+			;
+		if (len > 0) {
+			// Wait for space
+			if (WaitForSingleObject(evt_wait_in, INFINITE) != WAIT_OBJECT_0)
+				return;
+			// Copy line
+			evt_priorities[evt_index_in] = priority;
+			memcpy(evt_lines[evt_index_in], lines+i, (len < LINELEN ? len : LINELEN));
+			if (len < LINELEN)
+				evt_lines[evt_index_in][len] = 0;
+			if (++evt_index_in >= MAXLINES)
+				evt_index_in = 0;
+			// Signal avail if ring buffer full
+			if (++cnt >= MAXLINES) {
+				ReleaseSemaphore(evt_wait_out, cnt, NULL);
+				cnt = 0;
+			}
+			i += len;
+		}
+		if (!lines[i])
+			break;
+	}
+
+	// Signal avail
+	if (cnt > 0)
+		ReleaseSemaphore(evt_wait_out, cnt, NULL);
+	Sleep(1);
+}
+
+
+// Write lines to logfile
+
+static void write_logfile(FILE * f, int priority, const char * lines)
+{
+	time_t now; char stamp[sizeof("2004-04-04 10:00:00")+13];
+	int i;
+
+	now = time((time_t*)0);
+	if (!strftime(stamp, sizeof(stamp)-1, "%Y-%m-%d %H:%M:%S", localtime(&now)))
+		strcpy(stamp,"?");
+
+	for (i = 0; lines[i]; i++) {
+		int len = 0;
+		while (lines[i+len] && lines[i+len] != '\n')
+			len++;
+		if (len > 0) {
+			fprintf(f, "%s %s[%s]: %-5s: ",
+				stamp, sl_ident, sl_pidstr, pri2text(priority));
+			fwrite(lines+i, len, 1, f);
+			fputc('\n', f);
+			i += len;
+		}
+		if (!lines[i])
+			break;
+	}
+}
+
+
+void openlog(const char *ident, int logopt, int facility)
+{
+	int pid;
+	if (sl_logpath[0] || sl_logfile || sl_hevtsrc)
+		return; // Already open
+
+	strncpy(sl_ident, ident, sizeof(sl_ident)-1);
+	// logopt==LOG_PID assumed
+	ARGUSED(logopt);
+	pid = getpid();
+	if (snprintf(sl_pidstr, sizeof(sl_pidstr)-1, (pid >= 0 ? "%d" : "0x%X"), pid) < 0)
+		strcpy(sl_pidstr,"?");
+
+	if (facility == LOG_LOCAL0) // "ident.log"
+		strcat(strcpy(sl_logpath, sl_ident), ".log");
+	else if (facility == LOG_LOCAL1) // stdout
+		sl_logfile = stdout;
+	else if (facility == LOG_LOCAL2) // stderr
+		sl_logfile = stderr;
+	else if (LOG_LOCAL2 < facility && facility <= LOG_LOCAL7) { // "ident[1-5].log"
+		snprintf(sl_logpath, sizeof(sl_logpath)-1, "%s%d.log",
+			sl_ident, LOG_FAC(facility)-LOG_FAC(LOG_LOCAL2));
+	}
+	else // Assume LOG_DAEMON, use event log if possible, else "ident.log"
+	if (!(sl_hevtsrc = RegisterEventSourceA(NULL/*localhost*/, sl_ident))) {
+		// Cannot open => Use logfile
+		long err = GetLastError();
+		strcat(strcpy(sl_logpath, sl_ident), ".log");
+		if (GetVersion() & 0x80000000)
+			fprintf(stderr, "%s: No event log on Win9x/ME, writing to %s\n",
+				sl_ident, sl_logpath);
+		else
+			fprintf(stderr, "%s: Cannot register event source (Error=%ld), writing to %s\n",
+				sl_ident, err, sl_logpath);
+	}
+	else {
+		// Start event log thread
+		start_event_logger();
+	}
+	//assert(sl_logpath[0] || sl_logfile || sl_hevtsrc);
+
+}
+
+
+void closelog()
+{
 }
 
 
@@ -201,22 +380,16 @@ void vsyslog(int priority, const char * message, va_list args)
 		message = "Internal error: \"%%m\" in log message";
 
 	// Format message
-	if (format_msg(buffer, sizeof(buffer), !sl_hevtsrc, priority, message, args) < 0)
-		strcpy(buffer, "Internal Error: buffer overflow\n");
+	if (vsnprintf(buffer, sizeof(buffer)-1, message, args) < 0)
+		strcpy(buffer, "Internal Error: buffer overflow");
 
 	if (sl_hevtsrc) {
 		// Write to event log
-		const char * msgs[1] = { buffer };
-		ReportEventA(sl_hevtsrc,
-			pri2evtype(priority), // type
-			0, MSG_SYSLOG,        // category, message id
-			NULL,                 // no security id
-			1   , 0,              // 1 string, ...
-			msgs, NULL);          // ...     , no data
+		write_event_log(priority, buffer);
 	}
 	else if (sl_logfile) {
 		// Write to stdout/err
-		fputs(buffer, sl_logfile);
+		write_logfile(sl_logfile, priority, buffer);
 		fflush(sl_logfile);
 	}
 	else if (sl_logpath[0]) {
@@ -224,13 +397,14 @@ void vsyslog(int priority, const char * message, va_list args)
 		FILE * f;
 		if (!(f = fopen(sl_logpath, "a")))
 			return;
-		fputs(buffer, f);
+		write_logfile(f, priority, buffer);
 		fclose(f);
 	}
 }
 
 
 #ifdef TEST
+// Test program
 
 void syslog(int priority, const char *message, ...)
 {
@@ -242,10 +416,18 @@ void syslog(int priority, const char *message, ...)
 
 int main(int argc, char* argv[])
 {
-	openlog(argc < 2 ? "test" : argv[1], LOG_PID, LOG_DAEMON);
+	int i;
+	openlog(argc < 2 ? "test" : argv[1], LOG_PID, (argc < 3 ? LOG_DAEMON : LOG_LOCAL1));
 	syslog(LOG_INFO,    "Info\n");
 	syslog(LOG_WARNING, "Warning %d\n\n", 42);
 	syslog(LOG_ERR,     "Error %s", "Fatal");
+	for (i = 0; i < 100; i++) {
+		char buf[LINELEN];
+		if (i % 13 == 0)
+			Sleep(1000L);
+		sprintf(buf, "Log Line %d\n", i);
+		syslog(i % 17 ? LOG_INFO : LOG_ERR, buf);
+	}
 	closelog();
 	return 0;
 }
diff --git a/sm5/os_win32/syslogevt.c b/sm5/os_win32/syslogevt.c
index 3651c0be5..65f8565f1 100644
--- a/sm5/os_win32/syslogevt.c
+++ b/sm5/os_win32/syslogevt.c
@@ -16,7 +16,7 @@
  *
  */
 
-static char rcsid[] = "$Id: syslogevt.c,v 1.1 2004/03/15 10:48:28 chrfranke Exp $";
+static char rcsid[] = "$Id: syslogevt.c,v 1.2 2004/04/07 11:17:08 chrfranke Exp $";
 
 #include <stdio.h>
 #include <string.h>
@@ -33,7 +33,7 @@ static char rcsid[] = "$Id: syslogevt.c,v 1.1 2004/03/15 10:48:28 chrfranke Exp
 static int usage()
 {
 	puts(
-		"syslogevt $Revision: 1.1 $ Copyright (C) 2004 Christian Franke\n"
+		"syslogevt $Revision: 1.2 $ Copyright (C) 2004 Christian Franke\n"
 		"Home page is http://smartmontools.sourceforge.net/\n"
 		"\n"
 		"Usage: syslogevt [-ru] name [ident ...]\n"
@@ -68,8 +68,8 @@ main(int argc, char ** argv)
 	FILE * f1, * f2;
 
 #ifdef _DEBUG
-	if (MSG_SYSLOG != 0) {
-		puts("Internal error: MSG_SYSLOG != 0"); return 1;
+	if (!(MSG_SYSLOG == 0 && MSG_SYSLOG_01 == 1 && MSG_SYSLOG_10 == 10)) {
+		puts("Internal error: MSG_SYSLOG_n != n"); return 1;
 	}
 #endif
 
diff --git a/sm5/os_win32/syslogevt.mc b/sm5/os_win32/syslogevt.mc
index 6848de223..049f2eb5a 100644
--- a/sm5/os_win32/syslogevt.mc
+++ b/sm5/os_win32/syslogevt.mc
@@ -16,12 +16,13 @@
 ; *
 ; */
 ;
-;// $Id: syslogevt.mc,v 1.1 2004/03/15 10:48:28 chrfranke Exp $
+;// $Id: syslogevt.mc,v 1.2 2004/04/07 11:17:08 chrfranke Exp $
 ;
 ;// Use message compiler "mc" to generate
 ;//   syslogevt.rc, syslogevt.h, msg00001.bin
 ;// from this file.
 ;// MSG_SYSLOG in syslogmsg.h must be zero
+;// MSG_SYSLOG_nn must be == nn
 ;
 ;
 
@@ -32,3 +33,129 @@ SymbolicName=MSG_SYSLOG
 Language=English
 %1
 .
+;// 1-10 Line SYSLOG Messages
+;// %1=Ident, %2=PID, %3=Severity, %[4-13]=Line 1-10
+MessageId=0x1
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_01
+Language=English
+%1[%2]:%3: %4
+.
+MessageId=0x2
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_02
+Language=English
+%1[%2]:%3%n
+%4%n
+%5
+.
+MessageId=0x3
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_03
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6
+.
+MessageId=0x4
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_04
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7
+.
+MessageId=0x5
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_05
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7%n
+%8
+.
+MessageId=0x6
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_06
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7%n
+%8%n
+%9
+.
+MessageId=0x7
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_07
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7%n
+%8%n
+%9%n
+%10
+.
+MessageId=0x8
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_08
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7%n
+%8%n
+%9%n
+%10%n
+%11
+.
+MessageId=0x9
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_09
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7%n
+%8%n
+%9%n
+%10%n
+%11%n
+%12
+.
+MessageId=0xa
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_10
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7%n
+%8%n
+%9%n
+%10%n
+%11%n
+%12%n
+%13
+.
-- 
GitLab