233 lines
5.2 KiB
C++
233 lines
5.2 KiB
C++
/*
|
|
* Copyright 2003-2010, Axel Dörfler, axeld@pinc-software.de.
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
|
|
#include "syslog_output.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <syslog.h>
|
|
#include <unistd.h>
|
|
|
|
#include <FindDirectory.h>
|
|
#include <Path.h>
|
|
#include <driver_settings.h>
|
|
|
|
|
|
static const char *kFacilities[] = {
|
|
"KERN", "USER", "MAIL", "DAEMON",
|
|
"AUTH", "SYSLOGD", "LPR", "NEWS",
|
|
"UUCP", "CRON", "AUTHPRIV", "FTP",
|
|
"", "", "", "",
|
|
"LOCAL0", "LOCAL1", "LOCAL2", "LOCAL3",
|
|
"LOCAL4", "LOCAL5", "LOCAL6", "LOCAL7",
|
|
NULL
|
|
};
|
|
static const int32 kNumFacilities = 24;
|
|
|
|
static int sLog = -1;
|
|
static char sLastMessage[1024];
|
|
static thread_id sLastThread;
|
|
static int32 sRepeatCount;
|
|
static size_t sLogMaxSize = 524288; // 512kB
|
|
static bool sLogTimeStamps = false;
|
|
|
|
|
|
/*! Creates the log file if not yet existing, or renames the old
|
|
log file, if it's too big already.
|
|
*/
|
|
static status_t
|
|
prepare_output()
|
|
{
|
|
bool needNew = true;
|
|
bool tooLarge = false;
|
|
|
|
if (sLog >= 0) {
|
|
// check file size
|
|
struct stat stat;
|
|
if (fstat(sLog, &stat) == 0) {
|
|
if (stat.st_size < (off_t)sLogMaxSize)
|
|
needNew = false;
|
|
else
|
|
tooLarge = true;
|
|
}
|
|
}
|
|
|
|
if (needNew) {
|
|
// close old file; it'll be (re)moved soon
|
|
if (sLog >= 0)
|
|
close(sLog);
|
|
|
|
// get path (and create it if necessary)
|
|
BPath base;
|
|
find_directory(B_SYSTEM_LOG_DIRECTORY, &base, true);
|
|
|
|
BPath syslog(base);
|
|
syslog.Append("syslog");
|
|
|
|
// move old file if it already exists
|
|
if (tooLarge) {
|
|
BPath oldlog(base);
|
|
oldlog.Append("syslog.old");
|
|
|
|
remove(oldlog.Path());
|
|
rename(syslog.Path(), oldlog.Path());
|
|
|
|
// ToDo: just remove old file if space on device is tight?
|
|
}
|
|
|
|
bool haveSyslog = sLog >= 0;
|
|
|
|
// open file
|
|
sLog = open(syslog.Path(), O_APPEND | O_CREAT | O_WRONLY, 0644);
|
|
if (!haveSyslog && sLog >= 0) {
|
|
// first time open, check file size again
|
|
prepare_output();
|
|
}
|
|
}
|
|
|
|
return sLog >= 0 ? B_OK : B_ERROR;
|
|
}
|
|
|
|
|
|
static status_t
|
|
write_to_log(const char *buffer, int32 length)
|
|
{
|
|
if (sRepeatCount > 0) {
|
|
char repeat[64];
|
|
ssize_t size = snprintf(repeat, sizeof(repeat),
|
|
"Last message repeated %" B_PRId32 " time%s\n", sRepeatCount,
|
|
sRepeatCount > 1 ? "s" : "");
|
|
sRepeatCount = 0;
|
|
if (write(sLog, repeat, strlen(repeat)) < size)
|
|
return B_ERROR;
|
|
}
|
|
|
|
if (write(sLog, buffer, length) < length)
|
|
return B_ERROR;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
static void
|
|
syslog_output(syslog_message &message)
|
|
{
|
|
char header[128];
|
|
int32 headerLength;
|
|
int32 pos = 0;
|
|
|
|
if (sLogTimeStamps) {
|
|
// parse & nicely print the time stamp from the message
|
|
struct tm when;
|
|
localtime_r(&message.when, &when);
|
|
pos = strftime(header, sizeof(header), "%Y-%m-%d %H:%M:%S ", &when);
|
|
}
|
|
|
|
// add facility
|
|
int facility = SYSLOG_FACILITY_INDEX(message.priority);
|
|
if (facility >= kNumFacilities)
|
|
facility = SYSLOG_FACILITY_INDEX(LOG_USER);
|
|
pos += snprintf(header + pos, sizeof(header) - pos, "%s",
|
|
kFacilities[facility]);
|
|
|
|
// add ident/thread ID
|
|
if (message.ident[0] == '\0') {
|
|
// ToDo: find out team name?
|
|
} else {
|
|
pos += snprintf(header + pos, sizeof(header) - pos, " '%s'",
|
|
message.ident);
|
|
}
|
|
|
|
if ((message.options & LOG_PID) != 0) {
|
|
pos += snprintf(header + pos, sizeof(header) - pos, "[%" B_PRId32 "]",
|
|
message.from);
|
|
}
|
|
|
|
headerLength = pos + strlcpy(header + pos, ": ", sizeof(header) - pos);
|
|
if (headerLength >= (int32)sizeof(header))
|
|
headerLength = sizeof(header) - 1;
|
|
|
|
// add header to every line of the message and write it to the syslog
|
|
|
|
char buffer[SYSLOG_MESSAGE_BUFFER_SIZE];
|
|
pos = 0;
|
|
|
|
while (true) {
|
|
strcpy(buffer, header);
|
|
int32 length;
|
|
|
|
const char *newLine = strchr(message.message + pos, '\n');
|
|
if (newLine != NULL) {
|
|
length = newLine - message.message + 1 - pos;
|
|
strlcpy(buffer + headerLength, message.message + pos, length + 1);
|
|
pos += length;
|
|
} else {
|
|
length = strlcpy(buffer + headerLength, message.message + pos,
|
|
sizeof(buffer) - headerLength);
|
|
if (length == 0)
|
|
break;
|
|
}
|
|
|
|
length += headerLength;
|
|
|
|
if (length >= (int32)sizeof(buffer))
|
|
length = sizeof(buffer) - 1;
|
|
|
|
if (message.from == sLastThread
|
|
&& !strncmp(buffer + headerLength, sLastMessage,
|
|
sizeof(sLastMessage))) {
|
|
// we got this message already
|
|
sRepeatCount++;
|
|
} else {
|
|
// dump message line
|
|
|
|
if (prepare_output() < B_OK
|
|
|| write_to_log(buffer, length) < B_OK) {
|
|
// cannot write to syslog!
|
|
break;
|
|
}
|
|
|
|
// save this message to suppress repeated messages
|
|
strlcpy(sLastMessage, buffer + headerLength, sizeof(sLastMessage));
|
|
sLastThread = message.from;
|
|
}
|
|
|
|
if (newLine == NULL || !newLine[1]) {
|
|
// wrote last line of output
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
init_syslog_output(SyslogDaemon *daemon)
|
|
{
|
|
void *handle;
|
|
|
|
// get kernel syslog settings
|
|
handle = load_driver_settings("kernel");
|
|
if (handle != NULL) {
|
|
sLogTimeStamps = get_driver_boolean_parameter(handle,
|
|
"syslog_time_stamps", false, false);
|
|
const char *param = get_driver_parameter(handle,
|
|
"syslog_max_size", "0", "0");
|
|
int maxSize = strtol(param, NULL, 0);
|
|
if (strchr(param, 'k') || strchr(param, 'K'))
|
|
maxSize *= 1024;
|
|
else if (strchr(param, 'm') || strchr(param, 'M'))
|
|
maxSize *= 1048576;
|
|
if (maxSize > 0)
|
|
sLogMaxSize = maxSize;
|
|
|
|
unload_driver_settings(handle);
|
|
}
|
|
|
|
daemon->AddHandler(syslog_output);
|
|
}
|