2010-08-14 21:59:25 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2010-01-20 21:18:52 +03:00
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)
static LogTarget log_target = LOG_TARGET_CONSOLE ;
2010-06-18 00:52:55 +04:00
static int log_max_level = LOG_INFO ;
2010-04-06 23:57:45 +04:00
2010-05-15 19:25:08 +04:00
static int console_fd = STDERR_FILENO ;
2010-04-06 23:57:45 +04:00
static int syslog_fd = - 1 ;
static int kmsg_fd = - 1 ;
2010-09-08 05:07:44 +04:00
static bool syslog_is_stream = false ;
2010-06-18 00:52:55 +04:00
static bool show_color = false ;
static bool show_location = false ;
2010-04-22 05:51:26 +04:00
/* Akin to glibc's __abort_msg; which is private and we hance cannot
* use here . */
static char * log_abort_msg = NULL ;
2010-05-15 19:25:08 +04:00
void log_close_console ( void ) {
if ( console_fd < 0 )
return ;
2010-04-06 23:57:45 +04:00
2010-05-18 07:10:52 +04:00
if ( getpid ( ) = = 1 ) {
if ( console_fd > = 3 )
close_nointr_nofail ( console_fd ) ;
2010-05-15 19:25:08 +04:00
console_fd = - 1 ;
2010-04-06 23:57:45 +04:00
}
}
2010-05-15 19:25:08 +04:00
static int log_open_console ( void ) {
2010-04-06 23:57:45 +04:00
2010-05-15 19:25:08 +04:00
if ( console_fd > = 0 )
2010-04-06 23:57:45 +04:00
return 0 ;
2010-05-15 19:25:08 +04:00
if ( getpid ( ) = = 1 ) {
if ( ( console_fd = open_terminal ( " /dev/console " , O_WRONLY | O_NOCTTY | O_CLOEXEC ) ) < 0 ) {
log_error ( " Failed to open /dev/console for logging: %s " , strerror ( - console_fd ) ) ;
return console_fd ;
}
2010-08-17 00:39:02 +04:00
log_debug ( " Succesfully opened /dev/console for logging. " ) ;
2010-05-15 19:25:08 +04:00
} else
console_fd = STDERR_FILENO ;
return 0 ;
}
void log_close_kmsg ( void ) {
if ( kmsg_fd < 0 )
return ;
close_nointr_nofail ( kmsg_fd ) ;
kmsg_fd = - 1 ;
}
static int log_open_kmsg ( void ) {
2010-04-06 23:57:45 +04:00
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 ) {
2010-05-15 19:25:08 +04:00
log_info ( " Failed to open /dev/kmsg 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-08-17 00:39:02 +04:00
log_debug ( " Succesfully opened /dev/kmsg for logging. " ) ;
2010-04-08 02:58:03 +04:00
2010-04-06 23:57:45 +04:00
return 0 ;
}
void log_close_syslog ( void ) {
2010-05-15 19:25:08 +04:00
if ( syslog_fd < 0 )
return ;
close_nointr_nofail ( syslog_fd ) ;
syslog_fd = - 1 ;
2010-04-06 23:57:45 +04:00
}
2010-09-08 05:07:44 +04:00
static int create_log_socket ( int type ) {
struct timeval tv ;
int fd ;
if ( ( fd = socket ( AF_UNIX , type | 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 ( fd , SOL_SOCKET , SO_SNDTIMEO , & tv , sizeof ( tv ) ) < 0 ) {
close_nointr_nofail ( fd ) ;
return - errno ;
}
return fd ;
}
2010-05-15 19:25:08 +04:00
static int log_open_syslog ( void ) {
2010-04-06 23:57:45 +04:00
union {
struct sockaddr sa ;
struct sockaddr_un un ;
} sa ;
int r ;
if ( syslog_fd > = 0 )
return 0 ;
zero ( sa ) ;
sa . un . sun_family = AF_UNIX ;
strncpy ( sa . un . sun_path , " /dev/log " , sizeof ( sa . un . sun_path ) ) ;
2010-09-08 05:07:44 +04:00
if ( ( syslog_fd = create_log_socket ( SOCK_DGRAM ) ) < 0 ) {
2010-04-06 23:57:45 +04:00
r = - errno ;
2010-05-15 19:25:08 +04:00
goto fail ;
2010-04-06 23:57:45 +04:00
}
2010-09-08 05:07:44 +04:00
if ( connect ( syslog_fd , & sa . sa , sizeof ( sa ) ) < 0 ) {
close_nointr_nofail ( syslog_fd ) ;
/* Some legacy syslog systems still use stream
* sockets . They really shouldn ' t . But what can we
* do . . . */
if ( ( syslog_fd = create_log_socket ( SOCK_STREAM ) ) < 0 ) {
r = - errno ;
goto fail ;
}
if ( connect ( syslog_fd , & sa . sa , sizeof ( sa ) ) < 0 ) {
r = - errno ;
goto fail ;
}
syslog_is_stream = true ;
} else
syslog_is_stream = false ;
2010-08-17 00:39:02 +04:00
log_debug ( " Succesfully opened syslog for logging. " ) ;
2010-04-08 02:58:03 +04:00
2010-04-06 23:57:45 +04:00
return 0 ;
2010-05-15 19:25:08 +04:00
fail :
log_close_syslog ( ) ;
log_info ( " Failed to open syslog for logging: %s " , strerror ( - r ) ) ;
return r ;
}
int log_open ( void ) {
int r ;
/* If we don't use the console we close it here, to not get
* killed by SAK . If we don ' t use syslog we close it here so
* that we are not confused by somebody deleting the socket in
* the fs . If we don ' t use / dev / kmsg we still keep it open ,
* because there is no reason to close it . */
2010-06-09 17:37:37 +04:00
if ( log_target = = LOG_TARGET_NULL ) {
log_close_syslog ( ) ;
log_close_console ( ) ;
return 0 ;
}
2010-05-15 19:25:08 +04:00
if ( log_target = = LOG_TARGET_SYSLOG_OR_KMSG | |
log_target = = LOG_TARGET_SYSLOG )
if ( ( r = log_open_syslog ( ) ) > = 0 ) {
log_close_console ( ) ;
return r ;
}
if ( log_target = = LOG_TARGET_SYSLOG_OR_KMSG | |
log_target = = LOG_TARGET_KMSG )
if ( ( r = log_open_kmsg ( ) ) > = 0 ) {
log_close_syslog ( ) ;
log_close_console ( ) ;
return r ;
}
log_close_syslog ( ) ;
2010-10-20 02:56:26 +04:00
/* Get the real /dev/console if we are PID=1, hence reopen */
log_close_console ( ) ;
2010-05-15 19:25:08 +04:00
return log_open_console ( ) ;
2010-04-06 23:57:45 +04:00
}
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 ;
}
2010-05-15 19:25:08 +04:00
static int write_to_console (
2010-01-20 21:18:52 +03:00
int level ,
const char * file ,
int line ,
const char * func ,
2010-05-15 19:25:08 +04:00
const char * buffer ) {
2010-01-20 21:18:52 +03:00
2010-05-15 19:25:08 +04:00
char location [ 64 ] ;
struct iovec iovec [ 5 ] ;
unsigned n = 0 ;
bool highlight ;
2010-01-20 21:18:52 +03:00
2010-05-15 19:25:08 +04:00
if ( console_fd < 0 )
return 0 ;
snprintf ( location , sizeof ( location ) , " (%s:%u) " , file , line ) ;
char_array_0 ( location ) ;
2010-06-18 00:52:55 +04:00
highlight = LOG_PRI ( level ) < = LOG_ERR & & show_color ;
2010-05-15 19:25:08 +04:00
zero ( iovec ) ;
2010-06-18 00:52:55 +04:00
if ( show_location )
IOVEC_SET_STRING ( iovec [ n + + ] , location ) ;
2010-05-15 19:25:08 +04:00
if ( highlight )
2010-07-05 04:40:39 +04:00
IOVEC_SET_STRING ( iovec [ n + + ] , ANSI_HIGHLIGHT_ON ) ;
2010-05-15 19:25:08 +04:00
IOVEC_SET_STRING ( iovec [ n + + ] , buffer ) ;
if ( highlight )
2010-07-05 04:40:39 +04:00
IOVEC_SET_STRING ( iovec [ n + + ] , ANSI_HIGHLIGHT_OFF ) ;
2010-05-15 19:25:08 +04:00
IOVEC_SET_STRING ( iovec [ n + + ] , " \n " ) ;
if ( writev ( console_fd , iovec , n ) < 0 )
return - errno ;
2010-01-20 21:18:52 +03:00
2010-05-15 19:25:08 +04:00
return 1 ;
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 ,
2010-05-15 19:25:08 +04:00
const char * buffer ) {
2010-04-06 23:57:45 +04:00
char header_priority [ 16 ] , header_time [ 64 ] , header_pid [ 16 ] ;
struct iovec iovec [ 5 ] ;
struct msghdr msghdr ;
time_t t ;
struct tm * tm ;
if ( syslog_fd < 0 )
2010-05-15 19:25:08 +04:00
return 0 ;
2010-04-06 23:57:45 +04:00
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 ;
2010-06-19 06:35:52 +04:00
snprintf ( header_pid , sizeof ( header_pid ) , " [%lu]: " , ( unsigned long ) getpid ( ) ) ;
2010-04-06 23:57:45 +04:00
char_array_0 ( header_pid ) ;
zero ( iovec ) ;
IOVEC_SET_STRING ( iovec [ 0 ] , header_priority ) ;
IOVEC_SET_STRING ( iovec [ 1 ] , header_time ) ;
2010-06-16 23:54:17 +04:00
IOVEC_SET_STRING ( iovec [ 2 ] , program_invocation_short_name ) ;
2010-04-06 23:57:45 +04:00
IOVEC_SET_STRING ( iovec [ 3 ] , header_pid ) ;
IOVEC_SET_STRING ( iovec [ 4 ] , buffer ) ;
2010-09-29 17:13:04 +04:00
/* When using syslog via SOCK_STREAM separate the messages by NUL chars */
2010-09-08 05:07:44 +04:00
if ( syslog_is_stream )
iovec [ 4 ] . iov_len + + ;
2010-04-06 23:57:45 +04:00
zero ( msghdr ) ;
msghdr . msg_iov = iovec ;
msghdr . msg_iovlen = ELEMENTSOF ( iovec ) ;
2010-09-08 05:07:44 +04:00
for ( ; ; ) {
ssize_t n ;
if ( ( n = sendmsg ( syslog_fd , & msghdr , MSG_NOSIGNAL ) ) < 0 )
return - errno ;
if ( ! syslog_is_stream | |
( size_t ) n > = IOVEC_TOTAL_SIZE ( iovec , ELEMENTSOF ( iovec ) ) )
break ;
IOVEC_INCREMENT ( iovec , ELEMENTSOF ( iovec ) , n ) ;
}
2010-04-06 23:57:45 +04:00
2010-05-15 19:25:08 +04:00
return 1 ;
2010-04-06 23:57:45 +04:00
}
static int write_to_kmsg (
int level ,
const char * file ,
int line ,
const char * func ,
2010-05-15 19:25:08 +04:00
const char * buffer ) {
2010-04-06 23:57:45 +04:00
char header_priority [ 16 ] , header_pid [ 16 ] ;
struct iovec iovec [ 5 ] ;
if ( kmsg_fd < 0 )
2010-05-15 19:25:08 +04:00
return 0 ;
2010-04-06 23:57:45 +04:00
snprintf ( header_priority , sizeof ( header_priority ) , " <%i> " , LOG_PRI ( level ) ) ;
char_array_0 ( header_priority ) ;
2010-06-19 06:35:52 +04:00
snprintf ( header_pid , sizeof ( header_pid ) , " [%lu]: " , ( unsigned long ) getpid ( ) ) ;
2010-04-06 23:57:45 +04:00
char_array_0 ( header_pid ) ;
zero ( iovec ) ;
IOVEC_SET_STRING ( iovec [ 0 ] , header_priority ) ;
2010-06-16 23:54:17 +04:00
IOVEC_SET_STRING ( iovec [ 1 ] , program_invocation_short_name ) ;
2010-04-06 23:57:45 +04:00
IOVEC_SET_STRING ( iovec [ 2 ] , header_pid ) ;
IOVEC_SET_STRING ( iovec [ 3 ] , buffer ) ;
2010-05-15 19:25:08 +04:00
IOVEC_SET_STRING ( iovec [ 4 ] , " \n " ) ;
2010-04-06 23:57:45 +04:00
if ( writev ( kmsg_fd , iovec , ELEMENTSOF ( iovec ) ) < 0 )
return - errno ;
2010-05-15 19:25:08 +04:00
return 1 ;
2010-04-06 23:57:45 +04:00
}
2010-05-15 19:25:08 +04:00
static int log_dispatch (
int level ,
const char * file ,
int line ,
const char * func ,
2010-05-21 05:31:49 +04:00
char * buffer ) {
2010-05-15 19:25:08 +04:00
2010-05-21 05:31:49 +04:00
int r = 0 ;
2010-05-15 19:25:08 +04:00
2010-06-09 17:37:37 +04:00
if ( log_target = = LOG_TARGET_NULL )
return 0 ;
2010-05-21 05:31:49 +04:00
do {
char * e ;
2010-08-17 05:31:45 +04:00
int k = 0 ;
2010-05-15 19:25:08 +04:00
2010-05-21 05:31:49 +04:00
buffer + = strspn ( buffer , NEWLINE ) ;
2010-05-15 19:25:08 +04:00
2010-05-21 05:31:49 +04:00
if ( buffer [ 0 ] = = 0 )
break ;
2010-05-15 19:25:08 +04:00
2010-05-21 05:31:49 +04:00
if ( ( e = strpbrk ( buffer , NEWLINE ) ) )
* ( e + + ) = 0 ;
2010-05-15 19:25:08 +04:00
2010-05-21 05:31:49 +04:00
if ( log_target = = LOG_TARGET_SYSLOG_OR_KMSG | |
log_target = = LOG_TARGET_SYSLOG ) {
2010-08-17 05:31:45 +04:00
if ( ( k = write_to_syslog ( level , file , line , func , buffer ) ) < 0 ) {
2010-05-21 05:31:49 +04:00
log_close_syslog ( ) ;
log_open_kmsg ( ) ;
2010-08-17 05:31:45 +04:00
} else if ( k > 0 )
2010-05-21 05:31:49 +04:00
r + + ;
}
2010-08-17 05:31:45 +04:00
if ( k < = 0 & &
( log_target = = LOG_TARGET_SYSLOG_OR_KMSG | |
log_target = = LOG_TARGET_KMSG ) ) {
2010-05-21 05:31:49 +04:00
2010-08-17 05:31:45 +04:00
if ( ( k = write_to_kmsg ( level , file , line , func , buffer ) ) < 0 ) {
2010-05-21 05:31:49 +04:00
log_close_kmsg ( ) ;
log_open_console ( ) ;
2010-08-17 05:31:45 +04:00
} else if ( k > 0 )
2010-05-21 05:31:49 +04:00
r + + ;
}
2010-08-17 05:31:45 +04:00
if ( k < = 0 & &
( k = write_to_console ( level , file , line , func , buffer ) ) < 0 )
2010-05-21 05:31:49 +04:00
return k ;
buffer = e ;
} while ( buffer ) ;
return r ;
2010-05-15 19:25:08 +04:00
}
2010-06-04 21:45:53 +04:00
int log_dump_internal (
int level ,
const char * file ,
int line ,
const char * func ,
char * buffer ) {
int saved_errno , r ;
/* This modifies the buffer... */
if ( _likely_ ( LOG_PRI ( level ) > log_max_level ) )
return 0 ;
saved_errno = errno ;
r = log_dispatch ( level , file , line , func , buffer ) ;
errno = saved_errno ;
return r ;
}
2010-05-15 19:25:08 +04:00
int log_meta (
2010-04-06 23:57:45 +04:00
int level ,
const char * file ,
int line ,
const char * func ,
const char * format , . . . ) {
2010-08-21 05:57:47 +04:00
char buffer [ LINE_MAX ] ;
2010-05-15 19:25:08 +04:00
int saved_errno , r ;
va_list ap ;
2010-04-06 23:57:45 +04:00
2010-06-02 23:34:03 +04:00
if ( _likely_ ( LOG_PRI ( level ) > log_max_level ) )
2010-05-15 19:25:08 +04:00
return 0 ;
2010-04-06 23:57:45 +04:00
saved_errno = errno ;
2010-05-15 19:25:08 +04:00
va_start ( ap , format ) ;
vsnprintf ( buffer , sizeof ( buffer ) , format , ap ) ;
va_end ( ap ) ;
char_array_0 ( buffer ) ;
r = log_dispatch ( level , file , line , func , buffer ) ;
2010-04-22 05:51:26 +04:00
errno = saved_errno ;
2010-05-15 19:25:08 +04:00
return r ;
2010-04-22 05:51:26 +04:00
}
2010-04-06 23:57:45 +04:00
2010-04-22 05:51:26 +04:00
void log_assert (
const char * file ,
int line ,
const char * func ,
const char * format , . . . ) {
2010-08-21 05:57:47 +04:00
static char buffer [ LINE_MAX ] ;
2010-04-22 05:51:26 +04:00
int saved_errno = errno ;
2010-05-15 19:25:08 +04:00
va_list ap ;
2010-04-22 05:51:26 +04:00
va_start ( ap , format ) ;
vsnprintf ( buffer , sizeof ( buffer ) , format , ap ) ;
va_end ( ap ) ;
char_array_0 ( buffer ) ;
log_abort_msg = buffer ;
2010-05-15 19:25:08 +04:00
log_dispatch ( LOG_CRIT , file , line , func , buffer ) ;
2010-04-22 05:51:26 +04:00
abort ( ) ;
2010-01-20 21:18:52 +03:00
2010-04-22 05:51:26 +04:00
/* If the user chose to ignore this SIGABRT, we are happy to go on, as if nothing happened. */
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-06-18 00:52:55 +04:00
2010-06-24 05:07:06 +04:00
if ( ( e = getenv ( " SYSTEMD_LOG_COLOR " ) ) )
2010-06-18 00:52:55 +04:00
if ( log_show_color_from_string ( e ) < 0 )
log_warning ( " Failed to parse bool %s. Ignoring. " , e ) ;
2010-10-20 02:56:26 +04:00
if ( ( e = getenv ( " SYSTEMD_LOG_LOCATION " ) ) )
2010-06-18 00:52:55 +04:00
if ( log_show_location_from_string ( e ) < 0 )
log_warning ( " Failed to parse bool %s. Ignoring. " , e ) ;
2010-04-07 01:38:32 +04:00
}
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-06-18 00:52:55 +04:00
void log_show_color ( bool b ) {
show_color = b ;
}
void log_show_location ( bool b ) {
show_location = b ;
}
int log_show_color_from_string ( const char * e ) {
int t ;
if ( ( t = parse_boolean ( e ) ) < 0 )
return - EINVAL ;
log_show_color ( t ) ;
return 0 ;
}
int log_show_location_from_string ( const char * e ) {
int t ;
if ( ( t = parse_boolean ( e ) ) < 0 )
return - EINVAL ;
log_show_location ( t ) ;
return 0 ;
}
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 " ,
2010-05-15 19:25:08 +04:00
[ LOG_TARGET_SYSLOG_OR_KMSG ] = " syslog-or-kmsg " ,
2010-06-09 17:37:37 +04:00
[ LOG_TARGET_NULL ] = " null "
2010-04-07 01:38:32 +04:00
} ;
DEFINE_STRING_TABLE_LOOKUP ( log_target , LogTarget ) ;