From e49d81689c3426560acc84d6f7ef9fa231304169 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 10 Mar 2011 05:33:45 +0000 Subject: [PATCH] [syslog] Add support for sending console output to a syslog server Originally-implemented-by: Anselm Martin Hoffmeister Signed-off-by: Michael Brown --- src/config/console.h | 1 + src/include/ipxe/syslog.h | 36 +++++++ src/net/udp/syslog.c | 206 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 src/include/ipxe/syslog.h create mode 100644 src/net/udp/syslog.c diff --git a/src/config/console.h b/src/config/console.h index 586e3715..dd1d7469 100644 --- a/src/config/console.h +++ b/src/config/console.h @@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); //#define CONSOLE_DIRECT_VGA /* Direct access to VGA card */ //#define CONSOLE_BTEXT /* Who knows what this does? */ //#define CONSOLE_PC_KBD /* Direct access to PC keyboard */ +//#define CONSOLE_SYSLOG /* Syslog console */ #include diff --git a/src/include/ipxe/syslog.h b/src/include/ipxe/syslog.h new file mode 100644 index 00000000..25edc6b0 --- /dev/null +++ b/src/include/ipxe/syslog.h @@ -0,0 +1,36 @@ +#ifndef _IPXE_SYSLOG_H +#define _IPXE_SYSLOG_H + +/** @file + * + * Syslog protocol + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** Syslog server port */ +#define SYSLOG_PORT 514 + +/** Syslog line buffer size + * + * This is a policy decision + */ +#define SYSLOG_BUFSIZE 128 + +/** Syslog facility + * + * This is a policy decision + */ +#define SYSLOG_FACILITY 0 /* kernel */ + +/** Syslog severity + * + * This is a policy decision + */ +#define SYSLOG_SEVERITY 6 /* informational */ + +/** Syslog priority */ +#define SYSLOG_PRIORITY( facility, severity ) ( 8 * (facility) + (severity) ) + +#endif /* _IPXE_SYSLOG_H */ diff --git a/src/net/udp/syslog.c b/src/net/udp/syslog.c new file mode 100644 index 00000000..f81ba2f8 --- /dev/null +++ b/src/net/udp/syslog.c @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2011 Michael Brown . + * + * 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 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Syslog protocol + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** The syslog server */ +static struct sockaddr_tcpip logserver = { + .st_port = htons ( SYSLOG_PORT ), +}; + +/** Syslog UDP interface operations */ +static struct interface_operation syslogger_operations[] = {}; + +/** Syslog UDP interface descriptor */ +static struct interface_descriptor syslogger_desc = + INTF_DESC_PURE ( syslogger_operations ); + +/** The syslog UDP interface */ +static struct interface syslogger = INTF_INIT ( syslogger_desc ); + +/****************************************************************************** + * + * Console driver + * + ****************************************************************************** + */ + +/** Syslog line buffer */ +static char syslog_buffer[SYSLOG_BUFSIZE]; + +/** Index into syslog line buffer */ +static unsigned int syslog_idx; + +/** Syslog recursion marker */ +static int syslog_entered; + +/** Syslog ANSI escape sequence handlers */ +static struct ansiesc_handler syslog_ansiesc_handlers[] = { + { 0, NULL } +}; + +/** Syslog ANSI escape sequence context */ +static struct ansiesc_context syslog_ansiesc_ctx = { + .handlers = syslog_ansiesc_handlers, +}; + +/** + * Print a character to syslog console + * + * @v character Character to be printed + */ +static void syslog_putchar ( int character ) { + int rc; + + /* Do nothing if we have no log server */ + if ( ! logserver.st_family ) + return; + + /* Ignore if we are already mid-logging */ + if ( syslog_entered ) + return; + + /* Strip ANSI escape sequences */ + character = ansiesc_process ( &syslog_ansiesc_ctx, character ); + if ( character < 0 ) + return; + + /* Ignore carriage return */ + if ( character == '\r' ) + return; + + /* Treat newline as a terminator */ + if ( character == '\n' ) + character = 0; + + /* Add character to buffer */ + syslog_buffer[syslog_idx++] = character; + + /* Do nothing more unless we reach end-of-line (or end-of-buffer) */ + if ( ( character != 0 ) && + ( syslog_idx < ( sizeof ( syslog_buffer ) - 1 /* NUL */ ) ) ) { + return; + } + + /* Reset to start of buffer */ + syslog_idx = 0; + + /* Guard against re-entry */ + syslog_entered = 1; + + /* Send log message */ + if ( ( rc = xfer_printf ( &syslogger, "<%d>ipxe: %s", + SYSLOG_PRIORITY ( SYSLOG_FACILITY, + SYSLOG_SEVERITY ), + syslog_buffer ) ) != 0 ) { + DBG ( "SYSLOG could not send log message: %s\n", + strerror ( rc ) ); + } + + /* Clear re-entry flag */ + syslog_entered = 0; +} + +/** Syslog console driver */ +struct console_driver syslog_console __console_driver = { + .putchar = syslog_putchar, +}; + +/****************************************************************************** + * + * Settings + * + ****************************************************************************** + */ + +/** Syslog server setting */ +struct setting syslog_setting __setting = { + .name = "syslog", + .description = "Syslog server", + .tag = DHCP_LOG_SERVERS, + .type = &setting_type_ipv4, +}; + +/** + * Apply syslog settings + * + * @ret rc Return status code + */ +static int apply_syslog_settings ( void ) { + struct sockaddr_in *sin_logserver = + ( struct sockaddr_in * ) &logserver; + struct in_addr old_addr; + int len; + int rc; + + /* Fetch log server */ + old_addr.s_addr = sin_logserver->sin_addr.s_addr; + logserver.st_family = 0; + if ( ( len = fetch_ipv4_setting ( NULL, &syslog_setting, + &sin_logserver->sin_addr ) ) >= 0 ) { + sin_logserver->sin_family = AF_INET; + } + + /* Do nothing unless log server has changed */ + if ( sin_logserver->sin_addr.s_addr == old_addr.s_addr ) + return 0; + + /* Reset syslog connection */ + intf_restart ( &syslogger, 0 ); + + /* Do nothing unless we have a log server */ + if ( ! logserver.st_family ) { + DBG ( "SYSLOG has no log server\n" ); + return 0; + } + + /* Connect to log server */ + if ( ( rc = xfer_open_socket ( &syslogger, SOCK_DGRAM, + ( ( struct sockaddr * ) &logserver ), + NULL ) ) != 0 ) { + DBG ( "SYSLOG cannot connect to log server: %s\n", + strerror ( rc ) ); + return rc; + } + DBG ( "SYSLOG using log server %s\n", + inet_ntoa ( sin_logserver->sin_addr ) ); + + return 0; +} + +/** Syslog settings applicator */ +struct settings_applicator syslog_applicator __settings_applicator = { + .apply = apply_syslog_settings, +};