---
 Makefile |   10 +++
 email.c  |  167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 email.h  |   32 ++++++++++++
 mcelog.c |  103 ++++++++++++++++++++++++++++++++++++++
 mcelog.h |    1 
 msg.c    |    8 +++
 6 files changed, 318 insertions(+), 3 deletions(-)

Index: mcelog-1.0.2010.03.10/Makefile
===================================================================
--- mcelog-1.0.2010.03.10.orig/Makefile
+++ mcelog-1.0.2010.03.10/Makefile
@@ -1,3 +1,4 @@
+CONFIG_EMAIL := 1
 CFLAGS := -g -Os
 prefix := /usr
 etcprefix :=
@@ -34,7 +35,8 @@ OBJ := p4.o k8.o mcelog.o dmi.o tsc.o co
        client.o cache.o sysfs.o yellow.o page.o rbtree.o 	 \
        xeon75xx.o
 DISKDB_OBJ := diskdb.o dimm.o db.o
-CLEAN := mcelog dmi tsc dbquery .depend .depend.X dbquery.o ${DISKDB_OBJ}
+EMAIL_OBJ := email.o
+CLEAN := mcelog dmi tsc dbquery .depend .depend.X dbquery.o ${DISKDB_OBJ} ${EMAIL_OBJ}
 DOC := mce.pdf
 
 ADD_DEFINES :=
@@ -46,6 +48,12 @@ OBJ += ${DISKDB_OBJ}
 all: dbquery
 endif
 
+ifdef CONFIG_EMAIL
+ADD_DEFINES := -DCONFIG_EMAIL=1
+LDFLAGS := -lesmtp
+OBJ += ${EMAIL_OBJ}
+endif
+
 SRC := $(OBJ:.o=.c)
 
 mcelog: ${OBJ}
