| #define _GNU_SOURCE 1 |
| #include <stdlib.h> |
| #include <syslog.h> |
| #include <stdarg.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include "mcelog.h" |
| #include "msg.h" |
| #include "memutil.h" |
| |
| enum syslog_opt syslog_opt = SYSLOG_REMARK; |
| int syslog_level = LOG_WARNING; |
| static FILE *output_fh; |
| static char *output_fn; |
| |
| int need_stdout(void) |
| { |
| return !output_fh && (syslog_opt == 0); |
| } |
| |
| int open_logfile(char *fn) |
| { |
| output_fh = fopen(fn, "a"); |
| if (output_fh) { |
| char *old = output_fn; |
| output_fn = xstrdup(fn); |
| free(old); |
| return 0; |
| } |
| return -1; |
| } |
| |
| static void opensyslog(void) |
| { |
| static int syslog_opened; |
| if (syslog_opened) |
| return; |
| syslog_opened = 1; |
| openlog("mcelog", 0, 0); |
| } |
| |
| /* For warning messages that should reach syslog */ |
| void Lprintf(char *fmt, ...) |
| { |
| va_list ap; |
| if (syslog_opt & SYSLOG_REMARK) { |
| va_start(ap, fmt); |
| opensyslog(); |
| vsyslog(LOG_ERR, fmt, ap); |
| va_end(ap); |
| } |
| if (output_fh || !(syslog_opt & SYSLOG_REMARK)) { |
| va_start(ap, fmt); |
| opensyslog(); |
| vfprintf(output_fh ? output_fh : stdout, fmt, ap); |
| va_end(ap); |
| } |
| } |
| |
| /* For errors during operation */ |
| void Eprintf(char *fmt, ...) |
| { |
| FILE *f = output_fh ? output_fh : stderr; |
| va_list ap; |
| |
| if (!(syslog_opt & SYSLOG_ERROR) || output_fh) { |
| va_start(ap, fmt); |
| fputs("mcelog: ", f); |
| vfprintf(f, fmt, ap); |
| if (*fmt && fmt[strlen(fmt)-1] != '\n') |
| fputc('\n', f); |
| va_end(ap); |
| } |
| if (syslog_opt & SYSLOG_ERROR) { |
| va_start(ap, fmt); |
| opensyslog(); |
| vsyslog(LOG_ERR, fmt, ap); |
| va_end(ap); |
| } |
| } |
| |
| void SYSERRprintf(char *fmt, ...) |
| { |
| char *err = strerror(errno); |
| va_list ap; |
| FILE *f = output_fh ? output_fh : stderr; |
| |
| if (!(syslog_opt & SYSLOG_ERROR) || output_fh) { |
| va_start(ap, fmt); |
| fputs("mcelog: ", f); |
| vfprintf(f, fmt, ap); |
| fprintf(f, ": %s\n", err); |
| va_end(ap); |
| } |
| if (syslog_opt & SYSLOG_ERROR) { |
| char *fmt2; |
| va_start(ap, fmt); |
| opensyslog(); |
| xasprintf(&fmt2, "%s: %s\n", fmt, err); |
| vsyslog(LOG_ERR, fmt2, ap); |
| free(fmt2); |
| va_end(ap); |
| } |
| } |
| |
| /* Write to syslog with line buffering */ |
| static int vlinesyslog(char *fmt, va_list ap) |
| { |
| static char line[200]; |
| int n; |
| int lend = strlen(line); |
| int w = vsnprintf(line + lend, sizeof(line)-lend, fmt, ap); |
| while (line[n = strcspn(line, "\n")] != 0) { |
| line[n] = 0; |
| syslog(syslog_level, "%s", line); |
| memmove(line, line + n + 1, strlen(line + n + 1) + 1); |
| } |
| return w; |
| } |
| |
| /* For decoded machine check output */ |
| int Wprintf(char *fmt, ...) |
| { |
| int n = 0; |
| va_list ap; |
| if (syslog_opt & SYSLOG_LOG) { |
| va_start(ap,fmt); |
| opensyslog(); |
| n = vlinesyslog(fmt, ap); |
| va_end(ap); |
| } |
| if (!(syslog_opt & SYSLOG_LOG) || output_fh) { |
| va_start(ap,fmt); |
| n = vfprintf(output_fh ? output_fh : stdout, fmt, ap); |
| va_end(ap); |
| } |
| return n; |
| } |
| |
| /* For output that should reach both syslog and normal log */ |
| void Gprintf(char *fmt, ...) |
| { |
| va_list ap; |
| if (syslog_opt & (SYSLOG_REMARK|SYSLOG_LOG)) { |
| va_start(ap,fmt); |
| vlinesyslog(fmt, ap); |
| va_end(ap); |
| } |
| if (!(syslog_opt & SYSLOG_LOG) || output_fh) { |
| va_start(ap,fmt); |
| vfprintf(output_fh ? output_fh : stdout, fmt, ap); |
| va_end(ap); |
| } |
| } |
| |
| void flushlog(void) |
| { |
| FILE *f = output_fh ? output_fh : stdout; |
| fflush(f); |
| } |
| |
| void reopenlog(void) |
| { |
| if (output_fn && output_fh) { |
| fclose(output_fh); |
| output_fh = NULL; |
| if (open_logfile(output_fn) < 0) |
| SYSERRprintf("Cannot reopen logfile `%s'", output_fn); |
| } |
| } |