* Copyright 2003-2015, Axel DΓΆrfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include <syslog.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <launch.h>
#include <syslog_daemon.h>
#include <TLS.h>
#include <util/KMessage.h>
struct syslog_context {
char ident[B_OS_NAME_LENGTH];
int16 mask;
int16 facility;
int32 options;
};
static syslog_context sTeamContext = {
"",
-1,
LOG_USER,
LOG_CONS
};
static int32 sThreadContextSlot = -1;
static port_id sSystemLoggerPort = -1;
static syslog_context *
allocate_context()
{
syslog_context *context = (syslog_context *)malloc(sizeof(syslog_context));
if (context == NULL)
return NULL;
memcpy(context, &sTeamContext, sizeof(syslog_context));
return context;
}
If there is none for the current thread, it will create one
that inherits the context attributes from the team and put it
into TLS.
If it could not allocate a thread context, it will return the
team context; this function is guaranteed to return a valid
syslog context.
*/
static syslog_context *
get_context()
{
if (sThreadContextSlot == B_NO_MEMORY)
return &sTeamContext;
if (sThreadContextSlot < 0) {
static int32 lock = 0;
if (atomic_add(&lock, 1) == 0) {
int32 slot = tls_allocate();
if (slot < 0) {
sThreadContextSlot = B_NO_MEMORY;
return &sTeamContext;
}
sThreadContextSlot = slot;
} else {
while (sThreadContextSlot == -1)
snooze(10000);
}
}
syslog_context *context = (syslog_context *)tls_get(sThreadContextSlot);
if (context == NULL) {
*tls_address(sThreadContextSlot) = context = allocate_context();
}
if (context != NULL)
return context;
return &sTeamContext;
}
static void
message_to_console(syslog_context *context, const char *text, va_list args)
{
if (context->ident[0])
fprintf(stderr, "'%s' ", context->ident);
if (context->options & LOG_PID)
fprintf(stderr, "[%" B_PRId32 "] ", find_thread(NULL));
vfprintf(stderr, text, args);
fputc('\n', stderr);
}
*/
static port_id
get_system_logger_port()
{
if (sSystemLoggerPort >= 0)
return sSystemLoggerPort;
BPrivate::KMessage data;
if (BPrivate::get_launch_data(B_SYSTEM_LOGGER_SIGNATURE, data) == B_OK)
sSystemLoggerPort = data.GetInt32("logger_port", -1);
return sSystemLoggerPort;
}
daemon, if the priority mask matches.
If the message couldn't be delivered, and LOG_CONS was set, it will
redirect the message to stderr.
*/
static void
send_syslog_message(syslog_context *context, int priority, const char *text,
va_list args)
{
int options = context->options;
if ((context->mask & LOG_MASK(SYSLOG_PRIORITY(priority))) == 0)
return;
port_id port = get_system_logger_port();
if ((options & LOG_PERROR) != 0
|| ((options & LOG_CONS) != 0 && port < B_OK)) {
message_to_console(context, text, args);
}
if (port < B_OK) {
return;
}
if (SYSLOG_FACILITY(priority) == 0)
priority |= context->facility;
char buffer[2048];
syslog_message &message = *(syslog_message *)&buffer[0];
message.from = find_thread(NULL);
message.when = real_time_clock();
message.options = options;
message.priority = priority;
strcpy(message.ident, context->ident);
int length = vsnprintf(message.message, sizeof(buffer)
- sizeof(syslog_message), text, args);
if (message.message + length - buffer < (int32)sizeof(buffer)) {
if (length == 0 || message.message[length - 1] != '\n')
message.message[length++] = '\n';
} else
buffer[length - 1] = '\n';
status_t status;
do {
status = write_port(port, SYSLOG_MESSAGE, &message,
sizeof(syslog_message) + length);
} while (status == B_INTERRUPTED);
if (status < B_OK && (options & LOG_CONS) != 0
&& (options & LOG_PERROR) == 0) {
message_to_console(context, text, args);
}
}
void
closelog(void)
{
closelog_thread();
}
void
openlog(const char *ident, int options, int facility)
{
openlog_thread(ident, options, facility);
}
int
setlogmask(int priorityMask)
{
return setlogmask_thread(priorityMask);
}
void
syslog(int priority, const char *message, ...)
{
va_list args;
va_start(args, message);
send_syslog_message(get_context(), priority, message, args);
va_end(args);
}
void
closelog_team(void)
{
}
void
openlog_team(const char *ident, int options, int facility)
{
if (ident != NULL)
strlcpy(sTeamContext.ident, ident, sizeof(sTeamContext.ident));
sTeamContext.options = options;
sTeamContext.facility = SYSLOG_FACILITY(facility);
}
int
setlogmask_team(int priorityMask)
{
int oldMask = sTeamContext.mask;
if (priorityMask != 0)
sTeamContext.mask = priorityMask;
return oldMask;
}
void
log_team(int priority, const char *message, ...)
{
va_list args;
va_start(args, message);
send_syslog_message(&sTeamContext, priority, message, args);
va_end(args);
}
void
closelog_thread(void)
{
if (sThreadContextSlot < 0)
return;
free(tls_get(sThreadContextSlot));
*tls_address(sThreadContextSlot) = NULL;
}
void
openlog_thread(const char *ident, int options, int facility)
{
syslog_context *context = get_context();
if (ident)
strcpy(context->ident, ident);
context->options = options;
context->facility = SYSLOG_FACILITY(facility);
}
int
setlogmask_thread(int priorityMask)
{
syslog_context *context = get_context();
int oldMask = context->mask;
if (priorityMask != 0)
context->mask = priorityMask;
return oldMask;
}
void
log_thread(int priority, const char *message, ...)
{
va_list args;
va_start(args, message);
send_syslog_message(get_context(), priority, message, args);
va_end(args);
}
void
vsyslog(int priority, const char *message, va_list args)
{
send_syslog_message(get_context(), priority, message, args);
}