Index: mcelog-1.0.2010.03.10/email.c
===================================================================
--- /dev/null
+++ mcelog-1.0.2010.03.10/email.c
@@ -0,0 +1,167 @@
+#include <unistd.h>
+#include <signal.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <libesmtp.h>
+#include "mcelog.h"
+#include "email.h"
+
+#define MAX_STRING_LEN 512
+char c_recipient[MAX_STRING_LEN] = "";
+static int debug;
+
+static char buf[128];
+#define ERROR() { fprintf (stderr, "SMTP problem [%d] %s\n", __LINE__,	\
+			 smtp_strerror (smtp_errno (), buf, sizeof buf));	\
+		return -1; }
+
+
+void email_usage(void) {
+	fprintf(stderr,
+		"--email address     Requires daemon mode\n");
+}
+
+int email_cmd(int opt, int ac, char **av)
+{
+	char *arg = optarg;
+
+	switch (opt) {
+	case O_EMAIL_ADDRESS:
+		if (arg) {
+			if (strlen(arg) >= MAX_STRING_LEN) {
+				Eprintf("email address too long"
+					" [max:%d]\n", MAX_STRING_LEN);
+				return 0;
+			}
+			strcpy(c_recipient, arg);
+			return 1;
+		}
+	case O_EMAIL_DEBUG:
+		debug = 1;
+		return 0;
+	}
+	return 0;
+}
+
+/* Callback to prnt the recipient status */
+static void
+print_recipient_status (smtp_recipient_t recipient,
+                        const char *mailbox, void *arg)
+{
+	const smtp_status_t *status;
+
+	status = smtp_recipient_status (recipient);
+	if (debug)
+		printf ("%s: %d %s", mailbox, status->code, status->text);
+}
+
+void setup_mail_header(FILE *fp, struct mce *m)
+{
+	char host[MAX_STRING_LEN] = "lasso";
+	char domain[MAX_STRING_LEN] = "suse.de";
+
+	if (gethostname(host, (MAX_STRING_LEN-7)/2))
+		fprintf(stderr, "Cannot get host name\n");
+
+	if (getdomainname(domain, (MAX_STRING_LEN-7)/2))
+		fprintf(stderr, "Cannot get domain name\n");
+
+	fprintf(fp, "Return-Path: <brian.stafford@ukonline.co.uk>\r\n"
+		"Subject: Machine Check Exception on %s.%s detected\r\n"
+		"MIME-Version: 1.0\r\n"
+		"Content-Type: text/plain;\r\n"
+		"  charset=iso-8859-1\r\n"
+		"Content-Transfer-Encoding: 7bit\r\n\r\n", host, domain);
+}
+
+int send_mail(FILE *fp)
+{
+	char smtp_host[MAX_STRING_LEN] = "localhost:25";
+	char host[MAX_STRING_LEN];
+	char domain[MAX_STRING_LEN];
+	char from[MAX_STRING_LEN];
+
+	const smtp_status_t *status;
+	smtp_session_t session;
+	smtp_message_t message;
+	smtp_recipient_t recipient;
+	struct sigaction sa;
+
+	session = smtp_create_session ();
+	message = smtp_add_message (session);
+	/* half the hostname, half the domainname and
+	   7 chars for root@ and \0*/
+	if (gethostname(host, (MAX_STRING_LEN-7)/2)) {
+		fprintf(stderr, "Cannot get host name\n");
+		return -1;
+	}
+
+	if (getdomainname(domain, (MAX_STRING_LEN-7)/2)) {
+		fprintf(stderr, "Cannot get domain name\n");
+		return -1;
+	}
+
+	snprintf(from, MAX_STRING_LEN, "root@%s.%s", host, domain);
+
+	/* NB.  libESMTP sets timeouts as it progresses through the protocol.
+	   In addition the remote server might close its socket on a timeout.
+	   Consequently libESMTP may sometimes try to write to a socket with
+	   no reader.  Ignore SIGPIPE, then the program doesn't get killed
+	   if/when this happens. */
+	sa.sa_handler = SIG_IGN;
+	sigemptyset (&sa.sa_mask);
+	sa.sa_flags = 0;
+	sigaction (SIGPIPE, &sa, NULL);
+
+	/* Set the host running the SMTP server.  LibESMTP has a default port
+	   number of 587, however this is not widely deployed so the port
+	   is specified as 25 along with the default MTA host. */
+	if (!smtp_set_server (session, smtp_host))
+		ERROR();
+
+	smtp_set_reverse_path (message, from);
+
+	/* RFC 2822 doesn't require recipient headers but a To: header would
+	   be nice to have if not present. */
+	smtp_set_header (message, "To", NULL, NULL);
+
+	/* RFC 2822 doesn't require recipient headers but a To: header would
+	   be nice to have if not present. */
+	if (!smtp_set_header (message, "From", "mcelog", from))
+		ERROR();
+
+	smtp_set_message_fp (message, fp);
+
+	recipient = smtp_add_recipient (message, c_recipient);
+	if (!recipient)
+		ERROR();
+	if (!smtp_dsn_set_notify (recipient, Notify_NEVER))
+		ERROR();
+
+	/* Initiate a connection to the SMTP server and transfer the
+	   message. */
+	if (!smtp_start_session (session))
+		Eprintf("SMTP server problem %s\n",
+			smtp_strerror (smtp_errno (), buf, sizeof buf));
+	else {
+		/* Report on the success or otherwise of the mail transfer.
+		 */
+		if (debug) {
+			status = smtp_message_transfer_status (message);
+			printf ("%d %s", status->code,
+				(status->text != NULL) ? status->text : "\n");
+		}
+		smtp_enumerate_recipients (message, print_recipient_status, NULL);
+	}
+
+	if (debug)
+		fprintf(stderr, "Email sent successfully!\n");
+
+	/* Free resources consumed by the program.
+	 */
+	smtp_destroy_session (session);
+	return 0;
+}
Index: mcelog-1.0.2010.03.10/email.h
===================================================================
--- /dev/null
+++ mcelog-1.0.2010.03.10/email.h
@@ -0,0 +1,32 @@
+#ifndef _MCELOG_EMAIL_H_
+#define _MCELOG_EMAIL_H_
+
+extern FILE *email_fd;
+extern int email_mode;
+
+#ifdef CONFIG_EMAIL
+extern int send_mail(FILE *email_fd);
+extern void setup_mail_header(FILE *email_fd, struct mce *m);
+extern void email_usage(void);
+extern int email_cmd(int opt, int ac, char **av);
+
+#define EMAIL_OPTIONS					\
+	{ "email", 1, NULL, O_EMAIL_ADDRESS },		\
+	{ "email-debug", 0, NULL, O_EMAIL_DEBUG },
+
+enum email_options {
+	O_EMAIL_ADDRESS = O_EMAIL,
+	O_EMAIL_DEBUG,
+};
+
+#else
+/*
+static int send_mail(FILE *email_fd) { return 0; }
+static void setup_mail_header(FILE *email_fd) { return; };
+*/
+static void email_usage(void) { return; }
+static int email_cmd(int opt, int ac, char **av) { return 0; }
+#define EMAIL_OPTIONS
+#endif
+
+#endif
Index: mcelog-1.0.2010.03.10/mcelog.c
===================================================================
--- mcelog-1.0.2010.03.10.orig/mcelog.c
+++ mcelog-1.0.2010.03.10/mcelog.c
@@ -37,6 +37,7 @@
 #include <assert.h>
 #include <signal.h>
 #include <pwd.h>
