Commit 9f4de680 authored by Howard Chu's avatar Howard Chu Committed by Quanah Gibson-Mount
Browse files

ITS#6949 add support for logfile rotation

Uses debuglevel, not sysloglevel.
parent 27a54243
......@@ -567,10 +567,23 @@ option description. The default is 71.
Specify a file for recording slapd debug messages. By default these messages
only go to stderr, are not recorded anywhere else, and are unrelated to
messages exposed by the
.B loglevel
.B olcLogLevel
configuration parameter. Specifying a logfile copies messages to both stderr
and the logfile.
.TP
.B olcLogFileOnly: TRUE | FALSE
Specify that debug messages should only go to the configured logfile, and
not to stderr.
.TP
.B olcLogFileRotate: <max> <Mbytes> <hours>
Specify automatic rotation for the configured logfile as the maximum
number of old logfiles to retain, a maximum size in megabytes to allow a
logfile to grow before rotation, and a maximum age in hours for a logfile
to be used before rotation. The maximum number must be in the range 1-99.
Setting Mbytes or hours to zero disables the size or age check, respectively.
At least one of Mbytes or hours must be non-zero. By default no automatic
rotation will be performed.
.TP
.B olcLogLevel: <integer> [...]
Specify the level at which debugging statements and operation
statistics should be syslogged (currently logged to the
......
......@@ -625,6 +625,19 @@ messages exposed by the
configuration parameter. Specifying a logfile copies messages to both stderr
and the logfile.
.TP
.B logfile-only on | off
Specify that debug messages should only go to the configured logfile, and
not to stderr.
.TP
.B logfile-rotate <max> <Mbytes> <hours>
Specify automatic rotation for the configured logfile as the maximum
number of old logfiles to retain, a maximum size in megabytes to allow a
logfile to grow before rotation, and a maximum age in hours for a logfile
to be used before rotation. The maximum number must be in the range 1-99.
Setting Mbytes or hours to zero disables the size or age check, respectively.
At least one of Mbytes or hours must be non-zero. By default no automatic
rotation will be performed.
.TP
.B loglevel <integer> [...]
Specify the level at which debugging statements and operation
statistics should be syslogged (currently logged to the
......
......@@ -80,8 +80,6 @@ typedef struct {
static CfBackInfo cfBackInfo;
static char *passwd_salt;
static FILE *logfile;
static char *logfileName;
static AccessControl *defacl_parsed = NULL;
static struct berval cfdir;
......@@ -203,6 +201,7 @@ enum {
CFG_TLS_CACERT,
CFG_TLS_CERT,
CFG_TLS_KEY,
CFG_LOGFILE_ROTATE,
CFG_LAST
};
......@@ -486,6 +485,14 @@ static ConfigTable config_back_cf_table[] = {
&config_generic, "( OLcfgGlAt:27 NAME 'olcLogFile' "
"EQUALITY caseExactMatch "
"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
{ "logfile-only", "on|off", 2, 2, 0, ARG_ON_OFF,
&logfile_only, "( OLcfgGlAt:102 NAME 'olcLogFileOnly' "
"EQUALITY booleanMatch "
"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
{ "logfile-rotate", "max> <Mbyte> <hours", 4, 4, 0, ARG_MAGIC|CFG_LOGFILE_ROTATE,
&config_generic, "( OLcfgGlAt:103 NAME 'olcLogFileRotate' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
{ "loglevel", "level", 2, 0, 0, ARG_MAGIC,
&config_loglevel, "( OLcfgGlAt:28 NAME 'olcLogLevel' "
"EQUALITY caseIgnoreMatch "
......@@ -980,7 +987,7 @@ static ConfigOCs cf_ocs[] = {
"olcIndexSubstrAnyLen $ olcIndexSubstrAnyStep $ olcIndexHash64 $ "
"olcIndexIntLen $ "
"olcListenerThreads $ olcLocalSSF $ olcLogFile $ olcLogLevel $ "
"olcMaxFilterDepth $ "
"olcLogFileOnly $ olcLogFileRotate $ olcMaxFilterDepth $ "
"olcPasswordCryptSaltFormat $ olcPasswordHash $ olcPidFile $ "
"olcPluginLogFile $ olcReadOnly $ olcReferral $ "
"olcReplogFile $ olcRequires $ olcRestrict $ olcReverseLookup $ "
......@@ -1373,11 +1380,27 @@ config_generic(ConfigArgs *c) {
rc = 1;
}
break;
case CFG_LOGFILE:
if ( logfileName )
case CFG_LOGFILE: {
const char *logfileName = logfile_name();
if ( logfileName && *logfileName )
c->value_string = ch_strdup( logfileName );
else
rc = 1;
}
break;
case CFG_LOGFILE_ROTATE:
rc = 1;
if ( logfile_max ) {
char buf[64];
struct berval bv;
bv.bv_len = snprintf( buf, sizeof(buf), "%d %ld %ld", logfile_max,
(long) logfile_fslimit / 1048576, (long) logfile_age / 3600 );
if ( bv.bv_len > 0 && bv.bv_len < sizeof(buf) ) {
bv.bv_val = buf;
value_add_one( &c->rvalue_vals, &bv );
rc = 0;
}
}
break;
case CFG_LASTMOD:
c->value_int = (SLAP_NOLASTMOD(c->be) == 0);
......@@ -1610,12 +1633,11 @@ config_generic(ConfigArgs *c) {
break;
case CFG_LOGFILE:
ch_free( logfileName );
logfileName = NULL;
if ( logfile ) {
fclose( logfile );
logfile = NULL;
}
logfile_close();
break;
case CFG_LOGFILE_ROTATE:
logfile_max = logfile_fslimit = logfile_age = 0;
break;
case CFG_SERVERID: {
......@@ -2392,11 +2414,44 @@ sortval_reject:
}
break;
case CFG_LOGFILE: {
if ( logfileName ) ch_free( logfileName );
logfileName = c->value_string;
logfile = fopen(logfileName, "w");
if(logfile) lutil_debug_file(logfile);
} break;
int rc = logfile_open( c->value_string );
ch_free( c->value_string );
return rc;
}
break;
case CFG_LOGFILE_ROTATE: {
unsigned lf_max, lf_mbyte, lf_hour;
if ( lutil_atoux( &lf_max, c->argv[1], 0 ) != 0 ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
"invalid max value \"%s\"", c->argv[0], c->argv[1] );
return 1;
}
if ( !lf_max || lf_max > 99 ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
"invalid max value \"%s\" must be 1-99", c->argv[0], c->argv[1] );
return 1;
}
if ( lutil_atoux( &lf_mbyte, c->argv[2], 0 ) != 0 ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
"invalid Mbyte value \"%s\"", c->argv[0], c->argv[1] );
return 1;
}
if ( lutil_atoux( &lf_hour, c->argv[3], 0 ) != 0 ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
"invalid hours value \"%s\"", c->argv[0], c->argv[2] );
return 1;
}
if ( !lf_mbyte && !lf_hour ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
"Mbyte and hours cannot both be zero", c->argv[0] );
return 1;
}
logfile_max = lf_max;
logfile_fslimit = lf_mbyte * 1048576; /* Megabytes to bytes */
logfile_age = lf_hour * 3600; /* hours to seconds */
}
break;
case CFG_LASTMOD:
if(SLAP_NOLASTMODCMD(c->be)) {
......
......@@ -35,6 +35,10 @@
#include <ac/wait.h>
#include <ac/errno.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <fcntl.h>
#include "slap.h"
#include "lutil.h"
#include "ldif.h"
......@@ -377,27 +381,121 @@ usage( char *name )
);
}
static char logfile_suffix[sizeof(".xx.gz")];
char logfile_path[MAXPATHLEN - sizeof(logfile_suffix) -1];
long logfile_fslimit;
int logfile_age, logfile_only, logfile_max;
static ldap_pvt_thread_mutex_t logfile_mutex;
static off_t logfile_fsize;
static time_t logfile_fcreated;
static int logfile_fd;
static char logpaths[2][MAXPATHLEN];
static int logpathlen;
typedef void (BER_logger)(const char *buf);
static BER_logger *ber_logger;
static void debug_print( const char *data )
{
char buf[4136]; /* 4096 + 40 */
char prefix[sizeof("ssssssssssssssss.ffffffff 0xtttttttttttttttt ")];
struct iovec iov[2];
int rotate = 0;
#ifdef HAVE_CLOCK_GETTIME
struct timespec tv;
#define TS "%08x"
#define Tfrac tv.tv_nsec
clock_gettime( CLOCK_REALTIME, &tv );
#define gettime(tv) clock_gettime( CLOCK_REALTIME, tv )
#else
struct timeval tv;
#define TS "%05x"
#define Tfrac tv.tv_usec
gettimeofday( &tv, NULL );
#define gettime(tv) gettimeofday( tv, NULL )
#endif
buf[sizeof(buf)-1] = '\0';
snprintf( buf, sizeof(buf)-1, "%lx." TS " %p %s",
(long)tv.tv_sec, Tfrac, (void *)ldap_pvt_thread_self(), data );
ber_logger( buf );
gettime( &tv );
iov[0].iov_base = prefix;
iov[0].iov_len = sprintf( prefix, "%lx." TS " %p ",
(long)tv.tv_sec, (unsigned int)Tfrac, (void *)ldap_pvt_thread_self() );
iov[1].iov_base = (void *)data;
iov[1].iov_len = strlen( data );
if ( !logfile_only )
writev( 2, iov, 2 );
if ( logfile_fd ) {
int len = iov[0].iov_len + iov[1].iov_len;
if ( logfile_fslimit || logfile_age ) {
ldap_pvt_thread_mutex_lock( &logfile_mutex );
if ( logfile_fslimit && logfile_fsize + len > logfile_fslimit )
rotate = 1;
if ( logfile_age && tv.tv_sec - logfile_fcreated >= logfile_age )
rotate |= 2;
if ( rotate ) {
close( logfile_fd );
strcpy( logpaths[0]+logpathlen, ".tmp" );
rename( logfile_path, logpaths[0] );
logfile_open( logfile_path );
}
}
len = writev( logfile_fd, iov, 2 );
if ( len > 0 )
logfile_fsize += len;
if ( logfile_fslimit || logfile_age )
ldap_pvt_thread_mutex_unlock( &logfile_mutex );
}
if ( rotate ) {
int i;
for (i=logfile_max; i > 1; i--) {
sprintf( logpaths[0]+logpathlen, ".%02d", i );
sprintf( logpaths[1]+logpathlen, ".%02d", i-1 );
rename( logpaths[1], logpaths[0] );
}
sprintf( logpaths[0]+logpathlen, ".tmp" );
rename( logpaths[0], logpaths[1] );
}
}
void logfile_close()
{
if ( logfile_fd ) {
close( logfile_fd );
logfile_fd = 0;
}
logfile_path[0] = '\0';
}
int logfile_open( const char *path )
{
struct stat st;
int fd;
fd = open( path, O_CREAT|O_WRONLY, 0640 );
if ( fd < 0 )
return errno;
if ( fstat( fd, &st )) {
close( fd );
return errno;
}
if ( !logfile_path[0] ) {
logpathlen = strlen( path );
if ( logpathlen >= sizeof(logfile_path) )
return ENAMETOOLONG;
strcpy( logfile_path, path );
strcpy( logpaths[0], path );
strcpy( logpaths[1], path );
}
logfile_fsize = st.st_size;
logfile_fcreated = st.st_ctime; /* not strictly true but close enough */
logfile_fd = fd;
lseek( fd, 0, SEEK_END );
return 0;
}
const char *logfile_name()
{
return logfile_path[0] ? logfile_path : NULL;
}
#ifdef HAVE_NT_SERVICE_MANAGER
......@@ -449,8 +547,8 @@ int main( int argc, char **argv )
slap_sl_mem_init();
(void) ldap_pvt_thread_initialize();
ldap_pvt_thread_mutex_init( &logfile_mutex );
serverName = lutil_progname( "slapd", argc, argv );
......@@ -1164,6 +1262,7 @@ stop:
mal_dumpleaktrace( leakfile );
#endif
ldap_pvt_thread_mutex_destroy( &logfile_mutex );
MAIN_RETURN(rc);
}
......
......@@ -1254,6 +1254,16 @@ LDAP_SLAPD_F (int)
parse_debug_unknowns LDAP_P(( char **unknowns, int *levelp ));
LDAP_SLAPD_F (void)
slap_check_unknown_level LDAP_P(( char *levelstr, int level ));
LDAP_SLAPD_F (int)
logfile_open LDAP_P(( const char *path ));
LDAP_SLAPD_F (void)
logfile_close LDAP_P(( void ));
LDAP_SLAPD_F (const char *)
logfile_name LDAP_P(( void ));
LDAP_SLAPD_V(int) logfile_age;
LDAP_SLAPD_V(int) logfile_only;
LDAP_SLAPD_V(int) logfile_max;
LDAP_SLAPD_V(long) logfile_fslimit;
/*
* matchedValues.c
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment