Uses debuglevel, not sysloglevel.
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
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
static CfBackInfo cfBackInfo;
static char *passwd_salt;
-static FILE *logfile;
-static char *logfileName;
static AccessControl *defacl_parsed = NULL;
static struct berval cfdir;
CFG_TLS_CACERT,
CFG_TLS_CERT,
CFG_TLS_KEY,
+ CFG_LOGFILE_ROTATE,
CFG_LAST
};
&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 "
"olcIndexSubstrAnyLen $ olcIndexSubstrAnyStep $ olcIndexHash64 $ "
"olcIndexIntLen $ "
"olcListenerThreads $ olcLocalSSF $ olcLogFile $ olcLogLevel $ "
- "olcMaxFilterDepth $ "
+ "olcLogFileOnly $ olcLogFileRotate $ olcMaxFilterDepth $ "
"olcPasswordCryptSaltFormat $ olcPasswordHash $ olcPidFile $ "
"olcPluginLogFile $ olcReadOnly $ olcReferral $ "
"olcReplogFile $ olcRequires $ olcRestrict $ olcReverseLookup $ "
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);
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: {
}
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)) {
#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"
);
}
+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
slap_sl_mem_init();
-
(void) ldap_pvt_thread_initialize();
+ ldap_pvt_thread_mutex_init( &logfile_mutex );
serverName = lutil_progname( "slapd", argc, argv );
mal_dumpleaktrace( leakfile );
#endif
+ ldap_pvt_thread_mutex_destroy( &logfile_mutex );
MAIN_RETURN(rc);
}
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