2010-01-20 21:18:52 +03:00
/*-*- Mode: C; c-basic-offset: 8 -*-*/
2010-02-03 15:03:47 +03:00
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd 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 .
systemd 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 systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
2010-01-20 21:18:52 +03:00
# include <stdarg.h>
# include <stdio.h>
2010-01-28 00:39:29 +03:00
# include <errno.h>
2010-04-06 23:57:45 +04:00
# include <unistd.h>
# include <fcntl.h>
# include <sys/socket.h>
# include <sys/un.h>
2010-01-20 21:18:52 +03:00
# include "log.h"
2010-04-06 23:57:45 +04:00
# include "util.h"
# include "macro.h"
2010-01-20 21:18:52 +03:00
2010-04-06 23:57:45 +04:00
# define SYSLOG_TIMEOUT_USEC (5*USEC_PER_SEC)
# define LOG_BUFFER_MAX 1024
static LogTarget log_target = LOG_TARGET_CONSOLE ;
static int log_max_level = LOG_DEBUG ;
static int syslog_fd = - 1 ;
static int kmsg_fd = - 1 ;
void log_close_kmsg ( void ) {
if ( kmsg_fd > = 0 ) {
close_nointr ( kmsg_fd ) ;
kmsg_fd = - 1 ;
}
}
int log_open_kmsg ( void ) {
if ( log_target ! = LOG_TARGET_KMSG ) {
log_close_kmsg ( ) ;
return 0 ;
}
if ( kmsg_fd > = 0 )
return 0 ;
2010-04-08 03:22:27 +04:00
if ( ( kmsg_fd = open ( " /dev/kmsg " , O_WRONLY | O_NOCTTY | O_CLOEXEC ) ) < 0 ) {
log_info ( " Failed to open syslog for logging: %s " , strerror ( errno ) ) ;
2010-04-06 23:57:45 +04:00
return - errno ;
2010-04-08 03:22:27 +04:00
}
2010-04-06 23:57:45 +04:00
2010-04-08 02:58:03 +04:00
log_info ( " Succesfully opened /dev/kmsg for logging. " ) ;
2010-04-06 23:57:45 +04:00
return 0 ;
}
void log_close_syslog ( void ) {
if ( syslog_fd > = 0 ) {
close_nointr ( syslog_fd ) ;
syslog_fd = - 1 ;
}
}
int log_open_syslog ( void ) {
union {
struct sockaddr sa ;
struct sockaddr_un un ;
} sa ;
struct timeval tv ;
int r ;
if ( log_target ! = LOG_TARGET_SYSLOG ) {
log_close_syslog ( ) ;
return 0 ;
}
if ( syslog_fd > = 0 )
return 0 ;
if ( ( syslog_fd = socket ( AF_UNIX , SOCK_DGRAM | SOCK_CLOEXEC , 0 ) ) < 0 )
return - errno ;
/* Make sure we don't block for more than 5s when talking to
* syslog */
timeval_store ( & tv , SYSLOG_TIMEOUT_USEC ) ;
if ( setsockopt ( syslog_fd , SOL_SOCKET , SO_SNDTIMEO , & tv , sizeof ( tv ) ) < 0 ) {
r = - errno ;
log_close_syslog ( ) ;
return r ;
}
zero ( sa ) ;
sa . un . sun_family = AF_UNIX ;
strncpy ( sa . un . sun_path , " /dev/log " , sizeof ( sa . un . sun_path ) ) ;
if ( connect ( syslog_fd , & sa . sa , sizeof ( sa ) ) < 0 ) {
r = - errno ;
log_close_syslog ( ) ;
2010-04-08 03:22:27 +04:00
log_info ( " Failed to open syslog for logging: %s " , strerror ( - r ) ) ;
return r ;
2010-04-06 23:57:45 +04:00
}
2010-04-08 02:58:03 +04:00
log_info ( " Succesfully opened syslog for logging. " ) ;
2010-04-06 23:57:45 +04:00
return 0 ;
}
void log_set_target ( LogTarget target ) {
assert ( target > = 0 ) ;
assert ( target < _LOG_TARGET_MAX ) ;
log_target = target ;
}
void log_set_max_level ( int level ) {
assert ( ( level & LOG_PRIMASK ) = = level ) ;
log_max_level = level ;
}
static void write_to_console (
2010-01-20 21:18:52 +03:00
int level ,
const char * file ,
int line ,
const char * func ,
2010-04-06 23:57:45 +04:00
const char * format ,
va_list ap ) {
2010-01-20 21:18:52 +03:00
const char * prefix , * suffix ;
if ( LOG_PRI ( level ) < = LOG_ERR ) {
prefix = " \x1B [1;31m " ;
suffix = " \x1B [0m " ;
} else {
prefix = " " ;
suffix = " " ;
}
fprintf ( stderr , " (%s:%u) %s " , file , line , prefix ) ;
vfprintf ( stderr , format , ap ) ;
fprintf ( stderr , " %s \n " , suffix ) ;
2010-04-06 23:57:45 +04:00
}
2010-01-20 21:18:52 +03:00
2010-04-06 23:57:45 +04:00
static int write_to_syslog (
int level ,
const char * file ,
int line ,
const char * func ,
const char * format ,
va_list ap ) {
char header_priority [ 16 ] , header_time [ 64 ] , header_pid [ 16 ] ;
char buffer [ LOG_BUFFER_MAX ] ;
struct iovec iovec [ 5 ] ;
struct msghdr msghdr ;
time_t t ;
struct tm * tm ;
if ( syslog_fd < 0 )
return - EIO ;
snprintf ( header_priority , sizeof ( header_priority ) , " <%i> " , LOG_MAKEPRI ( LOG_DAEMON , LOG_PRI ( level ) ) ) ;
char_array_0 ( header_priority ) ;
t = ( time_t ) ( now ( CLOCK_REALTIME ) / USEC_PER_SEC ) ;
if ( ! ( tm = localtime ( & t ) ) )
return - EINVAL ;
if ( strftime ( header_time , sizeof ( header_time ) , " %h %e %T " , tm ) < = 0 )
return - EINVAL ;
snprintf ( header_pid , sizeof ( header_pid ) , " [%llu]: " , ( unsigned long long ) getpid ( ) ) ;
char_array_0 ( header_pid ) ;
vsnprintf ( buffer , sizeof ( buffer ) , format , ap ) ;
char_array_0 ( buffer ) ;
zero ( iovec ) ;
IOVEC_SET_STRING ( iovec [ 0 ] , header_priority ) ;
IOVEC_SET_STRING ( iovec [ 1 ] , header_time ) ;
2010-04-07 01:36:48 +04:00
IOVEC_SET_STRING ( iovec [ 2 ] , __progname ) ;
2010-04-06 23:57:45 +04:00
IOVEC_SET_STRING ( iovec [ 3 ] , header_pid ) ;
IOVEC_SET_STRING ( iovec [ 4 ] , buffer ) ;
zero ( msghdr ) ;
msghdr . msg_iov = iovec ;
msghdr . msg_iovlen = ELEMENTSOF ( iovec ) ;
if ( sendmsg ( syslog_fd , & msghdr , 0 ) < 0 )
return - errno ;
return 0 ;
}
static int write_to_kmsg (
int level ,
const char * file ,
int line ,
const char * func ,
const char * format ,
va_list ap ) {
char header_priority [ 16 ] , header_pid [ 16 ] ;
char buffer [ LOG_BUFFER_MAX ] ;
struct iovec iovec [ 5 ] ;
if ( kmsg_fd < 0 )
return - EIO ;
snprintf ( header_priority , sizeof ( header_priority ) , " <%i> " , LOG_PRI ( level ) ) ;
char_array_0 ( header_priority ) ;
snprintf ( header_pid , sizeof ( header_pid ) , " [%llu]: " , ( unsigned long long ) getpid ( ) ) ;
char_array_0 ( header_pid ) ;
vsnprintf ( buffer , sizeof ( buffer ) , format , ap ) ;
char_array_0 ( buffer ) ;
zero ( iovec ) ;
IOVEC_SET_STRING ( iovec [ 0 ] , header_priority ) ;
2010-04-07 01:36:48 +04:00
IOVEC_SET_STRING ( iovec [ 1 ] , __progname ) ;
2010-04-06 23:57:45 +04:00
IOVEC_SET_STRING ( iovec [ 2 ] , header_pid ) ;
IOVEC_SET_STRING ( iovec [ 3 ] , buffer ) ;
IOVEC_SET_STRING ( iovec [ 4 ] , ( char * ) " \n " ) ;
if ( writev ( kmsg_fd , iovec , ELEMENTSOF ( iovec ) ) < 0 )
return - errno ;
return 0 ;
}
void log_meta (
int level ,
const char * file ,
int line ,
const char * func ,
const char * format , . . . ) {
va_list ap ;
bool written ;
int saved_errno ;
if ( LOG_PRI ( level ) > log_max_level )
return ;
saved_errno = errno ;
written = false ;
if ( log_target = = LOG_TARGET_KMSG ) {
va_start ( ap , format ) ;
written = write_to_kmsg ( level , file , line , func , format , ap ) > = 0 ;
va_end ( ap ) ;
} else if ( log_target = = LOG_TARGET_SYSLOG ) {
va_start ( ap , format ) ;
written = write_to_syslog ( level , file , line , func , format , ap ) > = 0 ;
va_end ( ap ) ;
}
if ( ! written ) {
va_start ( ap , format ) ;
write_to_console ( level , file , line , func , format , ap ) ;
va_end ( ap ) ;
}
2010-01-20 21:18:52 +03:00
2010-01-28 00:39:29 +03:00
errno = saved_errno ;
2010-01-20 21:18:52 +03:00
}
2010-04-07 01:38:32 +04:00
int log_set_target_from_string ( const char * e ) {
LogTarget t ;
if ( ( t = log_target_from_string ( e ) ) < 0 )
return - EINVAL ;
log_set_target ( t ) ;
return 0 ;
}
int log_set_max_level_from_string ( const char * e ) {
int t ;
if ( ( t = log_level_from_string ( e ) ) < 0 )
return - EINVAL ;
log_set_max_level ( t ) ;
return 0 ;
}
void log_parse_environment ( void ) {
const char * e ;
if ( ( e = getenv ( " SYSTEMD_LOG_TARGET " ) ) )
if ( log_set_target_from_string ( e ) < 0 )
log_warning ( " Failed to parse log target %s. Ignoring. " , e ) ;
if ( ( e = getenv ( " SYSTEMD_LOG_LEVEL " ) ) )
if ( log_set_max_level_from_string ( e ) < 0 )
log_warning ( " Failed to parse log level %s. Ignoring. " , e ) ;
}
2010-04-10 19:40:18 +04:00
LogTarget log_get_target ( void ) {
return log_target ;
}
int log_get_max_level ( void ) {
return log_max_level ;
}
2010-04-07 01:38:32 +04:00
static const char * const log_target_table [ ] = {
[ LOG_TARGET_CONSOLE ] = " console " ,
[ LOG_TARGET_SYSLOG ] = " syslog " ,
[ LOG_TARGET_KMSG ] = " kmsg " ,
} ;
DEFINE_STRING_TABLE_LOOKUP ( log_target , LogTarget ) ;