Changeset View
Standalone View
usr.sbin/syslogd/syslogd.c
Show All 22 Lines | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
*/ | */ | ||||
/*- | |||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | |||||
* | |||||
* Copyright (c) 2018 Prodrive Technologies, https://prodrive-technologies.com/ | |||||
* Author: Ed Schouten <ed@FreeBSD.org> | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
*/ | |||||
#ifndef lint | #ifndef lint | ||||
static const char copyright[] = | static const char copyright[] = | ||||
"@(#) Copyright (c) 1983, 1988, 1993, 1994\n\ | "@(#) Copyright (c) 1983, 1988, 1993, 1994\n\ | ||||
The Regents of the University of California. All rights reserved.\n"; | The Regents of the University of California. All rights reserved.\n"; | ||||
#endif /* not lint */ | #endif /* not lint */ | ||||
#ifndef lint | #ifndef lint | ||||
Show All 27 Lines | |||||
* more extensive changes by Eric Allman (again) | * more extensive changes by Eric Allman (again) | ||||
* Extension to log by program name as well as facility and priority | * Extension to log by program name as well as facility and priority | ||||
* by Peter da Silva. | * by Peter da Silva. | ||||
* -u and -v by Harlan Stenn. | * -u and -v by Harlan Stenn. | ||||
* Priority comparison code by Harlan Stenn. | * Priority comparison code by Harlan Stenn. | ||||
*/ | */ | ||||
/* Maximum number of characters in time of last occurrence */ | /* Maximum number of characters in time of last occurrence */ | ||||
#define MAXDATELEN 16 | #define MAXLINE 2048 /* maximum line length */ | ||||
#define MAXLINE 1024 /* maximum line length */ | |||||
#define MAXSVLINE MAXLINE /* maximum saved line length */ | #define MAXSVLINE MAXLINE /* maximum saved line length */ | ||||
#define DEFUPRI (LOG_USER|LOG_NOTICE) | #define DEFUPRI (LOG_USER|LOG_NOTICE) | ||||
#define DEFSPRI (LOG_KERN|LOG_CRIT) | #define DEFSPRI (LOG_KERN|LOG_CRIT) | ||||
#define TIMERINTVL 30 /* interval for checking flush, mark */ | #define TIMERINTVL 30 /* interval for checking flush, mark */ | ||||
#define TTYMSGTIME 1 /* timeout passed to ttymsg */ | #define TTYMSGTIME 1 /* timeout passed to ttymsg */ | ||||
#define RCVBUF_MINSIZE (80 * 1024) /* minimum size of dgram rcv buffer */ | #define RCVBUF_MINSIZE (80 * 1024) /* minimum size of dgram rcv buffer */ | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Flags to logmsg(). | * Flags to logmsg(). | ||||
*/ | */ | ||||
#define IGN_CONS 0x001 /* don't print on console */ | #define IGN_CONS 0x001 /* don't print on console */ | ||||
#define SYNC_FILE 0x002 /* do fsync on file after printing */ | #define SYNC_FILE 0x002 /* do fsync on file after printing */ | ||||
#define MARK 0x008 /* this message is a mark */ | #define MARK 0x008 /* this message is a mark */ | ||||
#define ISKERNEL 0x010 /* kernel generated message */ | |||||
/* Timestamps of log entries. */ | |||||
struct logtime { | |||||
struct tm tm; | |||||
suseconds_t usec; | |||||
}; | |||||
/* Traditional syslog timestamp format. */ | |||||
#define RFC3164_DATELEN 15 | |||||
#define RFC3164_DATEFMT "%b %e %H:%M:%S" | |||||
/* | /* | ||||
* This structure represents the files that will have log | * This structure represents the files that will have log | ||||
* copies printed. | * copies printed. | ||||
* We require f_file to be valid if f_type is F_FILE, F_CONSOLE, F_TTY | * We require f_file to be valid if f_type is F_FILE, F_CONSOLE, F_TTY | ||||
* or if f_type is F_PIPE and f_pid > 0. | * or if f_type is F_PIPE and f_pid > 0. | ||||
*/ | */ | ||||
struct filed { | struct filed { | ||||
Show All 23 Lines | #define PRI_GT 0x4 | ||||
} f_un; | } f_un; | ||||
#define fu_uname f_un.f_uname | #define fu_uname f_un.f_uname | ||||
#define fu_forw_hname f_un.f_forw.f_hname | #define fu_forw_hname f_un.f_forw.f_hname | ||||
#define fu_forw_addr f_un.f_forw.f_addr | #define fu_forw_addr f_un.f_forw.f_addr | ||||
#define fu_fname f_un.f_fname | #define fu_fname f_un.f_fname | ||||
#define fu_pipe_pname f_un.f_pipe.f_pname | #define fu_pipe_pname f_un.f_pipe.f_pname | ||||
#define fu_pipe_pid f_un.f_pipe.f_pid | #define fu_pipe_pid f_un.f_pipe.f_pid | ||||
char f_prevline[MAXSVLINE]; /* last message logged */ | char f_prevline[MAXSVLINE]; /* last message logged */ | ||||
char f_lasttime[MAXDATELEN]; /* time of last occurrence */ | struct logtime f_lasttime; /* time of last occurrence */ | ||||
char f_prevhost[MAXHOSTNAMELEN]; /* host from which recd. */ | char f_prevhost[MAXHOSTNAMELEN]; /* host from which recd. */ | ||||
int f_prevpri; /* pri of f_prevline */ | int f_prevpri; /* pri of f_prevline */ | ||||
int f_prevlen; /* length of f_prevline */ | size_t f_prevlen; /* length of f_prevline */ | ||||
int f_prevcount; /* repetition cnt of prevline */ | int f_prevcount; /* repetition cnt of prevline */ | ||||
u_int f_repeatcount; /* number of "repeated" msgs */ | u_int f_repeatcount; /* number of "repeated" msgs */ | ||||
int f_flags; /* file-specific flags */ | int f_flags; /* file-specific flags */ | ||||
#define FFLAG_SYNC 0x01 | #define FFLAG_SYNC 0x01 | ||||
#define FFLAG_NEEDSYNC 0x02 | #define FFLAG_NEEDSYNC 0x02 | ||||
}; | }; | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 117 Lines • ▼ Show 20 Lines | |||||
static int decode(const char *, const CODE *); | static int decode(const char *, const CODE *); | ||||
static void die(int) __dead2; | static void die(int) __dead2; | ||||
static void dodie(int); | static void dodie(int); | ||||
static void dofsync(void); | static void dofsync(void); | ||||
static void domark(int); | static void domark(int); | ||||
static void fprintlog(struct filed *, int, const char *); | static void fprintlog(struct filed *, int, const char *); | ||||
static void init(int); | static void init(int); | ||||
static void logerror(const char *); | static void logerror(const char *); | ||||
static void logmsg(int, const char *, const char *, const char *, int); | static void logmsg(int, const struct logtime *, const char *, const char *, | ||||
const char *, const char *, const char *, const char *, int); | |||||
static void log_deadchild(pid_t, int, const char *); | static void log_deadchild(pid_t, int, const char *); | ||||
static void markit(void); | static void markit(void); | ||||
static int socksetup(struct peer *); | static int socksetup(struct peer *); | ||||
static int socklist_recv_file(struct socklist *); | static int socklist_recv_file(struct socklist *); | ||||
static int socklist_recv_sock(struct socklist *); | static int socklist_recv_sock(struct socklist *); | ||||
static int socklist_recv_signal(struct socklist *); | static int socklist_recv_signal(struct socklist *); | ||||
static void sighandler(int); | static void sighandler(int); | ||||
static int skip_message(const char *, const char *, int); | static int skip_message(const char *, const char *, int); | ||||
▲ Show 20 Lines • Show All 483 Lines • ▼ Show 20 Lines | fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", | ||||
" [-b bind_address] [-f config_file]", | " [-b bind_address] [-f config_file]", | ||||
" [-l [mode:]path] [-m mark_interval]", | " [-l [mode:]path] [-m mark_interval]", | ||||
" [-P pid_file] [-p log_socket]", | " [-P pid_file] [-p log_socket]", | ||||
" [-S logpriv_socket]"); | " [-S logpriv_socket]"); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
/* | /* | ||||
* Take a raw input line, extract PRI, TIMESTAMP and HOSTNAME from the message, | * Removes characters from log messages that are unsafe to display. | ||||
* and print the message on the appropriate log files. | * TODO: Permit UTF-8 strings that include a BOM per RFC 5424? | ||||
*/ | */ | ||||
static void | static void | ||||
parsemsg(const char *from, char *msg) | parsemsg_remove_unsafe_characters(const char *in, char *out, size_t outlen) | ||||
{ | { | ||||
const char *timestamp; | |||||
char *q; | char *q; | ||||
long n; | int c; | ||||
int i, c, pri, msglen; | |||||
char line[MAXLINE + 1]; | |||||
/* Parse PRI. */ | q = out; | ||||
if (msg[0] != '<' || !isdigit(msg[1])) { | while ((c = (unsigned char)*in++) != '\0' && q < out + outlen - 4) { | ||||
dprintf("Invalid PRI from %s\n", from); | if (mask_C1 && (c & 0x80) && c < 0xA0) { | ||||
return; | c &= 0x7F; | ||||
*q++ = 'M'; | |||||
*q++ = '-'; | |||||
} | } | ||||
for (i = 2; i <= 4; i++) { | if (isascii(c) && iscntrl(c)) { | ||||
if (msg[i] == '>') | if (c == '\n') { | ||||
*q++ = ' '; | |||||
} else if (c == '\t') { | |||||
*q++ = '\t'; | |||||
} else { | |||||
*q++ = '^'; | |||||
*q++ = c ^ 0100; | |||||
} | |||||
} else { | |||||
*q++ = c; | |||||
} | |||||
} | |||||
*q = '\0'; | |||||
} | |||||
/* | |||||
* Parses a syslog message according to RFC 5424, assuming that PRI and | |||||
* VERSION (i.e., "<%d>1 ") have already been parsed by parsemsg(). The | |||||
* parsed result is passed to logmsg(). | |||||
*/ | |||||
static void | |||||
parsemsg_rfc5424(const char *from, int pri, char *msg) | |||||
{ | |||||
const struct logtime *timestamp; | |||||
struct logtime timestamp_remote = { 0 }; | |||||
const char *omsg, *hostname, *app_name, *procid, *msgid, | |||||
*structured_data; | |||||
char line[MAXLINE + 1]; | |||||
#define FAIL_IF(field, expr) do { \ | |||||
if (expr) { \ | |||||
dprintf("Failed to parse " field " from %s: %s\n", \ | |||||
from, omsg); \ | |||||
return; \ | |||||
} \ | |||||
} while (0) | |||||
#define PARSE_CHAR(field, sep) do { \ | |||||
FAIL_IF(field, *msg != sep); \ | |||||
++msg; \ | |||||
} while (0) | |||||
#define IF_NOT_NILVALUE(var) \ | |||||
if (msg[0] == '-' && msg[1] == ' ') { \ | |||||
msg += 2; \ | |||||
var = NULL; \ | |||||
} else if (msg[0] == '-' && msg[1] == '\0') { \ | |||||
++msg; \ | |||||
var = NULL; \ | |||||
} else | |||||
omsg = msg; | |||||
IF_NOT_NILVALUE(timestamp) { | |||||
/* Parse RFC 3339-like timestamp. */ | |||||
#define PARSE_NUMBER(dest, length, min, max) do { \ | |||||
int i, v; \ | |||||
\ | |||||
v = 0; \ | |||||
for (i = 0; i < length; ++i) { \ | |||||
FAIL_IF("TIMESTAMP", *msg < '0' || *msg > '9'); \ | |||||
v = v * 10 + *msg++ - '0'; \ | |||||
} \ | |||||
FAIL_IF("TIMESTAMP", v < min || v > max); \ | |||||
dest = v; \ | |||||
} while (0) | |||||
/* Date and time. */ | |||||
PARSE_NUMBER(timestamp_remote.tm.tm_year, 4, 0, 9999); | |||||
timestamp_remote.tm.tm_year -= 1900; | |||||
PARSE_CHAR("TIMESTAMP", '-'); | |||||
PARSE_NUMBER(timestamp_remote.tm.tm_mon, 2, 1, 12); | |||||
--timestamp_remote.tm.tm_mon; | |||||
PARSE_CHAR("TIMESTAMP", '-'); | |||||
PARSE_NUMBER(timestamp_remote.tm.tm_mday, 2, 1, 31); | |||||
PARSE_CHAR("TIMESTAMP", 'T'); | |||||
PARSE_NUMBER(timestamp_remote.tm.tm_hour, 2, 0, 23); | |||||
PARSE_CHAR("TIMESTAMP", ':'); | |||||
PARSE_NUMBER(timestamp_remote.tm.tm_min, 2, 0, 59); | |||||
PARSE_CHAR("TIMESTAMP", ':'); | |||||
PARSE_NUMBER(timestamp_remote.tm.tm_sec, 2, 0, 59); | |||||
/* Perform normalization. */ | |||||
timegm(×tamp_remote.tm); | |||||
/* Optional: fractional seconds. */ | |||||
if (msg[0] == '.' && msg[1] >= '0' && msg[1] <= '9') { | |||||
int i; | |||||
++msg; | |||||
for (i = 100000; i != 0; i /= 10) { | |||||
if (*msg < '0' || *msg > '9') | |||||
break; | break; | ||||
if (!isdigit(msg[i])) { | timestamp_remote.usec += (*msg++ - '0') * i; | ||||
dprintf("Invalid PRI header from %s\n", from); | |||||
return; | |||||
} | } | ||||
} | } | ||||
if (msg[i] != '>') { | /* Timezone. */ | ||||
dprintf("Invalid PRI header from %s\n", from); | if (*msg == 'Z') { | ||||
return; | /* UTC. */ | ||||
++msg; | |||||
} else { | |||||
int sign, tz_hour, tz_min; | |||||
/* Local time zone offset. */ | |||||
FAIL_IF("TIMESTAMP", *msg != '-' && *msg != '+'); | |||||
sign = *msg++ == '-' ? -1 : 1; | |||||
PARSE_NUMBER(tz_hour, 2, 0, 23); | |||||
PARSE_CHAR("TIMESTAMP", ':'); | |||||
PARSE_NUMBER(tz_min, 2, 0, 59); | |||||
timestamp_remote.tm.tm_gmtoff = | |||||
sign * (tz_hour * 3600 + tz_min * 60); | |||||
} | } | ||||
errno = 0; | #undef PARSE_NUMBER | ||||
n = strtol(msg + 1, &q, 10); | PARSE_CHAR("TIMESTAMP", ' '); | ||||
if (errno != 0 || *q != msg[i] || n < 0 || n >= INT_MAX) { | timestamp = ×tamp_remote; | ||||
dprintf("Invalid PRI %ld from %s: %s\n", | |||||
n, from, strerror(errno)); | |||||
return; | |||||
} | } | ||||
woodsb02: Probably worth adding an “else timestamp = NULL” to specifically initialise timestamp, and for… | |||||
Not Done Inline ActionsIndeed. Fixed! ed: Indeed. Fixed! | |||||
pri = n; | |||||
if (pri &~ (LOG_FACMASK|LOG_PRIMASK)) | |||||
pri = DEFUPRI; | |||||
/* String fields part of the HEADER. */ | |||||
#define PARSE_STRING(field, var) \ | |||||
IF_NOT_NILVALUE(var) { \ | |||||
var = msg; \ | |||||
while (*msg >= '!' && *msg <= '~') \ | |||||
++msg; \ | |||||
FAIL_IF(field, var == msg); \ | |||||
PARSE_CHAR(field, ' '); \ | |||||
msg[-1] = '\0'; \ | |||||
} | |||||
PARSE_STRING("HOSTNAME", hostname); | |||||
PARSE_STRING("APP-NAME", app_name); | |||||
PARSE_STRING("PROCID", procid); | |||||
PARSE_STRING("MSGID", msgid); | |||||
#undef PARSE_STRING | |||||
/* Structured data. */ | |||||
#define PARSE_SD_NAME() do { \ | |||||
const char *start; \ | |||||
\ | |||||
start = msg; \ | |||||
while (*msg >= '!' && *msg <= '~' && *msg != '=' && \ | |||||
*msg != ']' && *msg != '"') \ | |||||
++msg; \ | |||||
FAIL_IF("STRUCTURED-NAME", start == msg); \ | |||||
} while (0) | |||||
IF_NOT_NILVALUE(structured_data) { | |||||
/* SD-ELEMENT. */ | |||||
while (*msg == '[') { | |||||
++msg; | |||||
/* SD-ID. */ | |||||
PARSE_SD_NAME(); | |||||
/* SD-PARAM. */ | |||||
while (*msg == ' ') { | |||||
++msg; | |||||
/* PARAM-NAME. */ | |||||
PARSE_SD_NAME(); | |||||
PARSE_CHAR("STRUCTURED-NAME", '='); | |||||
PARSE_CHAR("STRUCTURED-NAME", '"'); | |||||
while (*msg != '"') { | |||||
FAIL_IF("STRUCTURED-NAME", | |||||
*msg == '\0'); | |||||
if (*msg++ == '\\') { | |||||
FAIL_IF("STRUCTURED-NAME", | |||||
*msg == '\0'); | |||||
++msg; | |||||
} | |||||
} | |||||
++msg; | |||||
} | |||||
PARSE_CHAR("STRUCTURED-NAME", ']'); | |||||
} | |||||
PARSE_CHAR("STRUCTURED-NAME", ' '); | |||||
msg[-1] = '\0'; | |||||
} | |||||
#undef PARSE_SD_NAME | |||||
#undef FAIL_IF | |||||
#undef PARSE_CHAR | |||||
#undef IF_NOT_NILVALUE | |||||
parsemsg_remove_unsafe_characters(msg, line, sizeof(line)); | |||||
logmsg(pri, timestamp, from, app_name, procid, msgid, | |||||
structured_data, line, 0); | |||||
} | |||||
/* | /* | ||||
* Don't allow users to log kernel messages. | * Trims the application name ("TAG" in RFC 3164 terminology) and | ||||
* NOTE: since LOG_KERN == 0 this will also match | * process ID from a message if present. | ||||
* messages with no facility specified. | |||||
*/ | */ | ||||
if ((pri & LOG_FACMASK) == LOG_KERN && !KeepKernFac) | static void | ||||
pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri)); | parsemsg_rfc3164_app_name_procid(char **msg, const char **app_name, | ||||
const char **procid) { | |||||
char *m, *app_name_begin, *procid_begin; | |||||
size_t app_name_length, procid_length; | |||||
m = *msg; | |||||
/* Application name. */ | |||||
app_name_begin = m; | |||||
app_name_length = strspn(m, | |||||
"abcdefghijklmnopqrstuvwxyz" | |||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |||||
"0123456789" | |||||
"_-"); | |||||
if (app_name_length == 0) | |||||
goto bad; | |||||
m += app_name_length; | |||||
/* Process identifier (optional). */ | |||||
if (*m == '[') { | |||||
procid_begin = ++m; | |||||
procid_length = strspn(m, "0123456789"); | |||||
if (procid_length == 0) | |||||
goto bad; | |||||
m += procid_length; | |||||
if (*m++ != ']') | |||||
goto bad; | |||||
} else { | |||||
procid_begin = NULL; | |||||
procid_length = 0; | |||||
} | |||||
/* Separator. */ | |||||
if (m[0] != ':' || m[1] != ' ') | |||||
goto bad; | |||||
/* Split strings from input. */ | |||||
app_name_begin[app_name_length] = '\0'; | |||||
if (procid_begin != 0) | |||||
procid_begin[procid_length] = '\0'; | |||||
*msg = m + 2; | |||||
*app_name = app_name_begin; | |||||
*procid = procid_begin; | |||||
return; | |||||
bad: | |||||
*app_name = NULL; | |||||
*procid = NULL; | |||||
} | |||||
/* | /* | ||||
* The TIMESTAMP field is the local time and is in the format of | * Parses a syslog message according to RFC 3164, assuming that PRI | ||||
* "Mmm dd hh:mm:ss" (without the quote marks). | * (i.e., "<%d>") has already been parsed by parsemsg(). The parsed | ||||
* A single space character MUST follow the TIMESTAMP field. | * result is passed to logmsg(). | ||||
* | |||||
* XXXGL: the check can be improved. | |||||
*/ | */ | ||||
msg += i + 1; | static void | ||||
msglen = strlen(msg); | parsemsg_rfc3164(const char *from, int pri, char *msg) | ||||
if (msglen < MAXDATELEN || msg[3] != ' ' || msg[6] != ' ' || | { | ||||
msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') { | struct tm tm_parsed; | ||||
dprintf("Invalid TIMESTAMP from %s: %s\n", from, msg); | const struct logtime *timestamp; | ||||
struct logtime timestamp_remote = { 0 }; | |||||
const char *app_name, *procid; | |||||
size_t i, msglen; | |||||
char line[MAXLINE + 1]; | |||||
/* Parse the timestamp provided by the remote side. */ | |||||
if (strptime(msg, RFC3164_DATEFMT, &tm_parsed) != | |||||
msg + RFC3164_DATELEN || msg[RFC3164_DATELEN] != ' ') { | |||||
dprintf("Failed to parse TIMESTAMP from %s: %s\n", from, msg); | |||||
return; | return; | ||||
} | } | ||||
msg += RFC3164_DATELEN + 1; | |||||
if (!RemoteAddDate) | if (!RemoteAddDate) { | ||||
timestamp = msg; | struct tm tm_now; | ||||
else | time_t t_now; | ||||
int year; | |||||
/* | |||||
* As the timestamp does not contain the year number, | |||||
* daylight saving time information, nor a time zone, | |||||
* attempt to infer it. Due to clock skews, the | |||||
* timestamp may even be part of the next year. Use the | |||||
* last year for which the timestamp is at most one week | |||||
* in the future. | |||||
* | |||||
* This loop can only run for at most three iterations | |||||
* before terminating. | |||||
*/ | |||||
t_now = time(NULL); | |||||
localtime_r(&t_now, &tm_now); | |||||
for (year = tm_now.tm_year + 1;; --year) { | |||||
timestamp_remote.tm = tm_parsed; | |||||
timestamp_remote.tm.tm_year = year; | |||||
timestamp_remote.tm.tm_isdst = -1; | |||||
if (mktime(×tamp_remote.tm) < | |||||
t_now + 7 * 24 * 60 * 60) | |||||
break; | |||||
} | |||||
timestamp = ×tamp_remote; | |||||
} else | |||||
timestamp = NULL; | timestamp = NULL; | ||||
msg += MAXDATELEN; | |||||
msglen -= MAXDATELEN; | |||||
/* | /* | ||||
* A single space character MUST also follow the HOSTNAME field. | * A single space character MUST also follow the HOSTNAME field. | ||||
*/ | */ | ||||
msglen = strlen(msg); | |||||
for (i = 0; i < MIN(MAXHOSTNAMELEN, msglen); i++) { | for (i = 0; i < MIN(MAXHOSTNAMELEN, msglen); i++) { | ||||
if (msg[i] == ' ') { | if (msg[i] == ' ') { | ||||
if (RemoteHostname) { | if (RemoteHostname) { | ||||
msg[i] = '\0'; | msg[i] = '\0'; | ||||
from = msg; | from = msg; | ||||
} | } | ||||
msg += i + 1; | msg += i + 1; | ||||
break; | break; | ||||
} | } | ||||
/* | /* | ||||
* Support non RFC compliant messages, without hostname. | * Support non RFC compliant messages, without hostname. | ||||
*/ | */ | ||||
if (msg[i] == ':') | if (msg[i] == ':') | ||||
break; | break; | ||||
} | } | ||||
if (i == MIN(MAXHOSTNAMELEN, msglen)) { | if (i == MIN(MAXHOSTNAMELEN, msglen)) { | ||||
dprintf("Invalid HOSTNAME from %s: %s\n", from, msg); | dprintf("Invalid HOSTNAME from %s: %s\n", from, msg); | ||||
return; | return; | ||||
} | } | ||||
q = line; | /* Remove the TAG, if present. */ | ||||
while ((c = (unsigned char)*msg++) != '\0' && | parsemsg_rfc3164_app_name_procid(&msg, &app_name, &procid); | ||||
q < &line[sizeof(line) - 4]) { | parsemsg_remove_unsafe_characters(msg, line, sizeof(line)); | ||||
if (mask_C1 && (c & 0x80) && c < 0xA0) { | logmsg(pri, timestamp, from, app_name, procid, NULL, NULL, line, 0); | ||||
c &= 0x7F; | |||||
*q++ = 'M'; | |||||
*q++ = '-'; | |||||
} | } | ||||
if (isascii(c) && iscntrl(c)) { | |||||
if (c == '\n') { | /* | ||||
*q++ = ' '; | * Takes a raw input line, extracts PRI and determines whether the | ||||
} else if (c == '\t') { | * message is formatted according to RFC 3164 or RFC 5424. Continues | ||||
*q++ = '\t'; | * parsing of addition fields in the message according to those | ||||
} else { | * standards and prints the message on the appropriate log files. | ||||
*q++ = '^'; | */ | ||||
*q++ = c ^ 0100; | static void | ||||
parsemsg(const char *from, char *msg) | |||||
{ | |||||
char *q; | |||||
long n; | |||||
size_t i; | |||||
int pri; | |||||
/* Parse PRI. */ | |||||
if (msg[0] != '<' || !isdigit(msg[1])) { | |||||
dprintf("Invalid PRI from %s\n", from); | |||||
return; | |||||
} | } | ||||
} else { | for (i = 2; i <= 4; i++) { | ||||
*q++ = c; | if (msg[i] == '>') | ||||
break; | |||||
if (!isdigit(msg[i])) { | |||||
dprintf("Invalid PRI header from %s\n", from); | |||||
return; | |||||
} | } | ||||
} | } | ||||
*q = '\0'; | if (msg[i] != '>') { | ||||
dprintf("Invalid PRI header from %s\n", from); | |||||
Done Inline ActionsPerhaps a comment should added to describe the functionality of this function, including describing that the PRI and VERSION have already been handled by parsemsg() and the actual log printing is handled by logmsg() woodsb02: Perhaps a comment should added to describe the functionality of this function, including… | |||||
return; | |||||
} | |||||
errno = 0; | |||||
n = strtol(msg + 1, &q, 10); | |||||
if (errno != 0 || *q != msg[i] || n < 0 || n >= INT_MAX) { | |||||
dprintf("Invalid PRI %ld from %s: %s\n", | |||||
n, from, strerror(errno)); | |||||
return; | |||||
} | |||||
pri = n; | |||||
if (pri &~ (LOG_FACMASK|LOG_PRIMASK)) | |||||
pri = DEFUPRI; | |||||
Done Inline ActionsAccording to ctime(3), the field tm.tm_gmtoff represents "offset from UTC in seconds". woodsb02: According to ctime(3), the field tm.tm_gmtoff represents "offset from UTC in **seconds**". | |||||
logmsg(pri, timestamp, line, from, 0); | /* | ||||
* Don't allow users to log kernel messages. | |||||
* NOTE: since LOG_KERN == 0 this will also match | |||||
* messages with no facility specified. | |||||
*/ | |||||
if ((pri & LOG_FACMASK) == LOG_KERN && !KeepKernFac) | |||||
pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri)); | |||||
/* Parse VERSION. */ | |||||
Done Inline ActionsDoes this prevent the MSG utilising the MSG-UTF8 format (starting with BOM) allowed in RFC5424? woodsb02: Does this prevent the MSG utilising the MSG-UTF8 format (starting with BOM) allowed in RFC5424? | |||||
Not Done Inline ActionsUnfortunately, it does mean that. I am not enough of an expert to fully judge what kind of data is safe to store and display on the console, which is why I thought it made more sense to stick to the existing filtering for the time being. I think we want to be consistent between RFC 3164 and RFC 5424 messages in this regard. As in, I think both of them should use the same parsemsg_remove_unsafe_characters() function to determine what is and what is not permitted. That way we can always translate between both flavours of messages. I've added a TODO to that function. ed: Unfortunately, it does mean that. I am not enough of an expert to fully judge what kind of data… | |||||
msg += i + 1; | |||||
if (msg[0] == '1' && msg[1] == ' ') | |||||
Done Inline ActionsIs this check still required for the !RemoteAddDate case, now that strptime() is being used? woodsb02: Is this check still required for the !RemoteAddDate case, now that strptime() is being used? | |||||
Not Done Inline ActionsThat's a good point. I've just changed the code to simply call strptime() unconditionally. That also keeps the external behaviour of syslogd consistent, regardless of whether RemoteAddDate is used. The strlen() call can be moved further to the bottom, so that its value is invariant. ed: That's a good point. I've just changed the code to simply call strptime() unconditionally. That… | |||||
parsemsg_rfc5424(from, pri, msg + 2); | |||||
else | |||||
parsemsg_rfc3164(from, pri, msg); | |||||
} | } | ||||
/* | /* | ||||
Done Inline ActionsCould this section be simplified by making use of strptime() to perform at least some of the parsing? woodsb02: Could this section be simplified by making use of strptime() to perform at least some of the… | |||||
Not Done Inline ActionsIt could, but I thought it made more sense not to down that path. With strptime() being (close to) the opposite of strftime(), one could argue that strptime() parses time in a locale and time zone dependent fashion. POSIX leaves it unspecified what happens if you pass in a timestamp that is nonexistent in the current time zone (e.g., due to daylight saving time). This piece of code is intended to parse time in a completely time zone oblivious fashion. It is also a bit more strict, as it does not permit leap seconds, per RFC 5424. As we already need a PARSE_NUMBER() to parse the time zone offset, it takes little effort to do the parsing of the date and time ourselves. ed: It could, but I thought it made more sense not to down that path. With `strptime()` being… | |||||
* Read /dev/klog while data are available, split into lines. | * Read /dev/klog while data are available, split into lines. | ||||
*/ | */ | ||||
static int | static int | ||||
socklist_recv_file(struct socklist *sl) | socklist_recv_file(struct socklist *sl) | ||||
Done Inline ActionsThis comment should be updated to reflect the reduced functionality of this function. woodsb02: This comment should be updated to reflect the reduced functionality of this function. | |||||
{ | { | ||||
char *p, *q, line[MAXLINE + 1]; | char *p, *q, line[MAXLINE + 1]; | ||||
int len, i; | int len, i; | ||||
len = 0; | len = 0; | ||||
for (;;) { | for (;;) { | ||||
i = read(sl->sl_socket, line + len, MAXLINE - 1 - len); | i = read(sl->sl_socket, line + len, MAXLINE - 1 - len); | ||||
if (i > 0) { | if (i > 0) { | ||||
line[i + len] = '\0'; | line[i + len] = '\0'; | ||||
} else { | } else { | ||||
Done Inline ActionsIs this supposed to be "timestamp_remote.tm.tm_year -= 1900;"? woodsb02: Is this supposed to be "timestamp_remote.tm.tm_year -= 1900;"? | |||||
Not Done Inline ActionsWell spotted. Thanks! ed: Well spotted. Thanks! | |||||
if (i < 0 && errno != EINTR && errno != EAGAIN) { | if (i < 0 && errno != EINTR && errno != EAGAIN) { | ||||
logerror("klog"); | logerror("klog"); | ||||
close(sl->sl_socket); | close(sl->sl_socket); | ||||
sl->sl_socket = -1; | sl->sl_socket = -1; | ||||
Done Inline ActionsWhat happens if the time() function fails and returns -1? woodsb02: What happens if the time() function fails and returns -1?
Consider checking "t_now != -1"… | |||||
Not Done Inline ActionsThe syslogd code already assumes that time() never fails in some other places. I think that's a fair assumption to make on somewhat decently configured systems. Even if this call were to fail, there's nothing failing really badly. It simply thinks it's December 31st, 1969, meaning it will generate log entries between January 7th, 1969 an January 6th, 1970. ed: The syslogd code already assumes that `time()` never fails in some other places. I think that's… | |||||
} | } | ||||
break; | break; | ||||
Done Inline ActionsThere are only really 3 options/iterations ever required: next year, current year, or last year. woodsb02: There are only really 3 options/iterations ever required: next year, current year, or last year. | |||||
Not Done Inline ActionsYes, it can only run three iterations. That said, instead of adding that as an explicit check, let me simply document this fact. ed: Yes, it can only run three iterations. That said, instead of adding that as an explicit check… | |||||
Done Inline ActionsMy concern was whether there was any possibility of the for loop iterating excessively (more than 3 times). E.g. if the inputs where bad, such as time() returning -1? Whilst I haven’t played out the possibilities, is it worth adding a for loop test to prevent any possible syslogd DoS. woodsb02: My concern was whether there was any possibility of the for loop iterating excessively (more… | |||||
Not Done Inline ActionsI've added an assertion to the code. ed: I've added an assertion to the code. | |||||
} | } | ||||
Done Inline ActionsI think this line can be done before the for loop, as tm_parsed will not set the tm.tm_year or tm.tm_isdst values? woodsb02: I think this line can be done before the for loop, as tm_parsed will not set the tm.tm_year or… | |||||
Not Done Inline ActionsMy initial version of this code did exactly this, but that's incorrect. mktime() alters its input, which in my case caused it to apply the time zone offset/DST during every loop of the iteration. This is why I'm reinitialising the entire structure during every iteration now. ed: My initial version of this code did exactly this, but that's incorrect. `mktime()` alters its… | |||||
for (p = line; (q = strchr(p, '\n')) != NULL; p = q + 1) { | for (p = line; (q = strchr(p, '\n')) != NULL; p = q + 1) { | ||||
*q = '\0'; | *q = '\0'; | ||||
printsys(p); | printsys(p); | ||||
} | } | ||||
len = strlen(p); | len = strlen(p); | ||||
if (len >= MAXLINE - 1) { | if (len >= MAXLINE - 1) { | ||||
printsys(p); | printsys(p); | ||||
Show All 13 Lines | |||||
*/ | */ | ||||
static void | static void | ||||
printsys(char *msg) | printsys(char *msg) | ||||
{ | { | ||||
char *p, *q; | char *p, *q; | ||||
long n; | long n; | ||||
int flags, isprintf, pri; | int flags, isprintf, pri; | ||||
flags = ISKERNEL | SYNC_FILE; /* fsync after write */ | flags = SYNC_FILE; /* fsync after write */ | ||||
p = msg; | p = msg; | ||||
pri = DEFSPRI; | pri = DEFSPRI; | ||||
Done Inline ActionsPerhaps a comment should added to describe the functionality of this function, including describing that the PRI has already been handled by parsemsg() and the actual log printing is handled by logmsg() woodsb02: Perhaps a comment should added to describe the functionality of this function, including… | |||||
isprintf = 1; | isprintf = 1; | ||||
if (*p == '<') { | if (*p == '<') { | ||||
errno = 0; | errno = 0; | ||||
n = strtol(p + 1, &q, 10); | n = strtol(p + 1, &q, 10); | ||||
if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) { | if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) { | ||||
p = q + 1; | p = q + 1; | ||||
pri = n; | pri = n; | ||||
isprintf = 0; | isprintf = 0; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Kernel printf's and LOG_CONSOLE messages have been displayed | * Kernel printf's and LOG_CONSOLE messages have been displayed | ||||
* on the console already. | * on the console already. | ||||
*/ | */ | ||||
if (isprintf || (pri & LOG_FACMASK) == LOG_CONSOLE) | if (isprintf || (pri & LOG_FACMASK) == LOG_CONSOLE) | ||||
flags |= IGN_CONS; | flags |= IGN_CONS; | ||||
if (pri &~ (LOG_FACMASK|LOG_PRIMASK)) | if (pri &~ (LOG_FACMASK|LOG_PRIMASK)) | ||||
pri = DEFSPRI; | pri = DEFSPRI; | ||||
logmsg(pri, NULL, p, LocalHostName, flags); | logmsg(pri, NULL, LocalHostName, "kernel", NULL, NULL, NULL, p, flags); | ||||
} | } | ||||
static time_t now; | static time_t now; | ||||
/* | /* | ||||
* Match a program or host name against a specification. | * Match a program or host name against a specification. | ||||
* Return a non-0 value if the message must be ignored | * Return a non-0 value if the message must be ignored | ||||
* based on the specification. | * based on the specification. | ||||
Show All 33 Lines | if (prev == ',' && (next == '\0' || next == ',')) | ||||
return exclude; | return exclude; | ||||
} | } | ||||
/* No explicit match for this name: skip the message iff | /* No explicit match for this name: skip the message iff | ||||
the spec is an inclusive one. */ | the spec is an inclusive one. */ | ||||
return !exclude; | return !exclude; | ||||
} | } | ||||
/* | /* | ||||
* Log a message to the appropriate log files, users, etc. based on | * Logs a message to the appropriate log files, users, etc. based on the | ||||
* the priority. | * priority. Log messages are always formatted according to RFC 3164, | ||||
* even if they were in RFC 5424 format originally, The MSGID and | |||||
* STRUCTURED-DATA fields are thus discarded for the time being. | |||||
*/ | */ | ||||
Done Inline ActionsThis comment should be updated to explain the current functionality of this function. woodsb02: This comment should be updated to explain the current functionality of this function.
I.e. this… | |||||
static void | static void | ||||
logmsg(int pri, const char *timestamp, const char *msg, const char *from, | logmsg(int pri, const struct logtime *timestamp, const char *from, | ||||
int flags) | const char *app_name, const char *procid, const char *msgid __unused, | ||||
const char *structured_data __unused, const char *msg, int flags) | |||||
{ | { | ||||
struct timeval tv; | |||||
struct logtime timestamp_now; | |||||
struct filed *f; | struct filed *f; | ||||
int i, fac, msglen, prilev; | size_t msglen; | ||||
char prog[NAME_MAX+1]; | int fac, prilev; | ||||
char buf[MAXLINE+1]; | char buf[MAXLINE+1]; | ||||
dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n", | dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n", | ||||
pri, flags, from, msg); | pri, flags, from, msg); | ||||
(void)time(&now); | (void)gettimeofday(&tv, NULL); | ||||
if (timestamp == NULL) | now = tv.tv_sec; | ||||
timestamp = ctime(&now) + 4; | if (timestamp == NULL) { | ||||
localtime_r(&now, ×tamp_now.tm); | |||||
timestamp_now.usec = tv.tv_usec; | |||||
timestamp = ×tamp_now; | |||||
} | |||||
/* extract facility and priority level */ | /* extract facility and priority level */ | ||||
if (flags & MARK) | if (flags & MARK) | ||||
fac = LOG_NFACILITIES; | fac = LOG_NFACILITIES; | ||||
else | else | ||||
fac = LOG_FAC(pri); | fac = LOG_FAC(pri); | ||||
/* Check maximum facility number. */ | /* Check maximum facility number. */ | ||||
if (fac > LOG_NFACILITIES) | if (fac > LOG_NFACILITIES) | ||||
return; | return; | ||||
prilev = LOG_PRI(pri); | prilev = LOG_PRI(pri); | ||||
/* Extract TAG part of the message (usually program name). */ | /* Prepend the application name to the message if provided. */ | ||||
for (i = 0; i < NAME_MAX; i++) { | if (app_name != NULL) { | ||||
if (!isprint(msg[i]) || msg[i] == ':' || msg[i] == '[' || | if (procid != NULL) | ||||
msg[i] == '/' || isspace(msg[i])) | msglen = snprintf(buf, sizeof(buf), "%s[%s]: %s", | ||||
break; | app_name, procid, msg); | ||||
prog[i] = msg[i]; | else | ||||
} | msglen = snprintf(buf, sizeof(buf), "%s: %s", | ||||
prog[i] = 0; | app_name, msg); | ||||
/* add kernel prefix for kernel messages */ | |||||
if (flags & ISKERNEL) { | |||||
snprintf(buf, sizeof(buf), "%s: %s", | |||||
use_bootfile ? bootfile : "kernel", msg); | |||||
msg = buf; | msg = buf; | ||||
} | } else | ||||
msglen = strlen(msg); | msglen = strlen(msg); | ||||
/* log the message to the particular outputs */ | /* log the message to the particular outputs */ | ||||
if (!Initialized) { | if (!Initialized) { | ||||
f = &consfile; | f = &consfile; | ||||
/* | /* | ||||
* Open in non-blocking mode to avoid hangs during open | * Open in non-blocking mode to avoid hangs during open | ||||
* and close(waiting for the port to drain). | * and close(waiting for the port to drain). | ||||
*/ | */ | ||||
f->f_file = open(ctty, O_WRONLY | O_NONBLOCK, 0); | f->f_file = open(ctty, O_WRONLY | O_NONBLOCK, 0); | ||||
if (f->f_file >= 0) { | if (f->f_file >= 0) { | ||||
(void)strlcpy(f->f_lasttime, timestamp, | f->f_lasttime = *timestamp; | ||||
sizeof(f->f_lasttime)); | |||||
fprintlog(f, flags, msg); | fprintlog(f, flags, msg); | ||||
close(f->f_file); | close(f->f_file); | ||||
f->f_file = -1; | f->f_file = -1; | ||||
} | } | ||||
return; | return; | ||||
} | } | ||||
STAILQ_FOREACH(f, &fhead, next) { | STAILQ_FOREACH(f, &fhead, next) { | ||||
/* skip messages that are incorrect priority */ | /* skip messages that are incorrect priority */ | ||||
if (!(((f->f_pcmp[fac] & PRI_EQ) && (f->f_pmask[fac] == prilev)) | if (!(((f->f_pcmp[fac] & PRI_EQ) && (f->f_pmask[fac] == prilev)) | ||||
||((f->f_pcmp[fac] & PRI_LT) && (f->f_pmask[fac] < prilev)) | ||((f->f_pcmp[fac] & PRI_LT) && (f->f_pmask[fac] < prilev)) | ||||
||((f->f_pcmp[fac] & PRI_GT) && (f->f_pmask[fac] > prilev)) | ||((f->f_pcmp[fac] & PRI_GT) && (f->f_pmask[fac] > prilev)) | ||||
) | ) | ||||
|| f->f_pmask[fac] == INTERNAL_NOPRI) | || f->f_pmask[fac] == INTERNAL_NOPRI) | ||||
continue; | continue; | ||||
/* skip messages with the incorrect hostname */ | /* skip messages with the incorrect hostname */ | ||||
if (skip_message(from, f->f_host, 0)) | if (skip_message(from, f->f_host, 0)) | ||||
continue; | continue; | ||||
/* skip messages with the incorrect program name */ | /* skip messages with the incorrect program name */ | ||||
if (skip_message(prog, f->f_program, 1)) | if (skip_message(app_name == NULL ? "" : app_name, | ||||
f->f_program, 1)) | |||||
continue; | continue; | ||||
/* skip message to console if it has already been printed */ | /* skip message to console if it has already been printed */ | ||||
if (f->f_type == F_CONSOLE && (flags & IGN_CONS)) | if (f->f_type == F_CONSOLE && (flags & IGN_CONS)) | ||||
continue; | continue; | ||||
/* don't output marks to recently written files */ | /* don't output marks to recently written files */ | ||||
if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2) | if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2) | ||||
continue; | continue; | ||||
/* | /* | ||||
* suppress duplicate lines to this file | * suppress duplicate lines to this file | ||||
*/ | */ | ||||
if (no_compress - (f->f_type != F_PIPE) < 1 && | if (no_compress - (f->f_type != F_PIPE) < 1 && | ||||
(flags & MARK) == 0 && msglen == f->f_prevlen && | (flags & MARK) == 0 && msglen == f->f_prevlen && | ||||
!strcmp(msg, f->f_prevline) && | !strcmp(msg, f->f_prevline) && | ||||
!strcasecmp(from, f->f_prevhost)) { | !strcasecmp(from, f->f_prevhost)) { | ||||
(void)strlcpy(f->f_lasttime, timestamp, | f->f_lasttime = *timestamp; | ||||
sizeof(f->f_lasttime)); | |||||
f->f_prevcount++; | f->f_prevcount++; | ||||
dprintf("msg repeated %d times, %ld sec of %d\n", | dprintf("msg repeated %d times, %ld sec of %d\n", | ||||
f->f_prevcount, (long)(now - f->f_time), | f->f_prevcount, (long)(now - f->f_time), | ||||
repeatinterval[f->f_repeatcount]); | repeatinterval[f->f_repeatcount]); | ||||
/* | /* | ||||
* If domark would have logged this by now, | * If domark would have logged this by now, | ||||
* flush it now (so we don't hold isolated messages), | * flush it now (so we don't hold isolated messages), | ||||
* but back off so we'll flush less often | * but back off so we'll flush less often | ||||
* in the future. | * in the future. | ||||
*/ | */ | ||||
if (now > REPEATTIME(f)) { | if (now > REPEATTIME(f)) { | ||||
fprintlog(f, flags, (char *)NULL); | fprintlog(f, flags, (char *)NULL); | ||||
BACKOFF(f); | BACKOFF(f); | ||||
} | } | ||||
} else { | } else { | ||||
/* new line, save it */ | /* new line, save it */ | ||||
if (f->f_prevcount) | if (f->f_prevcount) | ||||
fprintlog(f, 0, (char *)NULL); | fprintlog(f, 0, (char *)NULL); | ||||
f->f_repeatcount = 0; | f->f_repeatcount = 0; | ||||
f->f_prevpri = pri; | f->f_prevpri = pri; | ||||
(void)strlcpy(f->f_lasttime, timestamp, | f->f_lasttime = *timestamp; | ||||
sizeof(f->f_lasttime)); | |||||
(void)strlcpy(f->f_prevhost, from, | (void)strlcpy(f->f_prevhost, from, | ||||
sizeof(f->f_prevhost)); | sizeof(f->f_prevhost)); | ||||
if (msglen < MAXSVLINE) { | if (msglen < MAXSVLINE) { | ||||
f->f_prevlen = msglen; | f->f_prevlen = msglen; | ||||
(void)strlcpy(f->f_prevline, msg, sizeof(f->f_prevline)); | (void)strlcpy(f->f_prevline, msg, sizeof(f->f_prevline)); | ||||
fprintlog(f, flags, (char *)NULL); | fprintlog(f, flags, (char *)NULL); | ||||
} else { | } else { | ||||
f->f_prevline[0] = 0; | f->f_prevline[0] = 0; | ||||
Show All 22 Lines | |||||
static void | static void | ||||
fprintlog(struct filed *f, int flags, const char *msg) | fprintlog(struct filed *f, int flags, const char *msg) | ||||
{ | { | ||||
struct iovec iov[IOV_SIZE]; | struct iovec iov[IOV_SIZE]; | ||||
struct addrinfo *r; | struct addrinfo *r; | ||||
int l, lsent = 0; | int l, lsent = 0; | ||||
char line[MAXLINE + 1], repbuf[80], greetings[200], *wmsg = NULL; | char line[MAXLINE + 1], repbuf[80], greetings[200], *wmsg = NULL; | ||||
char nul[] = "", space[] = " ", lf[] = "\n", crlf[] = "\r\n"; | char nul[] = "", space[] = " ", lf[] = "\n", crlf[] = "\r\n"; | ||||
char timebuf[RFC3164_DATELEN + 1]; | |||||
const char *msgret; | const char *msgret; | ||||
if (strftime(timebuf, sizeof(timebuf), RFC3164_DATEFMT, | |||||
&f->f_lasttime.tm) == 0) | |||||
timebuf[0] = '\0'; | |||||
if (f->f_type == F_WALL) { | if (f->f_type == F_WALL) { | ||||
/* The time displayed is not synchornized with the other log | /* The time displayed is not synchornized with the other log | ||||
* destinations (like messages). Following fragment was using | * destinations (like messages). Following fragment was using | ||||
* ctime(&now), which was updating the time every 30 sec. | * ctime(&now), which was updating the time every 30 sec. | ||||
* With f_lasttime, time is synchronized correctly. | * With f_lasttime, time is synchronized correctly. | ||||
*/ | */ | ||||
iov[0] = (struct iovec){ | iov[0] = (struct iovec){ | ||||
.iov_base = greetings, | .iov_base = greetings, | ||||
.iov_len = snprintf(greetings, sizeof(greetings), | .iov_len = snprintf(greetings, sizeof(greetings), | ||||
"\r\n\7Message from syslogd@%s " | "\r\n\7Message from syslogd@%s " | ||||
"at %.24s ...\r\n", | "at %.24s ...\r\n", | ||||
f->f_prevhost, f->f_lasttime) | f->f_prevhost, timebuf) | ||||
}; | }; | ||||
if (iov[0].iov_len >= sizeof(greetings)) | if (iov[0].iov_len >= sizeof(greetings)) | ||||
iov[0].iov_len = sizeof(greetings) - 1; | iov[0].iov_len = sizeof(greetings) - 1; | ||||
iov[1] = (struct iovec){ | iov[1] = (struct iovec){ | ||||
.iov_base = nul, | .iov_base = nul, | ||||
.iov_len = 0 | .iov_len = 0 | ||||
}; | }; | ||||
} else { | } else { | ||||
iov[0] = (struct iovec){ | iov[0] = (struct iovec){ | ||||
.iov_base = f->f_lasttime, | .iov_base = timebuf, | ||||
.iov_len = strlen(f->f_lasttime) | .iov_len = strlen(timebuf) | ||||
}; | }; | ||||
iov[1] = (struct iovec){ | iov[1] = (struct iovec){ | ||||
.iov_base = space, | .iov_base = space, | ||||
.iov_len = 1 | .iov_len = 1 | ||||
}; | }; | ||||
} | } | ||||
if (LogFacPri) { | if (LogFacPri) { | ||||
▲ Show 20 Lines • Show All 395 Lines • ▼ Show 20 Lines | logerror(const char *type) | ||||
recursed++; | recursed++; | ||||
if (errno) | if (errno) | ||||
(void)snprintf(buf, | (void)snprintf(buf, | ||||
sizeof buf, "syslogd: %s: %s", type, strerror(errno)); | sizeof buf, "syslogd: %s: %s", type, strerror(errno)); | ||||
else | else | ||||
(void)snprintf(buf, sizeof buf, "syslogd: %s", type); | (void)snprintf(buf, sizeof buf, "syslogd: %s", type); | ||||
errno = 0; | errno = 0; | ||||
dprintf("%s\n", buf); | dprintf("%s\n", buf); | ||||
logmsg(LOG_SYSLOG|LOG_ERR, NULL, buf, LocalHostName, 0); | logmsg(LOG_SYSLOG|LOG_ERR, NULL, LocalHostName, NULL, NULL, NULL, | ||||
NULL, buf, 0); | |||||
recursed--; | recursed--; | ||||
} | } | ||||
static void | static void | ||||
die(int signo) | die(int signo) | ||||
{ | { | ||||
struct filed *f; | struct filed *f; | ||||
struct socklist *sl; | struct socklist *sl; | ||||
▲ Show 20 Lines • Show All 330 Lines • ▼ Show 20 Lines | #endif | ||||
break; | break; | ||||
} | } | ||||
if (f->f_program) | if (f->f_program) | ||||
printf(" (%s)", f->f_program); | printf(" (%s)", f->f_program); | ||||
printf("\n"); | printf("\n"); | ||||
} | } | ||||
} | } | ||||
logmsg(LOG_SYSLOG|LOG_INFO, NULL, "syslogd: restart", LocalHostName, 0); | logmsg(LOG_SYSLOG|LOG_INFO, NULL, LocalHostName, NULL, NULL, NULL, | ||||
NULL, "syslogd: restart", 0); | |||||
dprintf("syslogd: restarted\n"); | dprintf("syslogd: restarted\n"); | ||||
/* | /* | ||||
* Log a change in hostname, but only on a restart. | * Log a change in hostname, but only on a restart. | ||||
*/ | */ | ||||
if (signo != 0 && strcmp(oldLocalHostName, LocalHostName) != 0) { | if (signo != 0 && strcmp(oldLocalHostName, LocalHostName) != 0) { | ||||
(void)snprintf(hostMsg, sizeof(hostMsg), | (void)snprintf(hostMsg, sizeof(hostMsg), | ||||
"syslogd: hostname changed, \"%s\" to \"%s\"", | "syslogd: hostname changed, \"%s\" to \"%s\"", | ||||
oldLocalHostName, LocalHostName); | oldLocalHostName, LocalHostName); | ||||
logmsg(LOG_SYSLOG|LOG_INFO, NULL, hostMsg, LocalHostName, 0); | logmsg(LOG_SYSLOG|LOG_INFO, NULL, LocalHostName, NULL, NULL, | ||||
NULL, NULL, hostMsg, 0); | |||||
dprintf("%s\n", hostMsg); | dprintf("%s\n", hostMsg); | ||||
} | } | ||||
/* | /* | ||||
* Log the kernel boot file if we aren't going to use it as | * Log the kernel boot file if we aren't going to use it as | ||||
* the prefix, and if this is *not* a restart. | * the prefix, and if this is *not* a restart. | ||||
*/ | */ | ||||
if (signo == 0 && !use_bootfile) { | if (signo == 0 && !use_bootfile) { | ||||
(void)snprintf(bootfileMsg, sizeof(bootfileMsg), | (void)snprintf(bootfileMsg, sizeof(bootfileMsg), | ||||
"syslogd: kernel boot file is %s", bootfile); | "syslogd: kernel boot file is %s", bootfile); | ||||
logmsg(LOG_KERN|LOG_INFO, NULL, bootfileMsg, LocalHostName, 0); | logmsg(LOG_KERN|LOG_INFO, NULL, LocalHostName, NULL, NULL, | ||||
NULL, NULL, bootfileMsg, 0); | |||||
dprintf("%s\n", bootfileMsg); | dprintf("%s\n", bootfileMsg); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Crack a configuration file line | * Crack a configuration file line | ||||
*/ | */ | ||||
static struct filed * | static struct filed * | ||||
▲ Show 20 Lines • Show All 292 Lines • ▼ Show 20 Lines | |||||
markit(void) | markit(void) | ||||
{ | { | ||||
struct filed *f; | struct filed *f; | ||||
struct deadq_entry *dq, *dq0; | struct deadq_entry *dq, *dq0; | ||||
now = time((time_t *)NULL); | now = time((time_t *)NULL); | ||||
MarkSeq += TIMERINTVL; | MarkSeq += TIMERINTVL; | ||||
if (MarkSeq >= MarkInterval) { | if (MarkSeq >= MarkInterval) { | ||||
logmsg(LOG_INFO, NULL, "-- MARK --", LocalHostName, MARK); | logmsg(LOG_INFO, NULL, LocalHostName, NULL, NULL, NULL, NULL, | ||||
"-- MARK --", MARK); | |||||
MarkSeq = 0; | MarkSeq = 0; | ||||
} | } | ||||
STAILQ_FOREACH(f, &fhead, next) { | STAILQ_FOREACH(f, &fhead, next) { | ||||
if (f->f_prevcount && now >= REPEATTIME(f)) { | if (f->f_prevcount && now >= REPEATTIME(f)) { | ||||
dprintf("flush %s: repeated %d times, %d sec.\n", | dprintf("flush %s: repeated %d times, %d sec.\n", | ||||
TypeNames[f->f_type], f->f_prevcount, | TypeNames[f->f_type], f->f_prevcount, | ||||
repeatinterval[f->f_repeatcount]); | repeatinterval[f->f_repeatcount]); | ||||
▲ Show 20 Lines • Show All 724 Lines • Show Last 20 Lines |
Probably worth adding an “else timestamp = NULL” to specifically initialise timestamp, and for clarity.