#include <stdlib.h>
#include <time.h>
#include <assert.h>
+#include <limits.h>
+#include <ctype.h>
/**
* @addtogroup SHARED_FUNCTIONS
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, ×tamp },
+ { 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), ×tamp, pid, msg);
+
+ return 0;
+}
+
/**
* @brief Reception timestamp
* @details Add a timestamp describing reception time.
--- /dev/null
+#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;
+}