+#include <sys/wait.h>
 #include "mcelog.h"
 #include "paths.h"
 #include "k8.h"
@@ -58,6 +59,9 @@
 #include "yellow.h"
 #include "page.h"
 
+#include "email.h"
+int email_mode;
+
 enum cputype cputype = CPU_GENERIC;	
 
 char *logfn = LOG_DEV_FILENAME; 
@@ -69,7 +73,7 @@ static double cpumhz;
 static int cpumhz_forced;
 int ascii_mode;
 int dump_raw_ascii;
-int daemon_mode;
+int daemon_mode = 0;
 static char *inputfile;
 char *processor_flags;
 static int foreground;
@@ -788,6 +792,7 @@ void usage(void)
 "--num-errors N      Only process N errors (for testing)\n"
 "--pidfile file	     Write pid of daemon into file\n"
 		);
+	email_usage();
 	diskdb_usage();
 	print_cputypes();
 	exit(1);
@@ -851,6 +856,7 @@ static struct option options[] = {
 	{ "num-errors", 1, NULL, O_NUMERRORS },
 	{ "pidfile", 1, NULL, O_PIDFILE },
 	DISKDB_OPTIONS
+	EMAIL_OPTIONS
 	{}
 };
 
@@ -1022,11 +1028,96 @@ static void drop_cred(void)
 	}
 }
 
