added syslog message parser 30/120830/22
authorRafal Pietruch <r.pietruch@samsung.com>
Fri, 24 Mar 2017 08:34:44 +0000 (09:34 +0100)
committerKarol Lewandowski <k.lewandowsk@samsung.com>
Mon, 24 Apr 2017 16:37:07 +0000 (16:37 +0000)
Change-Id: I7a6d986f8504e1d979779a061e8b0da29e4dfaff

Makefile.am
include/queued_entry.h
src/logger/logger.c
src/shared/queued_entry.c
src/tests/syslog_parser.c [new file with mode: 0644]

index 4fb834b..c02d572 100644 (file)
@@ -87,6 +87,7 @@ TESTS = \
        src/tests/test_ptrs_list \
        src/tests/config \
        src/tests/kmsg_parser \
+       src/tests/syslog_parser \
        src/tests/pipe_message \
        src/tests/filters
 
@@ -94,6 +95,7 @@ check_PROGRAMS = \
        src/tests/test_ptrs_list \
        src/tests/config \
        src/tests/kmsg_parser \
+       src/tests/syslog_parser \
        src/tests/pipe_message \
        src/tests/filters
 
@@ -109,6 +111,10 @@ src_tests_kmsg_parser_SOURCES = src/tests/kmsg_parser.c src/shared/queued_entry.
 src_tests_kmsg_parser_CFLAGS = $(AM_CFLAGS)
 src_tests_kmsg_parser_LDFLAGS = $(AM_LDFLAGS)
 
+src_tests_syslog_parser_SOURCES = src/tests/syslog_parser.c src/shared/queued_entry.c src/shared/logprint.c
+src_tests_syslog_parser_CFLAGS = $(AM_CFLAGS)
+src_tests_syslog_parser_LDFLAGS = $(AM_LDFLAGS)
+
 src_tests_pipe_message_SOURCES = src/tests/pipe_message.c src/shared/queued_entry.c
 src_tests_pipe_message_CFLAGS = $(AM_CFLAGS)
 src_tests_pipe_message_LDFLAGS = $(AM_LDFLAGS)
index f5a935a..37477af 100644 (file)
@@ -48,6 +48,7 @@ struct android_logger_entry {
 
 void create_pipe_message(void * buf, int prio, char const * tag, char const * msg);
 int parse_kmsg_message(char * buffer, int buffer_size);
+int parse_syslog_datagram(const char * buffer, int buffer_size, struct logger_entry * le);
 void parse_androidlogger_message(struct android_logger_entry * const ale, struct logger_entry * const le, const int dgram_size);
 void add_recv_timestamp(struct logger_entry * const le);
 
index 0f405fd..757f79a 100644 (file)
@@ -1548,11 +1548,6 @@ static int cond_service_reader(void *ptr, void *user_data)
        return FALSE;
 }
 
