/*************************************************************************** * * Copyright (c) 1999 Balázs Scheidler * Copyright (c) 1999-2001 BalaBit IT Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Inspired by nsyslog, originally written by Darren Reed. * * $Id: log.c,v 1.32.4.1 2004/01/13 18:08:02 bazsi Exp $ * ***************************************************************************/ #include "log.h" #include "xalloc.h" #include "format.h" #include #include #include #include #include #include #define CLASS_DEFINE #include "log.h.x" #undef CLASS_DEFINE static char aix_fwd_string[] = "Message forwarded from "; static char repeat_msg_string[] = "last message repeated"; static void parse_log_msg(struct log_info *lm, UINT32 length, UINT8 *data, UINT8 *prefix, regex_t *hostname_re) { unsigned char *src; unsigned int left, pri, oldleft; time_t now = time(NULL); unsigned char *oldsrc; src = data; left = length; if (left && src[0] == '<') { src++; left--; pri = 0; while (left && *src != '>') { if (isdigit(*src)) { pri = pri * 10 + ((*src) - '0'); } else { lm->msg = c_format_cstring("unparseable log message: \"%s\"", length, data); lm->pri = LOG_SYSLOG | LOG_ERR; return; } src++; left--; } lm->pri = pri; if (left) { src++; left--; } } /* No priority info in the buffer? Just assign a default. */ else { lm->pri = LOG_USER | LOG_NOTICE; } while (left && *src == ' ') { /* Move past whitespace */ src++; left--; } /* If the next chars look like a date, then read them as a date. */ if (left >= 15) { struct tm tm, *nowtm; if (left >= 21 && src[3] == ' ' && src[6] == ' ' && src[11] == ' ' && src[14] == ':' && src[17] == ':' && src[20] ==':') { /* PIX time stamp, format: MMM DD YYYY HH:MM:SS: */ src[20] = 0; memset(&tm, 0, sizeof(tm)); strptime((char *) src, "%b %e %Y %H:%M:%S", &tm); tm.tm_isdst = -1; lm->date = ol_string_alloc(16); strftime((char *) lm->date->data, 16, "%b %e %H:%M:%S", &tm); lm->date->length--; src[20] = ':'; src += 21; left -= 21; lm->stamp = mktime(&tm); } else if (src[3] == ' ' && src[6] == ' ' && src[9] == ':' && src[12] == ':') { /* Expected buffer format: MMM DD HH:MM:SS ... */ /* Just read the buffer data into a textual datestamp. */ lm->date = c_format_cstring("%s", 15, src); src += 15; left -= 15; /* And also make struct time timestamp for the msg */ nowtm = localtime(&now); memset(&tm, 0, sizeof(tm)); strptime((char *) lm->date->data, "%b %e %H:%M:%S", &tm); tm.tm_isdst = -1; tm.tm_year = nowtm->tm_year; if (tm.tm_mon > nowtm->tm_mon + 1) tm.tm_year--; lm->stamp = mktime(&tm); } } if (lm->date) { /* Expected format: hostname program[pid]: */ /* Possibly: Message forwarded from hostname: ... */ unsigned char *hostname_start = NULL; int hostname_len = 0; while (left && *src == ' ') { src++; /* skip whitespace */ left--; } /* Detect funny AIX syslogd forwarded message. */ if (left >= (sizeof(aix_fwd_string) - 1) && !memcmp(src, aix_fwd_string, sizeof(aix_fwd_string) - 1)) { oldsrc = src; oldleft = left; src += sizeof(aix_fwd_string) - 1; left -= sizeof(aix_fwd_string) - 1; hostname_start = src; hostname_len = 0; while (left && *src != ':') { src++; left--; hostname_len++; } while (left && (*src == ' ' || *src == ':')) { src++; /* skip whitespace */ left--; } } /* Now, try to tell if it's a "last message repeated" line */ if (left >= sizeof(repeat_msg_string) && !memcmp(src, repeat_msg_string, sizeof(repeat_msg_string) - 1)) { ; /* It is. Do nothing since there's no hostname or program name coming. */ } /* It's a regular ol' message. */ else { /* If we haven't already found the original hostname, look for it now. */ char hostname_buf[256]; int dst; oldsrc = src; oldleft = left; dst = 0; while (left && *src != ' ' && *src != ':' && *src != '[' && dst < sizeof(hostname_buf) - 1) { if (lm->flags & LF_CHECK_HOSTNAME && !((*src >= 'A' && *src <= 'Z') || (*src >= 'a' && *src <= 'z') || (*src >= '0' && *src <= '9') || *src == '-' || *src == '_' || *src == '.' || *src == ':' || *src == '@' || *src == '/')) { break; } hostname_buf[dst++] = *src; src++; left--; } hostname_buf[dst] = 0; if (left && *src == ' ' && (!hostname_re || regexec(hostname_re, hostname_buf, 0, NULL, 0))) { /* This was a hostname. It came from a syslog-ng, since syslogd doesn't send hostnames. It's even better then the one we got from the AIX fwd message, if we did. */ hostname_start = oldsrc; hostname_len = oldleft - left; } else { src = oldsrc; left = oldleft; } /* Skip whitespace. */ while (left && *src == ' ') { src++; left--; } /* Try to extract a program name */ oldsrc = src; oldleft = left; while (left && *src != ':' && *src != '[') { src++; left--; } if (left) { lm->program = c_format_cstring("%s", oldleft - left, oldsrc); } src = oldsrc; left = oldleft; } /* If we did manage to find a hostname, store it. */ if (hostname_start) lm->host = c_format_cstring("%s", hostname_len, hostname_start); } else { /* Different format */ oldsrc = src; oldleft = left; /* A kernel message? Use 'kernel' as the program name. */ if ((lm->pri & LOG_FACMASK) == LOG_KERN) { lm->program = c_format_cstring("kernel"); } /* No, not a kernel message. */ else { /* Capture the program name */ while (left && *src != ' ' && *src != '[' && *src != ':' && *src != '/' && *src != ',' && *src != '<') { src++; left--; } if (left) { lm->program = c_format_cstring("%s", oldleft - left, oldsrc); } left = oldleft; src = oldsrc; } lm->stamp = now; } for (oldsrc = src, oldleft = left; oldleft > 0; oldleft--, oldsrc++) { if (*oldsrc == '\n' || *oldsrc == '\r') *oldsrc = ' '; } lm->msg = c_format_cstring("%z%s", prefix ? prefix : (UINT8 *) "", left, src); } struct log_info *log_info_use(struct log_info *msg) { msg->use_cnt++; return msg; } void log_info_free(struct log_info *msg) { if (--msg->use_cnt == 0) { ol_string_free(msg->host); ol_string_free(msg->program); ol_string_free(msg->date); ol_string_free(msg->msg); ol_string_free(msg->host_from); ol_space_free(msg); } } struct log_info *make_log_info(UINT32 length, UINT8 *msg, UINT8 *prefix, UINT32 flags, regex_t *hostname_re) { struct log_info *self; NEW_SPACE(self); self->flags = flags & LF_USER_FLAGS; parse_log_msg(self, length, msg, prefix, hostname_re); self->use_cnt = 1; self->recvd = time(NULL); return self; } struct log_info *make_internal_message(UINT32 pri, UINT32 length, UINT8 *data) { struct log_info *self; NEW_SPACE(self); self->msg = c_format_cstring("syslog-ng[%i]: %s", getpid(), length, data); self->program = c_format_cstring("syslog-ng"); self->stamp = self->recvd = time(NULL); self->pri = pri; self->flags = LF_INTERNAL; self->use_cnt = 1; self->host_from = NULL; return self; } struct log_info *make_mark_message(void) { struct log_info *self = make_internal_message(LOG_SYSLOG | LOG_NOTICE, 12, (UINT8 *) "--- MARK ---"); self->flags |= LF_MARK; return self; }