Initial commit of libsyslogp

This commit is contained in:
Laurence Withers 2009-11-06 17:32:50 +00:00
parent ddc2c91660
commit 87cc7a8e14
10 changed files with 971 additions and 5 deletions

3
config
View File

@ -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"

View File

@ -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.
*/ */

View File

@ -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;

View File

@ -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;

View File

@ -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
*/

View File

@ -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
*/

View File

@ -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}..."

View File

@ -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

230
src/tests/regr.c Normal file
View File

@ -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
*/

74
src/tests/stdin_parser.c Normal file
View File

@ -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
*/