File email.patch of Package mcelog.13082

---
 Makefile |   10 ++-
 email.c  |  199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 email.h  |   34 ++++++++++
 mcelog.c |   93 +++++++++++++++++++++++++++++
 mcelog.h |    1 
 msg.c    |    8 ++
 6 files changed, 343 insertions(+), 2 deletions(-)

Index: mcelog-1.53/Makefile
===================================================================
--- mcelog-1.53.orig/Makefile
+++ mcelog-1.53/Makefile
@@ -1,3 +1,4 @@
+CONFIG_EMAIL := 1
 CFLAGS := -g -Os
 prefix := /usr
 etcprefix :=
@@ -35,15 +36,23 @@ OBJ := p4.o k8.o mcelog.o dmi.o tsc.o co
        broadwell_de.o broadwell_epex.o skylake_xeon.o		 \
        denverton.o						 \
        msr.o bus.o unknown.o
+EMAIL_OBJ := email.o
 CLEAN := mcelog dmi tsc dbquery .depend .depend.X dbquery.o \
-	version.o version.c version.tmp
+	 ${EMAIL_OBJ} version.o version.c version.tmp
 DOC := mce.pdf
 
 ADD_DEFINES :=
 
+ifdef CONFIG_EMAIL
+ADD_DEFINES := -DCONFIG_EMAIL=1
+LIBS := -lesmtp
+OBJ += ${EMAIL_OBJ}
+endif
+
 SRC := $(OBJ:.o=.c)
 
 mcelog: ${OBJ} version.o
+	$(CC) $(LDFLAGS) $^ ${LIBS} -o $@
 
 # dbquery intentionally not installed by default
 install: mcelog mcelog.conf mcelog.conf.5 mcelog.triggers.5
@@ -78,7 +87,7 @@ dbquery: db.o dbquery.o memutil.o
 depend: .depend
 
 %.o: %.c
-	$(CC) -c $(CFLAGS) $(CPPFLAGS) $(WARNINGS) $(ADD_DEFINES) -o $@ $<
+	$(CC) -c $(CFLAGS) $(CPPFLAGS) $(WARNINGS) $(ADD_DEFINES) $< -o $@
 
 version.tmp: FORCE
 	( printf "char version[] = \"" ; 			\
Index: mcelog-1.53/email.c
===================================================================
--- /dev/null
+++ mcelog-1.53/email.c
@@ -0,0 +1,200 @@
+#include <unistd.h>
+#include <signal.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define __USE_GNU
+/* To fetch the dnsname */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.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 dnsname[MAX_STRING_LEN];
+
+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;
+}
+
+int email_env(void)
+{
+	char *email_env = getenv("MCELOG_EMAIL_DEBUG");
+
+	if (email_env)
+		debug=0;
+
+	email_env = getenv("MCELOG_ADMIN_EMAIL");
+	/* No email validation, but at least check for not being empty... */
+	if (email_env && strlen(email_env) > 1) {
+		strncpy(c_recipient, email_env, MAX_STRING_LEN - 1);
+		return 1;
+	}
+	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];
+	struct addrinfo hints;
+	struct addrinfo *res=NULL;
+	int    ret, retry=3;
+
+	/* Taken from net-tools hostname.c showhname() */
+	memset(&hints, 0, sizeof(struct addrinfo));
+	hints.ai_family   = AF_UNSPEC;
+	hints.ai_flags    = AI_CANONNAME | AI_CANONIDN;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_protocol = 0;
+
+	if (gethostname(host, MAX_STRING_LEN)) {
+		fprintf(stderr, "Cannot get host name\n");
+		return;
+	}
+
+	do {
+		ret = getaddrinfo(host, NULL, &hints, &res);
+	} while(ret == EAI_AGAIN && retry-- > 0
+                             && usleep(50000) == 0);
+
+	if (ret != 0 || res == NULL) {
+		fprintf(stderr, "Could not retrieve hostname\n");
+		return;
+	}
+
+	memset(dnsname, '\0', MAX_STRING_LEN);
+	strncpy(dnsname, res->ai_canonname, MAX_STRING_LEN - 1);
+
+	fprintf(fp, "Return-Path: <dummy@will_get_overridden.net>\r\n"
+		"Subject: Machine Check Exception on %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", dnsname);
+	freeaddrinfo(res);
+}
+
+
+int send_mail(FILE *fp)
+{
+	char smtp_host[MAX_STRING_LEN] = "localhost:25";
+	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);
+
+	snprintf(from, MAX_STRING_LEN, "root@%s", dnsname);
+
+	/* 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.53/email.h
===================================================================
--- /dev/null
+++ mcelog-1.53/email.h
@@ -0,0 +1,34 @@
+#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);
+extern int email_env(void);
+
+#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; }
+static int email_env(void) { return 0; }
+#define EMAIL_OPTIONS
+#endif
+
+#endif
Index: mcelog-1.53/mcelog.c
===================================================================
--- mcelog-1.53.orig/mcelog.c
+++ mcelog-1.53/mcelog.c
@@ -37,6 +37,7 @@
 #include <assert.h>
 #include <signal.h>
 #include <pwd.h>
+#include <sys/wait.h>
 #include <fnmatch.h>
 #include "mcelog.h"
 #include "paths.h"
@@ -60,6 +61,9 @@
 #include "bus.h"
 #include "unknown.h"
 
+#include "email.h"
+int email_mode;
+
 enum cputype cputype = CPU_GENERIC;	
 
 char *logfn = LOG_DEV_FILENAME; 
@@ -71,7 +75,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;
@@ -983,6 +987,7 @@ void usage(void)
 "--no-imc-log	     Disable extended iMC logging\n"
 "--is-cpu-supported  Exit with return code indicating whether the CPU is supported\n"
 		);
+	email_usage();
 	printf("\n");
 	print_cputypes();
 	exit(1);
@@ -1051,6 +1056,7 @@ static struct option options[] = {
 	{ "debug-numerrors", 0, NULL, O_DEBUG_NUMERRORS }, /* undocumented: for testing */
 	{ "no-imc-log", 0, NULL, O_NO_IMC_LOG },
 	{ "is-cpu-supported", 0, NULL, O_IS_CPU_SUPPORTED },