+#ifdef CONFIG_EMAIL
+pid_t c_pid;
+#define MAX_STRING_LEN 512
+
+/* Not more than 12 mails in 5 mins... */
+#define LAST_LIMIT_COUNT (60 * 5)
+#define LIMIT_COUNT      12
+static time_t last_limit_count;
+static int limit_count;
+
+
+static int setup_email(struct mce *m) {
+	int pdes[2];
+	char host[MAX_STRING_LEN] = "";
+	char domain[MAX_STRING_LEN] = "";
+	static int suppressed;
+	int ret;
+
+	if (time(NULL) - last_limit_count < LAST_LIMIT_COUNT) {
+		if (limit_count >= LIMIT_COUNT && !suppressed) {
+			Eprintf("email rate limit [%d mails per %d mins]"
+				" reached, mails supressed\n",
+				LIMIT_COUNT, LAST_LIMIT_COUNT / 60);
+			suppressed = 1;
+		}
+		if (suppressed)
+			return -1;
+	} else {
+		suppressed = 0;
+		limit_count = 0;
+		last_limit_count = time(NULL);
+	}
+
+	limit_count++;
+
+	if (gethostname(host, (MAX_STRING_LEN-7)/2))
+		fprintf(stderr, "Cannot get host name\n");
+
+	if (getdomainname(domain, (MAX_STRING_LEN-7)/2))
+		fprintf(stderr, "Cannot get domain name\n");
+
+	ret = pipe(pdes);
+	if (ret)
+		return ret;
+
+	c_pid = fork();
+	if ( c_pid == 0 ) { /* child */
+		FILE *x = fdopen(pdes[0], "r");
+		close(pdes[1]);
+		send_mail(x);
+		exit(0);
+	} else {
+		close(pdes[0]);
+		/* something went wrong, better close... */
+		if (email_fd)
+			fclose(email_fd);
+		/* Wprintf will now also write into this pipe */
+		email_fd = fdopen(pdes[1], "w");
+		fprintf(email_fd, "Subject: Machine Check Exception "
+			"on %s.%s detected\r\n", host, domain);
+		setup_mail_header(email_fd, m);
+	}
+	return 0;
+}
+
+static int finish_email(void) {
+	int status;
+
+	fclose(email_fd);
+	fprintf(stderr, "Email set up for sending\n");
+	/* Anything else we can make sure we do not get orphaned threads? */
+	waitpid (c_pid, &status, WUNTRACED);
+	if (WIFSTOPPED(status)){
+		kill(c_pid, 9);
+		SYSERRprintf("Killed stopped email thread %d\n",
+			     c_pid);
+		return -1;
+	}
+	email_fd = NULL;
+	return 0;
+}
+
+#endif
+
 static void process(int fd, unsigned recordlen, unsigned loglen, char *buf)
 {	
 	int i; 
 	int len;
 	int finish = 0;
+	int mail_setup;
 
 	if (recordlen == 0) {
 		Wprintf("no data in mce record\n");
@@ -1037,19 +1128,23 @@ static void process(int fd, unsigned rec
 	if (len < 0) 
 		err("read"); 
 
-	for (i = 0; (i < len / (int)recordlen) && !finish; i++) { 
+	for (i = 0; (i < len / (int)recordlen) && !finish; i++) {
 		struct mce *mce = (struct mce *)(buf + i*recordlen);
 		mce_prepare(mce);
 		if (numerrors > 0 && --numerrors == 0)
 			finish = 1;
 		if (!mce_filter(mce, recordlen)) 
 			continue;
+		if (email_mode)
+			mail_setup = setup_email(mce);
 		if (!dump_raw_ascii) {
 			disclaimer();
 			Wprintf("MCE %d\n", i);
 			dump_mce(mce, recordlen);
 		} else
 			dump_mce_raw_ascii(mce, recordlen);
+		if (email_mode && !mail_setup)
+			finish_email();
 		flushlog();
 	}
 
@@ -1157,6 +1252,8 @@ int main(int ac, char **av)
 			exit(0);
 		} else if (diskdb_cmd(opt, ac, av)) {
 			exit(0);
+		} else if (email_cmd(opt, ac, av)) {
+			email_mode = 1;
 		} else if (opt == 0)
 			break;		    
 	} 
@@ -1165,6 +1262,8 @@ int main(int ac, char **av)
 		logfn = av[optind++];
 	if (av[optind])
 		usage();
+	/* email sending only in daemon mode */
+	email_mode &= daemon_mode;
 	checkdmi();
 	general_setup();
 		
Index: mcelog-1.0.2010.03.10/msg.c
===================================================================
--- mcelog-1.0.2010.03.10.orig/msg.c
+++ mcelog-1.0.2010.03.10/msg.c
@@ -8,10 +8,13 @@
 #include "mcelog.h"
 #include "msg.h"
 #include "memutil.h"
+#include "email.h"
+
 
 enum syslog_opt syslog_opt = SYSLOG_REMARK;
 int syslog_level = LOG_WARNING;
 static FILE *output_fh;
+       FILE *email_fd;
 static char *output_fn;
 
 int need_stdout(void)
@@ -135,6 +138,11 @@ int Wprintf(char *fmt, ...)
 		n = vfprintf(output_fh ? output_fh : stdout, fmt, ap);
 		va_end(ap);
 	}
+	if (email_fd) {
+ 		va_start(ap,fmt);
+ 		n = vfprintf(email_fd, fmt, ap);
+ 		va_end(ap);
+ 	}
 	return n;
 }
 
Index: mcelog-1.0.2010.03.10/mcelog.h
===================================================================
--- mcelog-1.0.2010.03.10.orig/mcelog.h
+++ mcelog-1.0.2010.03.10/mcelog.h
@@ -120,6 +120,7 @@ enum cputype {
 enum option_ranges {
 	O_COMMON = 500,
 	O_DISKDB = 1000,
+	O_EMAIL  = 1500,
 };
 
 enum syslog_opt { 
