511 lines
13 KiB
Diff
511 lines
13 KiB
Diff
---
|
|
Makefile | 13 +++-
|
|
email.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
email.h | 34 ++++++++++
|
|
mcelog.c | 93 ++++++++++++++++++++++++++++-
|
|
mcelog.h | 1
|
|
msg.c | 8 ++
|
|
6 files changed, 346 insertions(+), 3 deletions(-)
|
|
|
|
Index: mcelog-195/Makefile
|
|
===================================================================
|
|
--- mcelog-195.orig/Makefile
|
|
+++ mcelog-195/Makefile
|
|
@@ -1,3 +1,4 @@
|
|
+CONFIG_EMAIL := 1
|
|
CFLAGS := -g -Os
|
|
prefix := /usr
|
|
etcprefix :=
|
|
@@ -38,16 +39,24 @@ OBJ := p4.o k8.o mcelog.o dmi.o tsc.o co
|
|
broadwell_de.o broadwell_epex.o skylake_xeon.o \
|
|
denverton.o i10nm.o sapphire.o granite.o \
|
|
msr.o bus.o unknown.o lookup_intel_cputype.o
|
|
+EMAIL_OBJ := email.o
|
|
CLEAN := mcelog dmi tsc dbquery .depend .depend.X dbquery.o \
|
|
version.o version.c version.tmp cputype.h cputype.tmp \
|
|
- lookup_intel_cputype.c lookup_intel_cputype.tmp
|
|
+ lookup_intel_cputype.c lookup_intel_cputype.tmp ${EMAIL_OBJ}
|
|
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: install-nodoc mcelog.conf.5 mcelog.triggers.5
|
|
@@ -85,7 +94,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-195/email.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ mcelog-195/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-195/email.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ mcelog-195/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-195/mcelog.c
|
|
===================================================================
|
|
--- mcelog-195.orig/mcelog.c
|
|
+++ mcelog-195/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;
|
|
@@ -906,6 +910,7 @@ void usage(void)
|
|
"--max-corr-err-counters Max page correctable error counters\n"
|
|
"--help Display this message.\n"
|
|
);
|
|
+ email_usage();
|
|
printf("\n");
|
|
print_cputypes();
|
|
}
|
|
@@ -977,6 +982,7 @@ static struct option options[] = {
|
|
{ "max-corr-err-counters", 1, NULL, O_MAX_CORR_ERR_COUNTERS },
|
|
{ "help", 0, NULL, O_HELP },
|
|
{ "is-cpu-supported", 0, NULL, O_IS_CPU_SUPPORTED },
|
|
+ EMAIL_OPTIONS
|
|
{}
|
|
};
|
|
|
|
@@ -1171,11 +1177,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");
|
|
@@ -1202,12 +1283,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();
|
|
}
|
|
|
|
@@ -1321,6 +1406,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;
|
|
}
|
|
@@ -1355,6 +1442,10 @@ int main(int ac, char **av)
|
|
usage();
|
|
exit(1);
|
|
}
|
|
+ if (email_mode == 0)
|
|
+ email_mode = email_env();
|
|
+ /* email sending only in daemon mode */
|
|
+ email_mode &= daemon_mode;
|
|
checkdmi();
|
|
general_setup();
|
|
|
|
Index: mcelog-195/mcelog.h
|
|
===================================================================
|
|
--- mcelog-195.orig/mcelog.h
|
|
+++ mcelog-195/mcelog.h
|
|
@@ -118,6 +118,7 @@ extern int open_logfile(char *fn);
|
|
enum option_ranges {
|
|
O_COMMON = 500,
|
|
O_DISKDB = 1000,
|
|
+ O_EMAIL = 1500,
|
|
};
|
|
|
|
enum syslog_opt {
|
|
Index: mcelog-195/msg.c
|
|
===================================================================
|
|
--- mcelog-195.orig/msg.c
|
|
+++ mcelog-195/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;
|
|
}
|
|
|