+	EMAIL_OPTIONS
 	{}
 };
 
@@ -1229,11 +1235,86 @@ static void drop_cred(void)
 	}
 }
 
+#ifdef CONFIG_EMAIL
+pid_t c_pid;
+
+/* 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 const char *mail_thread = "mcelog_mail_thread";
+
+
+static int setup_email(struct mce *m) {
+	int pdes[2];
+	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++;
+
+	ret = pipe(pdes);
+	if (ret)
+		return ret;
+
+	c_pid = mcelog_fork(mail_thread);
+	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");
+		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, count;
 	int finish = 0, flags;
+	int mail_setup = 0;
 
 	if (recordlen == 0) {
 		Wprintf("no data in mce record\n");
@@ -1260,12 +1341,16 @@ static void process(int fd, unsigned rec
 			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();
 	}
 
@@ -1374,6 +1459,8 @@ int main(int ac, char **av)
 			noargs(ac, av);
 			fprintf(stderr, "mcelog %s\n", MCELOG_VERSION);
 			exit(0);
+		} else if (email_cmd(opt, ac, av)) {
+			email_mode = 1;
 		} else if (opt == 0)
 			break;		    
 	} 
@@ -1406,6 +1493,10 @@ int main(int ac, char **av)
 		logfn = av[optind++];
 	if (av[optind])
 		usage();
+	if (email_mode == 0)
+		email_mode = email_env();
+	/* email sending only in daemon mode */
+	email_mode &= daemon_mode;
 	checkdmi();
 	general_setup();
 		
Index: mcelog-1.53/mcelog.h
===================================================================
--- mcelog-1.53.orig/mcelog.h
+++ mcelog-1.53/mcelog.h
@@ -141,6 +141,7 @@ enum cputype {
 enum option_ranges {
 	O_COMMON = 500,
 	O_DISKDB = 1000,
+	O_EMAIL  = 1500,
 };
 
 enum syslog_opt { 
Index: mcelog-1.53/msg.c
===================================================================
--- mcelog-1.53.orig/msg.c
+++ mcelog-1.53/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;
 }