-static int parse_syslog_datagram(char * buffer, int buffer_size, struct logger_entry * le)
-{
-       return 0; /* TODO: Implement */
-}
-
 /**
  * @brief Service syslog
  * @details Read from the syslog socket
index 3f68741..0612c31 100644 (file)
@@ -23,6 +23,8 @@
 #include <stdlib.h>
 #include <time.h>
 #include <assert.h>
+#include <limits.h>
+#include <ctype.h>
 
 /**
  * @addtogroup SHARED_FUNCTIONS
@@ -168,6 +170,277 @@ void parse_androidlogger_message(struct android_logger_entry * const ale, struct
        le->len = dgram_size + 16;
 }
 
+enum syslog_priority {
+       SYSLOG_EMERGENCY = 0,
+       SYSLOG_ALERT,
+       SYSLOG_CRITICAL,
+       SYSLOG_ERROR,
+       SYSLOG_WARNING,
+       SYSLOG_NOTICE,
+       SYSLOG_INFO,
+       SYSLOG_DEBUG,
+};
+
+static const int syslog_priority_mapping[] = {
+       [SYSLOG_EMERGENCY] = DLOG_FATAL,
+       [SYSLOG_ALERT]     = DLOG_FATAL,
+       [SYSLOG_CRITICAL]  = DLOG_FATAL,
+       [SYSLOG_ERROR]     = DLOG_ERROR,
+       [SYSLOG_WARNING]   = DLOG_WARN,
+       [SYSLOG_NOTICE]    = DLOG_INFO,
+       [SYSLOG_INFO]      = DLOG_INFO,
+       [SYSLOG_DEBUG]     = DLOG_DEBUG,
+};
+
+/**
+ * @brief Translate syslog priority
+ * @details Maps syslog priority to DLog priority
+ * @param[in] prio Syslog priority
+ * @return DLog priority
+ */
+static int translate_syslog_priority(int prio)
+{
+       prio &= 7; // syslog priority is encoded on the three lowest bits.
+
+       return syslog_priority_mapping[prio];
+}
+
+static const char *syslog_facilities[] = {
+       "SYSLOG-KERN",
+       "SYSLOG-USER",
+       "SYSLOG-MAIL",
+       "SYSLOG-DAEMON",
+       "SYSLOG-AUTH",
+       "SYSLOG-SYSLOG",
+       "SYSLOG-LPR",
+       "SYSLOG-NEWS",
+       "SYSLOG-UUCP",
+       "SYSLOG-CRON",
+       "SYSLOG-AUTHPRIV",
+       "SYSLOG-SYSTEM",
+       "SYSLOG-SYSTEM",
+       "SYSLOG-SYSTEM",
+       "SYSLOG-SYSTEM",
+       "SYSLOG-SYSTEM",
+       "SYSLOG-LOCAL0",
+       "SYSLOG-LOCAL1",
+       "SYSLOG-LOCAL2",
+       "SYSLOG-LOCAL3",
+       "SYSLOG-LOCAL4",
+       "SYSLOG-LOCAL5",
+       "SYSLOG-LOCAL6",
+       "SYSLOG-LOCAL7",
+};
+
+/**
+ * @brief Translate syslog facility
+ * @details Translates the integer representation of a syslog facility into a string
+ * @param[in] facility The facility (as provided by syslog)
+ * @return String representation of the facility name
+ */
+static const char *translate_syslog_facility(int facility)
+{
+       facility >>= 3; // first three bits are priority, facility is encoded on the remaining higher bits
+
+       if (facility < 0 ||
+           facility >= NELEMS(syslog_facilities))
+               return "SYSLOG-UNKNOWN";
+
+       return syslog_facilities[facility];
+}
+
+/**
+ * @brief Parse syslog date
+ * @details Parses timestamp from a syslog message
+ * @param[in,out] next The beginning of the section, will contain the beginning of the next section
+ * @param[out] timestamp The timestamp
+ * @returns 0 on success, -errno on failure
+ */
+static int parse_syslog_date(char **next, struct tm *timestamp)
+{
+       memset(timestamp, 0, sizeof *timestamp);
+
+       char * const begin = *next;
+       char * const end = strptime(begin, "%h %e %T", timestamp);
+       if (!end)
+               return -EINVAL;
+
+       *next = end;
+       return 0;
+}
+
+/**
+ * @brief Construct log from syslog datagram
+ * @details Constructs a logger entry from data contained in a syslog datagram.
+ * @param[out] le The logger entry structure to construct
+ * @param[in] priority Priority
+ * @param[in] tag Tag
+ * @param[in] timestamp Timestamp
+ * @param[in] pid Process ID
+ * @param msg Message
+ */
+static void construct_logger_entry_from_syslog(struct logger_entry *le, int priority, const char *tag, struct tm *timestamp, int pid, const char *msg)
+{
+       const int tag_len = strlen(tag) + 1;
+       const int msg_len = strlen(msg) + 1;
+
+       le->len = sizeof(struct logger_entry) + tag_len + msg_len + 1;
+       le->pid = pid;
+       le->tid = 0; // could be set to PID but this way it's explicit the TID is not known
+       le->sec = mktime(timestamp);
+       le->nsec = 0; // the timestamp does not have such precision
+       le->msg[0] = priority;
+
+       char * const tag_begin = le->msg + 1;
+       char * const msg_begin = tag_begin + tag_len;
+       memcpy(tag_begin, tag, tag_len);
+       memcpy(msg_begin, msg, msg_len);
+}
+
+static int parse_const_char(const char **cursor, void *user_data)
+{
+       if (strlen(*cursor) <= 0 || **cursor != *((char *)user_data))
+               return -EINVAL;
+       ++(*cursor);
+       return 0;
+}
+
+static int parse_number(const char **cursor, void *user_data)
+{
+       char *endptr;
+       errno = 0;
+       int val = strtol(*cursor, &endptr, 10);
+       if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
+               || (errno != 0 && val == 0)) {
+               return -EINVAL;
+       }
+       if (endptr == *cursor) {
+               return -EINVAL;
+       }
+       *cursor = endptr;
+       int *pid = (int *)user_data;
+       if (pid)
+               *pid = val;
+       return 0;
+}
+
+static int parse_timestamp(const char **cursor, void *user_data)
+{
+       if (strlen(*cursor) <= 0)
+               return -EINVAL;
+       return parse_syslog_date((char **)cursor, (struct tm *)user_data);
+}
+
+static int skip_name(const char **cursor, void *user_data)
+{
+       if (strlen(*cursor) <= 0)
+               return -EINVAL;
+       if (!isalpha(**cursor))
+               return -EINVAL;
+       do
+               ++(*cursor);
+       while (isalpha(**cursor));
+       return 0;
+}
+
+static int parse_message(const char **cursor, void *user_data)
+{
+       if (strlen(*cursor) <= 0)
+               return -EINVAL;
+       const char **msg = (const char **)user_data;
+       *msg = *cursor;
+       return 0;
+}
+
+
+struct parser {
+       int (*parse)(const char **cursor, void *user_data);
+       void *user_data;
+};
+
+struct parsers {
+       struct parser *parsers;
+       int count;
+};
+
+static int parse(const char **cursor, struct parsers *parsers)
+{
+       assert(parsers);
+       int i;
+       for (i = 0; i < parsers->count; ++i) {
+               int err = parsers->parsers[i].parse(cursor, parsers->parsers[i].user_data);
+               if (err != 0)
+                       return err;
+       }
+       return 0;
+}
+
+static int parse_optional(const char **cursor, void *user_data)
+{
+       struct parsers *optional_parsers = (struct parsers *)user_data;
+       assert(optional_parsers->count > 0);
+       struct parser *first_parser = optional_parsers->parsers;
+       assert(first_parser);
+       int err = first_parser->parse(cursor, first_parser->user_data);
+       if (err != 0)
+               return 0;
+
+       struct parsers mandatory_parsers = (struct parsers) {
+               .parsers = first_parser + 1,
+               .count = optional_parsers->count - 1
+       };
+       return parse(cursor, &mandatory_parsers);
+}
+
+/**
+ * @brief Parse syslog message
+ * @details Interprets a message received from syslog
+ * @param[in] buffer The buffer which contains the message to parse
+ * @param[in] buffer_size The size of the received datagram
+ * @param[out] le The logger entry to fill
+ * @return 0 on failure, 1 on success
+ */
+int parse_syslog_datagram(const char *buffer, int buffer_size, struct logger_entry * le)
+{
+       int fac_prio, pid = 0;
+       struct tm timestamp;
+       const char *msg;
+
+       struct parser parsePid[] = {
+               { parse_const_char, "[" },
+               { parse_number, &pid },
+               { parse_const_char, "]" },
+       };
+       struct parsers optionalPid = (struct parsers) {
+               .parsers = parsePid,
+               .count = NELEMS(parsePid)
+       };
+       struct parser parseSyslog[] = {
+               { parse_const_char, "<" },
+               { parse_number, &fac_prio },
+               { parse_const_char, ">" },
+               { parse_timestamp, &timestamp },
+               { parse_const_char, " " },
+               { skip_name, NULL },
+               { parse_optional, &optionalPid },
+               { parse_const_char, ":" },
+               { parse_const_char, " " },
+               { parse_message, &msg }
+       };
+       struct parsers syslogParsers = (struct parsers) {
+               .parsers = parseSyslog,
+               .count = NELEMS(parseSyslog)
+       };
+       const char *cursor = buffer;
+       int err = parse(&cursor, &syslogParsers);
+       if (err != 0)
+               return err;
+
+       construct_logger_entry_from_syslog(le, translate_syslog_priority(fac_prio), translate_syslog_facility(fac_prio), &timestamp, pid, msg);
+
+       return 0;
+}
+
 /**
  * @brief Reception timestamp
  * @details Add a timestamp describing reception time.
diff --git a/src/tests/syslog_parser.c b/src/tests/syslog_parser.c
new file mode 100644 (file)
index 0000000..e3a1edc
--- /dev/null
@@ -0,0 +1,66 @@
+#include <logprint.h>
+#include <queued_entry.h>
+#include <stdio.h>
+#include <assert.h>
+
+int main()
+{
+       char buffer[1024];
+       log_entry le;
+       int len;
+       struct logger_entry * sle = alloca(1024);
+
+       // No priority delimiter '>'.
+       len = snprintf(buffer, sizeof buffer, "<10 Jan 02 03:04:05 progname: msg");
+       assert(parse_syslog_datagram(buffer, len, sle));
+
+       // No priority.
+       len = snprintf(buffer, sizeof buffer, "<>Jan 02 03:04:05 progname: msg");
+       assert(parse_syslog_datagram(buffer, len, sle));
+
+       // Priority is garbage.
+       len = snprintf(buffer, sizeof buffer, "<XX>Jan 02 03:04:05 progname: msg");
+       assert(parse_syslog_datagram(buffer, len, sle));
+
+       // No progname
+       len = snprintf(buffer, sizeof buffer, "<10>Jan 02 03:04:05 ");
+       assert(parse_syslog_datagram(buffer, len, sle));
+
+       // Progname cut
+       len = snprintf(buffer, sizeof buffer, "<10>Jan 02 03:04:05 prog");
+       assert(parse_syslog_datagram(buffer, len, sle));
+
+       // Pid not a number
+       len = snprintf(buffer, sizeof buffer, "<10>Jan 02 03:04:05 progname[pid]: msg");
+       assert(parse_syslog_datagram(buffer, len, sle));
+
+       // Pid bracket not closed
+       len = snprintf(buffer, sizeof buffer, "<10>Jan 02 03:04:05 progname[678: msg");
+       assert(parse_syslog_datagram(buffer, len, sle));
+
+       // No message
+       len = snprintf(buffer, sizeof buffer, "<10>Jan 02 03:04:05 progname:");
+       assert(parse_syslog_datagram(buffer, len, sle));
+
+       // Valid
+       len = snprintf(buffer, sizeof buffer, "<10>Jan 02 03:04:05 progname[678]: msg");
+       assert(!parse_syslog_datagram(buffer, len, sle));
+       log_process_log_buffer(sle, & le);
+
+       assert(le.pid == 678);
+       assert(le.priority == DLOG_FATAL);
+       assert(!strcmp("SYSLOG-USER", le.tag));
+       assert(!strcmp("msg", le.message));
+
+       // Valid
+       len = snprintf(buffer, sizeof buffer, "<10>Jan 02 03:04:05 progname: msg");
+       assert(!parse_syslog_datagram(buffer, len, sle));
+       log_process_log_buffer(sle, & le);
+
+       assert(le.pid == 0);
+       assert(le.priority == DLOG_FATAL);
+       assert(!strcmp("SYSLOG-USER", le.tag));
+       assert(!strcmp("msg", le.message));
+
+       return 0;
+}