Initial commit of libsyslogp
This commit is contained in:
parent
ddc2c91660
commit
87cc7a8e14
3
config
3
config
|
@ -32,5 +32,8 @@
|
||||||
source "scripts/paths"
|
source "scripts/paths"
|
||||||
|
|
||||||
# Project-specific variables below.
|
# Project-specific variables below.
|
||||||
|
[ -z "${LIBISO8601_CFLAGS}" ] && LIBISO8601_CFLAGS="$(libiso8601-config --cflags)"
|
||||||
|
[ -z "${LIBISO8601_LIBS}" ] && LIBISO8601_LIBS="$(libiso8601-config --libs)"
|
||||||
|
|
||||||
[ -z "${CC}" ] && CC="gcc"
|
[ -z "${CC}" ] && CC="gcc"
|
||||||
[ -z "${CFLAGS}" ] && CFLAGS="-g -O2 -W -Wall"
|
[ -z "${CFLAGS}" ] && CFLAGS="-g -O2 -W -Wall"
|
||||||
|
|
|
@ -5,7 +5,25 @@
|
||||||
* http://www.gnu.org/copyleft/gpl.html for details.
|
* http://www.gnu.org/copyleft/gpl.html for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \mainpage
|
/*! \mainpage Syslog message parsing library
|
||||||
|
|
||||||
|
This library contains a set of routines helpful in parsing syslog messages and
|
||||||
|
is intended for any consumer of syslog messages (e.g. a syslog relay or a log
|
||||||
|
analyser, etc.).
|
||||||
|
|
||||||
|
\section standards Standards
|
||||||
|
|
||||||
|
The parser is designed around <a href="http://tools.ietf.org/html/rfc3164">RFC
|
||||||
|
3164</a> messages, which it will parse perfectly. However, very few actual
|
||||||
|
syslog implementations seem to match this exactly, so the parsing routine is
|
||||||
|
more flexible; it can cope with various different date/time formats etc. In
|
||||||
|
addition, it will also recover a process ID in square brackets after the
|
||||||
|
application tag if one is present.
|
||||||
|
|
||||||
|
The parser is not (yet) <a href="http://tools.ietf.org/html/rfc5424">RFC
|
||||||
|
5424</a> compliant, although it will be able to read the priority, timestamp
|
||||||
|
and tag and PROCID fields correctly. RFC 5424 parsing is a future goal of the
|
||||||
|
library; it has not been implemented due to time constraints.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#define HEADER_libsyslogp
|
#define HEADER_libsyslogp
|
||||||
|
|
||||||
/* standard includes, or includes needed for type declarations */
|
/* standard includes, or includes needed for type declarations */
|
||||||
|
#include <iso8601.h>
|
||||||
|
|
||||||
/* options for text editors
|
/* options for text editors
|
||||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
#include "syslogp.h"
|
#include "syslogp.h"
|
||||||
|
|
||||||
/* Below are all the includes used throughout the library. */
|
/* Below are all the includes used throughout the library. */
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
|
||||||
/* options for text editors
|
/* options for text editors
|
||||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
||||||
|
|
|
@ -0,0 +1,465 @@
|
||||||
|
/* libsyslogp/src/libsyslogp/500_syslogp.c
|
||||||
|
*
|
||||||
|
* (c)2009, Laurence Withers, <l@lwithers.me.uk>.
|
||||||
|
* Released under the GNU GPLv3. See file COPYING or
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static char*
|
||||||
|
syslog_parse_pri(struct syslog_msg* msg, char* str)
|
||||||
|
{
|
||||||
|
char* rd;
|
||||||
|
unsigned int pri = 0;
|
||||||
|
|
||||||
|
rd = str;
|
||||||
|
if(*rd++ != '<') goto error_out;
|
||||||
|
|
||||||
|
if(*rd == '0') {
|
||||||
|
if(rd[1] != '>') goto error_out;
|
||||||
|
} else {
|
||||||
|
do {
|
||||||
|
if(*rd < '0' || *rd > '9') goto error_out;
|
||||||
|
pri *= 10;
|
||||||
|
pri += *rd - '0';
|
||||||
|
++rd;
|
||||||
|
}while(*rd != '>');
|
||||||
|
if(rd - str > 4) goto error_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(pri & 7) {
|
||||||
|
case 0: msg->level = LOG_EMERG; break;
|
||||||
|
case 1: msg->level = LOG_ALERT; break;
|
||||||
|
case 2: msg->level = LOG_CRIT; break;
|
||||||
|
case 3: msg->level = LOG_ERR; break;
|
||||||
|
case 4: msg->level = LOG_WARNING; break;
|
||||||
|
case 5: msg->level = LOG_NOTICE; break;
|
||||||
|
case 6: msg->level = LOG_INFO; break;
|
||||||
|
case 7: msg->level = LOG_DEBUG; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(pri >> 3) {
|
||||||
|
case 0: msg->facility = LOG_KERN; break;
|
||||||
|
case 1: msg->facility = LOG_USER; break;
|
||||||
|
case 2: msg->facility = LOG_MAIL; break;
|
||||||
|
case 3: msg->facility = LOG_DAEMON; break;
|
||||||
|
case 4: msg->facility = LOG_AUTHPRIV; break;
|
||||||
|
case 5: msg->facility = LOG_SYSLOG; break;
|
||||||
|
case 6: msg->facility = LOG_LPR; break;
|
||||||
|
case 7: msg->facility = LOG_NEWS; break;
|
||||||
|
case 8: msg->facility = LOG_UUCP; break;
|
||||||
|
case 9: msg->facility = LOG_CRON; break;
|
||||||
|
case 10: msg->facility = LOG_AUTHPRIV; break;
|
||||||
|
case 11: msg->facility = LOG_FTP; break;
|
||||||
|
case 12: msg->facility = LOG_CRON; break;
|
||||||
|
case 13: msg->facility = LOG_SYSLOG; break;
|
||||||
|
case 14: msg->facility = LOG_SYSLOG; break;
|
||||||
|
case 15: msg->facility = LOG_CRON; break;
|
||||||
|
case 16: msg->facility = LOG_LOCAL0; break;
|
||||||
|
case 17: msg->facility = LOG_LOCAL1; break;
|
||||||
|
case 18: msg->facility = LOG_LOCAL2; break;
|
||||||
|
case 19: msg->facility = LOG_LOCAL3; break;
|
||||||
|
case 20: msg->facility = LOG_LOCAL4; break;
|
||||||
|
case 21: msg->facility = LOG_LOCAL5; break;
|
||||||
|
case 22: msg->facility = LOG_LOCAL6; break;
|
||||||
|
case 23: msg->facility = LOG_LOCAL7; break;
|
||||||
|
default: goto error_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ++rd;
|
||||||
|
|
||||||
|
error_out:
|
||||||
|
msg->level = LOG_NOTICE;
|
||||||
|
msg->facility = LOG_USER;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
syslog_parse_month(const char* str, int* month)
|
||||||
|
{
|
||||||
|
if(!strncmp(str, "Jan", 3)) *month = 1;
|
||||||
|
else if(!strncmp(str, "Feb", 3)) *month = 2;
|
||||||
|
else if(!strncmp(str, "Mar", 3)) *month = 3;
|
||||||
|
else if(!strncmp(str, "Apr", 3)) *month = 4;
|
||||||
|
else if(!strncmp(str, "May", 3)) *month = 5;
|
||||||
|
else if(!strncmp(str, "Jun", 3)) *month = 6;
|
||||||
|
else if(!strncmp(str, "Jul", 3)) *month = 7;
|
||||||
|
else if(!strncmp(str, "Aug", 3)) *month = 8;
|
||||||
|
else if(!strncmp(str, "Sep", 3)) *month = 9;
|
||||||
|
else if(!strncmp(str, "Oct", 3)) *month = 10;
|
||||||
|
else if(!strncmp(str, "Nov", 3)) *month = 11;
|
||||||
|
else if(!strncmp(str, "Dec", 3)) *month = 12;
|
||||||
|
else return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
syslog_parse_implied_year = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
syslog_parse_set_implied_year(int year)
|
||||||
|
{
|
||||||
|
syslog_parse_implied_year = year;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static char*
|
||||||
|
syslog_parse_date(struct syslog_msg* msg, char* str)
|
||||||
|
{
|
||||||
|
int seen_t, len, year, month, day, hour, min, sec;
|
||||||
|
char* p;
|
||||||
|
time_t t;
|
||||||
|
struct tm* tm;
|
||||||
|
|
||||||
|
len = strlen(str);
|
||||||
|
|
||||||
|
/* skip leading whitespace */
|
||||||
|
for(; *str == ' '; ++str) { }
|
||||||
|
|
||||||
|
/* check for an ISO8601 date and time (don't just try parsing the string as
|
||||||
|
* something starting e.g. "2009 " would parse OK, but be wrong) */
|
||||||
|
seen_t = 0;
|
||||||
|
for(p = str; *p != ' '; ++p) {
|
||||||
|
switch(*p) {
|
||||||
|
case 'T':
|
||||||
|
if(seen_t) goto not_iso;
|
||||||
|
seen_t = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '0' ... '9':
|
||||||
|
case '-':
|
||||||
|
case ':':
|
||||||
|
case '.':
|
||||||
|
case 'Z':
|
||||||
|
case '+':
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto not_iso;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!seen_t) goto not_iso;
|
||||||
|
|
||||||
|
*p = 0;
|
||||||
|
if(iso8601_parse(str, &msg->timestamp, 0, 0)) {
|
||||||
|
*p = ' ';
|
||||||
|
goto not_iso;
|
||||||
|
}
|
||||||
|
return p + 1;
|
||||||
|
|
||||||
|
/* fall back to older BSD-style or similar timestamps */
|
||||||
|
not_iso:
|
||||||
|
if(len >= 21 && str[3] == ' ' && str[6] == ' ' && str[11] == ' ' &&
|
||||||
|
str[14] == ':' && str[17] == ':')
|
||||||
|
{
|
||||||
|
/* PIX timestamp, expected format: MMM DD YYYY HH:MM:SS: */
|
||||||
|
|
||||||
|
if(syslog_parse_month(str, &month)) goto not_pix;
|
||||||
|
|
||||||
|
day = strtol(str + 4, &p, 10);
|
||||||
|
if(*p != ' ') goto not_pix;
|
||||||
|
|
||||||
|
year = strtol(str + 7, &p, 10);
|
||||||
|
if(*p != ' ') goto not_pix;
|
||||||
|
|
||||||
|
hour = strtol(str + 12, &p, 10);
|
||||||
|
if(*p != ':') goto not_pix;
|
||||||
|
|
||||||
|
min = strtol(str + 15, &p, 10);
|
||||||
|
if(*p != ':') goto not_pix;
|
||||||
|
|
||||||
|
sec = strtol(str + 18, &p, 10);
|
||||||
|
if(*p != ' ' && *p != ':') goto not_pix;
|
||||||
|
|
||||||
|
if(iso8601_from_cal(&msg->timestamp, year, month, day) ||
|
||||||
|
iso8601_from_clocktime(&msg->timestamp, hour, min, sec))
|
||||||
|
{
|
||||||
|
goto not_pix;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str + ((str[20] == ' ') ? 21 : 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
not_pix:
|
||||||
|
if(len >= 21 && str[3] == ' ' && str[6] == ' ' && str[9] == ':' &&
|
||||||
|
str[12] == ':' && str[15] == ' ' && str[20] == ' ')
|
||||||
|
{
|
||||||
|
/* Linksys timestamp, expected format: MMM DD HH:MM:SS YYYY */
|
||||||
|
|
||||||
|
if(syslog_parse_month(str, &month)) goto not_linksys;
|
||||||
|
|
||||||
|
day = strtol(str + 4, &p, 10);
|
||||||
|
if(*p != ' ') goto not_linksys;
|
||||||
|
|
||||||
|
hour = strtol(str + 7, &p, 10);
|
||||||
|
if(*p != ':') goto not_linksys;
|
||||||
|
|
||||||
|
min = strtol(str + 10, &p, 10);
|
||||||
|
if(*p != ':') goto not_linksys;
|
||||||
|
|
||||||
|
sec = strtol(str + 13, &p, 10);
|
||||||
|
if(*p != ' ') goto not_linksys;
|
||||||
|
|
||||||
|
year = strtol(str + 16, &p, 10);
|
||||||
|
if(*p != ' ' || year < 1970 || year > 2100) goto not_linksys;
|
||||||
|
|
||||||
|
if(iso8601_from_cal(&msg->timestamp, year, month, day) ||
|
||||||
|
iso8601_from_clocktime(&msg->timestamp, hour, min, sec))
|
||||||
|
{
|
||||||
|
goto not_linksys;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str + 21;
|
||||||
|
}
|
||||||
|
|
||||||
|
not_linksys:
|
||||||
|
if(len >= 15 && str[3] == ' ' && str[6] == ' ' && str[9] == ':' &&
|
||||||
|
str[12] == ':')
|
||||||
|
{
|
||||||
|
/* RFC 3164 timestamp, expected format: MMM DD HH:MM:SS ... */
|
||||||
|
|
||||||
|
if(syslog_parse_month(str, &month)) goto not_bsd;
|
||||||
|
|
||||||
|
day = strtol(str + 4, &p, 10);
|
||||||
|
if(*p != ' ') goto not_bsd;
|
||||||
|
|
||||||
|
hour = strtol(str + 7, &p, 10);
|
||||||
|
if(*p != ':') goto not_bsd;
|
||||||
|
|
||||||
|
min = strtol(str + 10, &p, 10);
|
||||||
|
if(*p != ':') goto not_bsd;
|
||||||
|
|
||||||
|
sec = strtol(str + 13, &p, 10);
|
||||||
|
if(*p != ' ' && *p != ':' && *p != '.') goto not_bsd;
|
||||||
|
|
||||||
|
/* get year */
|
||||||
|
if(syslog_parse_implied_year) {
|
||||||
|
year = syslog_parse_implied_year;
|
||||||
|
} else {
|
||||||
|
time(&t);
|
||||||
|
tm = gmtime(&t);
|
||||||
|
year = tm->tm_year + 1900;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(iso8601_from_cal(&msg->timestamp, year, month, day) ||
|
||||||
|
iso8601_from_clocktime(&msg->timestamp, hour, min, sec))
|
||||||
|
{
|
||||||
|
goto not_bsd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for fractional seconds */
|
||||||
|
p = str + 15;
|
||||||
|
if(*p != '.') return str + 16; /* skip trailing ':' or space */
|
||||||
|
|
||||||
|
sec = 0;
|
||||||
|
min = 0;
|
||||||
|
++p;
|
||||||
|
do {
|
||||||
|
if(*p < '0' || *p > '9') return str + 15;
|
||||||
|
++min;
|
||||||
|
sec *= 10;
|
||||||
|
sec += *p - '0';
|
||||||
|
++p;
|
||||||
|
}while(*p && *p != ' ');
|
||||||
|
|
||||||
|
if(min > 9) return str + 15;
|
||||||
|
for(msg->timestamp.nsec = sec; min < 9; ++min) {
|
||||||
|
msg->timestamp.nsec *= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
not_bsd:
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static char*
|
||||||
|
syslog_parse_spacefield(char** ptr, char* str)
|
||||||
|
{
|
||||||
|
/* skip whitespace */
|
||||||
|
for(; *str == ' '; ++str) { }
|
||||||
|
if(!*str) return str;
|
||||||
|
|
||||||
|
/* store pointer to string */
|
||||||
|
*ptr = str;
|
||||||
|
|
||||||
|
/* move to end of string */
|
||||||
|
for(; *str != ' '; ++str) {
|
||||||
|
if(!*str) return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* null terminate and move to next field */
|
||||||
|
*str++ = 0;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static char*
|
||||||
|
syslog_parse_tag_id(struct syslog_msg* msg, char* str)
|
||||||
|
{
|
||||||
|
char* p;
|
||||||
|
|
||||||
|
/* skip whitespace */
|
||||||
|
for(; *str == ' '; ++str) { }
|
||||||
|
if(!*str) return str;
|
||||||
|
|
||||||
|
/* grab tag */
|
||||||
|
msg->tag = str;
|
||||||
|
|
||||||
|
/* search for tag delimiter */
|
||||||
|
for(;; ++str) {
|
||||||
|
switch(*str) {
|
||||||
|
case ':':
|
||||||
|
case '.':
|
||||||
|
case '[':
|
||||||
|
case '{':
|
||||||
|
case '<':
|
||||||
|
goto found_id;
|
||||||
|
|
||||||
|
case ' ':
|
||||||
|
case 0:
|
||||||
|
goto after_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* try to extract an ID */
|
||||||
|
found_id:
|
||||||
|
p = 0;
|
||||||
|
switch(*str) {
|
||||||
|
case ':':
|
||||||
|
if(str[1] == ' ') goto after_id;
|
||||||
|
/* fall through */
|
||||||
|
case '.':
|
||||||
|
*str++ = 0;
|
||||||
|
msg->id = str;
|
||||||
|
for(; *str && *str != ':' && *str != ' '; ++str) { }
|
||||||
|
goto after_id;
|
||||||
|
|
||||||
|
case '[':
|
||||||
|
p = strchr(str, ']');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '{':
|
||||||
|
p = strchr(str, '}');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '<':
|
||||||
|
p = strchr(str, '>');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!p) return str;
|
||||||
|
|
||||||
|
*str++ = 0;
|
||||||
|
msg->id = str;
|
||||||
|
*p = 0;
|
||||||
|
str = p + 1;
|
||||||
|
if(*str == ':') ++str;
|
||||||
|
if(*str == ' ') ++str;
|
||||||
|
return str;
|
||||||
|
|
||||||
|
after_id:
|
||||||
|
if(!*str) return str;
|
||||||
|
*str++ = 0;
|
||||||
|
if(*str == ' ') ++str;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
syslog_parse(struct syslog_msg* msg, char* str)
|
||||||
|
{
|
||||||
|
int version = 0, len;
|
||||||
|
|
||||||
|
memset(msg, 0, sizeof(struct syslog_msg));
|
||||||
|
str = syslog_parse_pri(msg, str);
|
||||||
|
|
||||||
|
/* check for an RFC5424 version 1 message */
|
||||||
|
if(!strncmp(str, "1 ", 2)) {
|
||||||
|
str += 2;
|
||||||
|
version = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
str = syslog_parse_date(msg, str);
|
||||||
|
str = syslog_parse_spacefield(&msg->hostname, str);
|
||||||
|
|
||||||
|
if(version == 1) {
|
||||||
|
str = syslog_parse_spacefield(&msg->tag, str);
|
||||||
|
str = syslog_parse_spacefield(&msg->id, str);
|
||||||
|
} else {
|
||||||
|
str = syslog_parse_tag_id(msg, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get message part, removing any trailing \n */
|
||||||
|
len = strlen(str);
|
||||||
|
if(len && str[len - 1] == '\n') str[len - 1] = 0;
|
||||||
|
msg->msg = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const char*
|
||||||
|
syslog_level_name(int level)
|
||||||
|
{
|
||||||
|
switch(level) {
|
||||||
|
case LOG_EMERG: return "emergency";
|
||||||
|
case LOG_ALERT: return "alert";
|
||||||
|
case LOG_CRIT: return "critical";
|
||||||
|
case LOG_ERR: return "error";
|
||||||
|
case LOG_WARNING: return "warning";
|
||||||
|
case LOG_NOTICE: return "notice";
|
||||||
|
case LOG_INFO: return "info";
|
||||||
|
case LOG_DEBUG: return "debug";
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const char*
|
||||||
|
syslog_facility_name(int facility)
|
||||||
|
{
|
||||||
|
switch(facility) {
|
||||||
|
case LOG_AUTH:
|
||||||
|
case LOG_AUTHPRIV: return "auth";
|
||||||
|
case LOG_CRON: return "cron";
|
||||||
|
case LOG_DAEMON: return "daemon";
|
||||||
|
case LOG_FTP: return "ftp";
|
||||||
|
case LOG_KERN: return "kernel";
|
||||||
|
case LOG_LOCAL0: return "local0";
|
||||||
|
case LOG_LOCAL1: return "local1";
|
||||||
|
case LOG_LOCAL2: return "local2";
|
||||||
|
case LOG_LOCAL3: return "local3";
|
||||||
|
case LOG_LOCAL4: return "local4";
|
||||||
|
case LOG_LOCAL5: return "local5";
|
||||||
|
case LOG_LOCAL6: return "local6";
|
||||||
|
case LOG_LOCAL7: return "local7";
|
||||||
|
case LOG_LPR: return "printer";
|
||||||
|
case LOG_MAIL: return "mail";
|
||||||
|
case LOG_NEWS: return "news";
|
||||||
|
case LOG_SYSLOG: return "syslog";
|
||||||
|
case LOG_USER: return "user";
|
||||||
|
case LOG_UUCP: return "uucp";
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* options for text editors
|
||||||
|
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
||||||
|
vim: expandtab:ts=4:sw=4
|
||||||
|
*/
|
|
@ -0,0 +1,171 @@
|
||||||
|
/* libsyslogp/src/libsyslogp/500_syslogp.h
|
||||||
|
*
|
||||||
|
* (c)2009, Laurence Withers, <l@lwithers.me.uk>.
|
||||||
|
* Released under the GNU GPLv3. See file COPYING or
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \defgroup syslogp syslog message parser
|
||||||
|
|
||||||
|
This set of routines allows parsing of a syslog message and processing of the
|
||||||
|
result. \ref syslog_parse() is called on the raw message data and the component
|
||||||
|
parts of the message are then stored in a struct \ref syslog_msg.
|
||||||
|
|
||||||
|
Note: BSD-syslog style messages don't generally contain a year in the timestamp
|
||||||
|
field. This means that \ref syslog_parse() calls \c time(3) to find the current
|
||||||
|
year whenever it parses such a message. This is expensive and, potentially,
|
||||||
|
incaccurate. You can set the year implied in such messages with
|
||||||
|
\ref syslog_parse_set_implied_year().
|
||||||
|
|
||||||
|
*/
|
||||||
|
/*!@{*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Parsed syslog message.
|
||||||
|
|
||||||
|
The component parts of an RFC3164 syslog message. The MSGID and SD (structured
|
||||||
|
data) parts of an RFC5424 syslog message will not be parsed and will be
|
||||||
|
considered part of the \a msg text.
|
||||||
|
|
||||||
|
*/
|
||||||
|
struct syslog_msg {
|
||||||
|
/*! \brief Message severity level.
|
||||||
|
|
||||||
|
This is one of the \c LOG_INFO/LOG_WARNING etc. types from \c syslog(3). It
|
||||||
|
can be turned into a friendly name via \ref syslog_level_name(). It will be
|
||||||
|
set to \c LOG_NOTICE if the priority field could not be recovered from the
|
||||||
|
original message.
|
||||||
|
|
||||||
|
*/
|
||||||
|
int level;
|
||||||
|
|
||||||
|
/*! \brief Message facility.
|
||||||
|
|
||||||
|
This is one of the \c LOG_DAEMON/LOG_LOCAL0 etc. types from \c syslog(3). It
|
||||||
|
can be turned into a friendly name via \ref syslog_facility_name(). It will
|
||||||
|
be set to \c LOG_USER if the priority field could not be recovered from the
|
||||||
|
original message.
|
||||||
|
|
||||||
|
*/
|
||||||
|
int facility;
|
||||||
|
|
||||||
|
/*! \brief Timestamp.
|
||||||
|
|
||||||
|
The timestamp of the message. It will be all 0 if the timestamp could not
|
||||||
|
be recovered from the original message.
|
||||||
|
|
||||||
|
*/
|
||||||
|
struct iso8601_date timestamp;
|
||||||
|
|
||||||
|
/*! \brief Hostname.
|
||||||
|
|
||||||
|
The hostname as specified by the producing application. If this could not be
|
||||||
|
recovered, it will be set to 0.
|
||||||
|
|
||||||
|
*/
|
||||||
|
char* hostname;
|
||||||
|
|
||||||
|
/*! \brief Producing application name (tag).
|
||||||
|
|
||||||
|
The name (or tag) of the application which produced the original message. If
|
||||||
|
this could not be recovered, it will be set to 0.
|
||||||
|
|
||||||
|
*/
|
||||||
|
char* tag;
|
||||||
|
|
||||||
|
/*! \brief Producing application instance ID.
|
||||||
|
|
||||||
|
Many (but not all) RFC3164 messages place an application instance ID in
|
||||||
|
square brackets immediately after the tag. RFC5424 has a field specifically
|
||||||
|
for this purpose. If discovered, this will point to the data in that field.
|
||||||
|
If not, it will be 0.
|
||||||
|
|
||||||
|
*/
|
||||||
|
char* id;
|
||||||
|
|
||||||
|
/*! \brief Message text.
|
||||||
|
|
||||||
|
This points to the remaining message text once the header and tag have been
|
||||||
|
stripped off. It may be 0 or a pointer to an empty string if the message was
|
||||||
|
truly empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
char* msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Parse a syslog message.
|
||||||
|
|
||||||
|
\param[out] msg Structure into which parsed message data is written.
|
||||||
|
\param str Pointer to message string. Null terminated. Will be altered!
|
||||||
|
|
||||||
|
This function parses the syslog message written into \a str, which is a null
|
||||||
|
terminated C string containing the syslog message. \a str will be altered by
|
||||||
|
this process (e.g. some characters will be overwritten with ASCII NULs). The
|
||||||
|
parsed message data is written into \a msg.
|
||||||
|
|
||||||
|
\a msg contains several string pointers (such as \a tag, \a id and \a msg).
|
||||||
|
After parsing, these will be set to be pointers into \a str. Each component
|
||||||
|
will be null-terminated (which is why \a str will be altered).
|
||||||
|
|
||||||
|
*/
|
||||||
|
void syslog_parse(struct syslog_msg* msg, char* str)
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
__attribute__((nonnull))
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Set year implied in BSD-style timestamps.
|
||||||
|
|
||||||
|
\param year Year to assume (or 0 to query the system clock).
|
||||||
|
|
||||||
|
BSD-style timestamps do not contain the current year. libsyslogp will thus query
|
||||||
|
the system clock when it is trying to parse such a timestamp. This is expensive
|
||||||
|
and, potentially, inaccurate. This function can be used to set the implied year.
|
||||||
|
|
||||||
|
*/
|
||||||
|
void syslog_parse_set_implied_year(int year);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Get friendly name for syslog level.
|
||||||
|
|
||||||
|
\param level \c syslog(3) log level constant.
|
||||||
|
\returns Pointer to string constant.
|
||||||
|
\retval 0 if \a level is invalid.
|
||||||
|
|
||||||
|
This function interprets a \c syslog(3) log level constant (such as \c LOG_INFO
|
||||||
|
or \c LOG_WARNING) and returns a friendly string ("info", "warning"). If the
|
||||||
|
value of \a level is invalid, it returns a null pointer.
|
||||||
|
|
||||||
|
*/
|
||||||
|
const char* syslog_level_name(int level);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Get friendly name for syslog facility.
|
||||||
|
|
||||||
|
\param facility \c syslog(3) log facility constant.
|
||||||
|
\returns Pointer to string constant.
|
||||||
|
\retval 0 if \a facility is invalid.
|
||||||
|
|
||||||
|
This function interprets a \c syslog(3) log facility constant (such as
|
||||||
|
\c LOG_DAEMON or \c LOG_USER) and returns a friendly string ("daemon", "user").
|
||||||
|
If the value of \a facility is invalid, it returns a null pointer.
|
||||||
|
|
||||||
|
*/
|
||||||
|
const char* syslog_facility_name(int facility);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*!@}*/
|
||||||
|
/* options for text editors
|
||||||
|
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
||||||
|
vim: expandtab:ts=4:sw=4:syntax=c.doxygen
|
||||||
|
*/
|
|
@ -12,9 +12,9 @@ then
|
||||||
source src/libsyslogp/soversion
|
source src/libsyslogp/soversion
|
||||||
|
|
||||||
libsyslogp="obj/${libsyslogp_BASE}.so.${SOMAJOR}.${SOMICRO}"
|
libsyslogp="obj/${libsyslogp_BASE}.so.${SOMAJOR}.${SOMICRO}"
|
||||||
libsyslogp_DEP_CFLAGS="" # @TODO@ cflags
|
libsyslogp_DEP_CFLAGS="${LIBISO8601_CFLAGS}"
|
||||||
libsyslogp_DEP_LIBS="" # @TODO@ libs
|
libsyslogp_DEP_LIBS="${LIBISO8601_LIBS}"
|
||||||
SO_EXTRA="${libsyslogp_DEP_CFLAGS} ${libsyslogp_DEP_LIBS} -lc"
|
SO_EXTRA="-D_GNU_SOURCE -std=gnu99 ${libsyslogp_DEP_CFLAGS} ${libsyslogp_DEP_LIBS} -lc"
|
||||||
|
|
||||||
echo "Building library ${libsyslogp}..."
|
echo "Building library ${libsyslogp}..."
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ build_target libsyslogp || return 1
|
||||||
if [ -z ${tests_BUILT} ]
|
if [ -z ${tests_BUILT} ]
|
||||||
then
|
then
|
||||||
LIBS="${libsyslogp} ${libsyslogp_DEP_CFLAGS} ${libsyslogp_DEP_LIBS} "
|
LIBS="${libsyslogp} ${libsyslogp_DEP_CFLAGS} ${libsyslogp_DEP_LIBS} "
|
||||||
EXTRAS="" # @TODO@ libs, cflags
|
EXTRAS="-D_GNU_SOURCE -std=gnu99"
|
||||||
|
|
||||||
echo "Building test programs..."
|
echo "Building test programs..."
|
||||||
do_cmd mkdir -p obj/tests || return 1
|
do_cmd mkdir -p obj/tests || return 1
|
||||||
|
|
|
@ -0,0 +1,230 @@
|
||||||
|
/* libsyslogp/src/tests/regr.c
|
||||||
|
*
|
||||||
|
* (c)2009, Laurence Withers, <l@lwithers.me.uk>.
|
||||||
|
* Released under the GNU GPLv3. See file COPYING or
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "syslogp.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <iso8601.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define ISO_DAY_20081106 (733717)
|
||||||
|
#define ISO_SEC_1700 (17 * 3600)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const struct syslog_msg exp_simplecron = {
|
||||||
|
.level = LOG_NOTICE,
|
||||||
|
.facility = LOG_USER,
|
||||||
|
.timestamp = {
|
||||||
|
.day = ISO_DAY_20081106,
|
||||||
|
.sec = ISO_SEC_1700,
|
||||||
|
},
|
||||||
|
.hostname = "amethyst",
|
||||||
|
.tag = "cron",
|
||||||
|
.id = "31311",
|
||||||
|
.msg = "(root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const struct syslog_msg exp_rfc5424cron = {
|
||||||
|
.level = LOG_NOTICE,
|
||||||
|
.facility = LOG_USER,
|
||||||
|
.timestamp = {
|
||||||
|
.day = ISO_DAY_20081106,
|
||||||
|
.sec = ISO_SEC_1700,
|
||||||
|
},
|
||||||
|
.hostname = "amethyst",
|
||||||
|
.tag = "cron",
|
||||||
|
.id = "31311",
|
||||||
|
.msg = "- (root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct test {
|
||||||
|
const char* name;
|
||||||
|
const char* orig;
|
||||||
|
char* string;
|
||||||
|
const struct syslog_msg* exp;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct test
|
||||||
|
tests[] = {
|
||||||
|
{
|
||||||
|
.name = "bsd",
|
||||||
|
.orig = "<13>Nov 6 17:00:00 amethyst cron[31311]: (root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)",
|
||||||
|
.exp = &exp_simplecron,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "space_bsd",
|
||||||
|
.orig = "<13> Nov 6 17:00:00 amethyst cron[31311]: (root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)",
|
||||||
|
.exp = &exp_simplecron,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "bsd_newline",
|
||||||
|
.orig = "<13>Nov 6 17:00:00 amethyst cron[31311]: (root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)\n",
|
||||||
|
.exp = &exp_simplecron,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "nopri_bsd_newline",
|
||||||
|
.orig = "Nov 6 17:00:00 amethyst cron[31311]: (root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)\n",
|
||||||
|
.exp = &exp_simplecron,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "bsd_secfrac",
|
||||||
|
.orig = "<13>Nov 6 17:00:00.000 amethyst cron[31311]: (root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)\n",
|
||||||
|
.exp = &exp_simplecron,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "linksys",
|
||||||
|
.orig = "<13>Nov 6 17:00:00 2008 amethyst cron[31311]: (root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)\n",
|
||||||
|
.exp = &exp_simplecron,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "space_linksys",
|
||||||
|
.orig = "<13> Nov 6 17:00:00 2008 amethyst cron[31311]: (root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)\n",
|
||||||
|
.exp = &exp_simplecron,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "pix",
|
||||||
|
.orig = "<13>Nov 6 2008 17:00:00 amethyst cron[31311]: (root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)\n",
|
||||||
|
.exp = &exp_simplecron,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "space_pix",
|
||||||
|
.orig = "<13> Nov 6 2008 17:00:00 amethyst cron[31311]: (root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)\n",
|
||||||
|
.exp = &exp_simplecron,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "iso",
|
||||||
|
.orig = "<13>2008-11-06T17:00:00Z amethyst cron[31311]: (root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)\n",
|
||||||
|
.exp = &exp_simplecron,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "_iso",
|
||||||
|
.orig = "<13> 2008-11-06T17:00:00Z amethyst cron[31311]: (root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)\n",
|
||||||
|
.exp = &exp_simplecron,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "rfc5424",
|
||||||
|
.orig = "<13>1 2008-11-06T17:00:00Z amethyst cron 31311 - (root) CMD (rm -f /var/spool/cron/lastrun/cron.hourly)\n",
|
||||||
|
.exp = &exp_rfc5424cron,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
compare_msg(const struct syslog_msg* a, const struct syslog_msg* b)
|
||||||
|
{
|
||||||
|
if(a->level != b->level) return -1;
|
||||||
|
if(a->facility != b->facility) return -1;
|
||||||
|
if(iso8601_cmp(&a->timestamp, &b->timestamp)) return -1;
|
||||||
|
#define CMP_STR(str) do { \
|
||||||
|
if((a->str && !b->str) || (b->str && !a->str)) return -1; \
|
||||||
|
if(a->str && strcmp(a->str, b->str)) return -1; \
|
||||||
|
}while(0)
|
||||||
|
CMP_STR(hostname);
|
||||||
|
CMP_STR(tag);
|
||||||
|
CMP_STR(id);
|
||||||
|
CMP_STR(msg);
|
||||||
|
#undef CMP_STR
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct iso8601_details
|
||||||
|
iso_details = {
|
||||||
|
.date_prec = iso8601_prec_day,
|
||||||
|
.time_prec = iso8601_prec_secfrac,
|
||||||
|
.extended = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
dump_msg(const char* label, const struct syslog_msg* msg)
|
||||||
|
{
|
||||||
|
char isodate[40];
|
||||||
|
|
||||||
|
printf("======== %s ========\n"
|
||||||
|
"Level : %d (%s)\n"
|
||||||
|
"Facility : %d (%s)\n"
|
||||||
|
"Timestamp: %s\n"
|
||||||
|
"Hostname : '%s'\n"
|
||||||
|
"Tag : '%s'\n"
|
||||||
|
"ID : '%s'\n"
|
||||||
|
"Message : “%s”\n\n",
|
||||||
|
label,
|
||||||
|
msg->level, syslog_level_name(msg->level),
|
||||||
|
msg->facility, syslog_facility_name(msg->facility),
|
||||||
|
iso8601_print(isodate, sizeof(isodate), &msg->timestamp, &iso_details),
|
||||||
|
msg->hostname, msg->tag, msg->id, msg->msg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct test* test;
|
||||||
|
struct syslog_msg msg_result;
|
||||||
|
|
||||||
|
if(argc == 2 && !strcmp(argv[1], "--print-summary")) {
|
||||||
|
fputs("Automated regression test.\n", stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
syslog_parse_set_implied_year(2008);
|
||||||
|
|
||||||
|
for(test = tests; test->name; ++test) {
|
||||||
|
/* operate on a copy of the string (constant is read only) */
|
||||||
|
test->string = strdup(test->orig);
|
||||||
|
syslog_parse(&msg_result, test->string);
|
||||||
|
|
||||||
|
/* compare the result, testing for and dumping errors */
|
||||||
|
ret = compare_msg(&msg_result, test->exp);
|
||||||
|
printf("%s: %s\n", test->name, ret ? "FAILED" : "passed");
|
||||||
|
|
||||||
|
if(ret) {
|
||||||
|
printf("*** Test failed while parsing:\n“%s”\n\n", test->orig);
|
||||||
|
dump_msg("Expected result", test->exp);
|
||||||
|
dump_msg("Actual result", &msg_result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* options for text editors
|
||||||
|
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
||||||
|
vim: expandtab:ts=4:sw=4
|
||||||
|
*/
|
|
@ -0,0 +1,74 @@
|
||||||
|
/* libsyslogp/src/tests/stdin_parser.c
|
||||||
|
*
|
||||||
|
* (c)2009, Laurence Withers, <l@lwithers.me.uk>.
|
||||||
|
* Released under the GNU GPLv3. See file COPYING or
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "syslogp.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct iso8601_details
|
||||||
|
iso_details = {
|
||||||
|
.date_prec = iso8601_prec_day,
|
||||||
|
.time_prec = iso8601_prec_secfrac,
|
||||||
|
.extended = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
test_parse(char* ln)
|
||||||
|
{
|
||||||
|
struct syslog_msg msg;
|
||||||
|
char isodate[40];
|
||||||
|
|
||||||
|
syslog_parse(&msg, ln);
|
||||||
|
|
||||||
|
printf("\n"
|
||||||
|
"Level : %d (%s)\n"
|
||||||
|
"Facility : %d (%s)\n"
|
||||||
|
"Timestamp: %s\n"
|
||||||
|
"Hostname : '%s'\n"
|
||||||
|
"Tag : '%s'\n"
|
||||||
|
"ID : '%s'\n"
|
||||||
|
"Message : “%s”\n",
|
||||||
|
msg.level, syslog_level_name(msg.level),
|
||||||
|
msg.facility, syslog_facility_name(msg.facility),
|
||||||
|
iso8601_print(isodate, sizeof(isodate), &msg.timestamp, &iso_details),
|
||||||
|
msg.hostname, msg.tag, msg.id, msg.msg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
char* ln = 0;
|
||||||
|
size_t lnsize = 0;
|
||||||
|
ssize_t rd;
|
||||||
|
|
||||||
|
if(argc == 2 && !strcmp(argv[1], "--print-summary")) {
|
||||||
|
fputs("Parses syslog lines presented on stdin.\n", stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(!feof(stdin)) {
|
||||||
|
rd = getline(&ln, &lnsize, stdin);
|
||||||
|
if(rd == -1) return 0;
|
||||||
|
test_parse(ln);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* options for text editors
|
||||||
|
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
||||||
|
vim: expandtab:ts=4:sw=4
|
||||||
|
*/
|
Loading…
Reference in New Issue