Home | History | Annotate | Line # | Download | only in slapd
      1 /*	$NetBSD: logging.c,v 1.2 2025/09/05 21:16:25 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 2021-2024 The OpenLDAP Foundation.
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted only as authorized by the OpenLDAP
     11  * Public License.
     12  *
     13  * A copy of this license is available in the file LICENSE in the
     14  * top-level directory of the distribution or, alternatively, at
     15  * <http://www.OpenLDAP.org/license.html>.
     16  */
     17 
     18 #include <sys/cdefs.h>
     19 __RCSID("$NetBSD: logging.c,v 1.2 2025/09/05 21:16:25 christos Exp $");
     20 
     21 #include "portable.h"
     22 
     23 #include <stdio.h>
     24 
     25 #include <ac/errno.h>
     26 #include <ac/param.h>
     27 #include <ac/string.h>
     28 #include <ac/time.h>
     29 #include <ac/unistd.h>
     30 #include <ac/ctype.h>
     31 
     32 #include <sys/stat.h>
     33 #ifndef _WIN32
     34 #include <sys/uio.h>
     35 #endif
     36 #include <fcntl.h>
     37 
     38 #include "slap.h"
     39 #include "ldif.h"
     40 
     41 #include "slap-config.h"
     42 #include "slap-cfglog.h"
     43 
     44 static int config_syslog, active_syslog;
     45 
     46 static char logfile_suffix[sizeof(".xx.gz")];
     47 static char logfile_path[MAXPATHLEN - sizeof(logfile_suffix) -1];
     48 static long logfile_fslimit;
     49 static int logfile_age, logfile_only, logfile_max;
     50 static char *syslog_prefix;
     51 static int splen;
     52 static int logfile_rotfail, logfile_openfail;
     53 
     54 typedef enum { LFMT_DEBUG, LFMT_SYSLOG, LFMT_RFC3339 } LogFormat;
     55 static LogFormat logfile_format;
     56 
     57 #define LFMT_LOCALTIME 0x80
     58 #define LFMT_DEFAULT	LFMT_DEBUG
     59 #define LFMT_SYSLOG_LOCAL	(LFMT_SYSLOG|LFMT_LOCALTIME)
     60 #define LFMT_SYSLOG_UTC	(LFMT_SYSLOG)
     61 #define LFMT_RFC3339_UTC	(LFMT_RFC3339)
     62 
     63 static slap_verbmasks logformat_key[] = {
     64 	{ BER_BVC("default"),		LFMT_DEFAULT },
     65 	{ BER_BVC("debug"),			LFMT_DEBUG },
     66 	{ BER_BVC("syslog-utc"),	LFMT_SYSLOG_UTC },
     67 	{ BER_BVC("syslog-localtime"),		LFMT_SYSLOG_LOCAL },
     68 	{ BER_BVC("rfc3339-utc"),		LFMT_RFC3339_UTC },
     69 	{ BER_BVNULL, 0 }
     70 };
     71 
     72 char *serverName;
     73 int slap_debug_orig;
     74 
     75 ldap_pvt_thread_mutex_t logfile_mutex;
     76 
     77 static off_t logfile_fsize;
     78 static time_t logfile_fcreated;
     79 static int logfile_fd = -1;
     80 static char logpaths[2][MAXPATHLEN];
     81 static int logpathlen;
     82 
     83 #define SYSLOG_STAMP	"Mmm dd hh:mm:ss"
     84 #ifdef HAVE_CLOCK_GETTIME
     85 #define RFC3339_FRAC	".fffffffffZ"
     86 #else
     87 #define RFC3339_FRAC	".ffffffZ"
     88 #endif
     89 #define RFC3339_BASE	"YYYY-mm-ddTHH:MM:SS"
     90 #define RFC3339_STAMP	 RFC3339_BASE RFC3339_FRAC
     91 
     92 void
     93 slap_debug_print( const char *data )
     94 {
     95 #ifdef _WIN32
     96 	char msgbuf[4096];
     97 	int prefixlen, poffset = 0, datalen;
     98 #else
     99 	char prefix[sizeof("ssssssssssssssss.ffffffff 0xtttttttttttttttt ")];
    100 	struct iovec iov[2];
    101 #endif
    102 	int rotate = 0;
    103 #ifdef HAVE_CLOCK_GETTIME
    104 	struct timespec tv;
    105 #define	TS	"%08x"
    106 #define	TSf	".%09ldZ"
    107 #define	Tfrac	tv.tv_nsec
    108 #define gettime(tv)	clock_gettime( CLOCK_REALTIME, tv )
    109 #else
    110 	struct timeval tv;
    111 #define	TS	"%05x"
    112 #define	TSf	".%06ldZ"
    113 #define	Tfrac	tv.tv_usec
    114 #define	gettime(tv)	gettimeofday( tv, NULL )
    115 #endif
    116 	char *ptr;
    117 	int len;
    118 
    119 
    120 	gettime( &tv );
    121 #ifdef _WIN32
    122 	ptr = msgbuf;
    123 	prefixlen = sprintf( ptr, "%lx." TS " %p ",
    124 		(long)tv.tv_sec, (unsigned int)Tfrac, (void *)ldap_pvt_thread_self() );
    125 	if ( prefixlen < splen ) {
    126 		poffset = splen - prefixlen;
    127 		AC_MEMCPY( ptr+poffset, ptr, prefixlen );
    128 	}
    129 
    130 	ptr = lutil_strncopy( ptr+poffset+prefixlen, data, sizeof(msgbuf) - prefixlen);
    131 	len = ptr - msgbuf - poffset;
    132 	datalen = len - prefixlen;
    133 	if ( !logfile_only )
    134 		(void)!write( 2, msgbuf+poffset, len );
    135 	ptr = msgbuf;
    136 #else
    137 	iov[0].iov_base = prefix;
    138 	iov[0].iov_len = sprintf( prefix, "%lx." TS " %p ",
    139 		(long)tv.tv_sec, (unsigned int)Tfrac, (void *)ldap_pvt_thread_self() );
    140 	iov[1].iov_base = (void *)data;
    141 	iov[1].iov_len = strlen( data );
    142 	len = iov[0].iov_len + iov[1].iov_len;
    143 	if ( !logfile_only )
    144 		(void)!writev( 2, iov, 2 );
    145 #endif
    146 	if ( logfile_fd >= 0 ) {
    147 		if ( logfile_fslimit || logfile_age ) {
    148 			ldap_pvt_thread_mutex_lock( &logfile_mutex );
    149 			if ( logfile_fslimit && logfile_fsize + len > logfile_fslimit )
    150 				rotate = 1;
    151 			if ( logfile_age && tv.tv_sec - logfile_fcreated >= logfile_age )
    152 				rotate |= 2;
    153 			if ( rotate ) {
    154 				int rc, savefd;
    155 				strcpy( logpaths[0]+logpathlen, ".tmp" );
    156 				if ( rename( logfile_path, logpaths[0] )) {
    157 					rc = errno;
    158 					if ( !logfile_rotfail ) {
    159 						char buf[BUFSIZ];
    160 						char ebuf[128];
    161 						int len = snprintf(buf, sizeof( buf ), "ERROR! logfile rotate failure, err=%d \"%s\"\n",
    162 							rc, AC_STRERROR_R( rc, ebuf, sizeof(ebuf) ));
    163 						if ( !logfile_only )
    164 							!write( 2, buf, len );
    165 						!write( logfile_fd, buf, len );
    166 						logfile_rotfail = 1;
    167 					}
    168 					rotate = 0;	/* don't bother since it will fail */
    169 				} else {
    170 					logfile_rotfail = 0;
    171 				}
    172 				savefd = logfile_fd;
    173 				logfile_fd = -1;
    174 				if (( rc = logfile_open( logfile_path ))) {
    175 					logfile_fd = savefd;
    176 					if ( !logfile_openfail ) {
    177 						char buf[BUFSIZ];
    178 						char ebuf[128];
    179 						int len = snprintf(buf, sizeof( buf ), "ERROR! logfile couldn't be reopened, err=%d \"%s\"\n",
    180 							rc, AC_STRERROR_R( rc, ebuf, sizeof(ebuf) ));
    181 						if ( !logfile_only )
    182 							!write( 2, buf, len );
    183 						!write( logfile_fd, buf, len );
    184 						logfile_openfail = 1;
    185 					}
    186 				} else {
    187 					close( savefd );
    188 					logfile_openfail = 0;
    189 				}
    190 			}
    191 		}
    192 
    193 		if ( logfile_format > LFMT_DEBUG ) {
    194 			struct tm tm;
    195 			if ( !( logfile_format & LFMT_LOCALTIME ) )
    196 				ldap_pvt_gmtime( &tv.tv_sec, &tm );
    197 			else
    198 				ldap_pvt_localtime( &tv.tv_sec, &tm );
    199 #ifdef _WIN32
    200 			if ( splen < prefixlen )
    201 				ptr += prefixlen - splen;
    202 			memcpy( ptr, syslog_prefix, splen );
    203 #else
    204 			ptr = syslog_prefix;
    205 #endif
    206 			if ( logfile_format & LFMT_SYSLOG ) {
    207 				ptr += strftime( ptr, sizeof( SYSLOG_STAMP ),
    208 					"%b %d %H:%M:%S", &tm );
    209 			}	else {
    210 				ptr += strftime( ptr, sizeof( RFC3339_BASE ),
    211 					"%Y-%m-%dT%H:%M:%S", &tm );
    212 				ptr += snprintf( ptr, sizeof( RFC3339_FRAC ), TSf, Tfrac );
    213 			}
    214 			*ptr = ' ';
    215 #ifdef _WIN32
    216 			len = datalen + splen;
    217 #else
    218 			iov[0].iov_base = syslog_prefix;
    219 			iov[0].iov_len = splen;
    220 #endif
    221 		}
    222 
    223 #ifdef _WIN32
    224 		if ( logfile_format <= LFMT_DEBUG )
    225 			ptr += poffset;	/* only nonzero if logfile-format was explicitly set */
    226 		len = write( logfile_fd, ptr, len );
    227 #else
    228 		len = writev( logfile_fd, iov, 2 );
    229 #endif
    230 		if ( len > 0 )
    231 			logfile_fsize += len;
    232 		if ( logfile_fslimit || logfile_age )
    233 			ldap_pvt_thread_mutex_unlock( &logfile_mutex );
    234 	}
    235 	if ( rotate ) {
    236 		int i;
    237 		for (i=logfile_max; i > 1; i--) {
    238 			sprintf( logpaths[0]+logpathlen, ".%02d", i );
    239 			sprintf( logpaths[1]+logpathlen, ".%02d", i-1 );
    240 			rename( logpaths[1], logpaths[0] );
    241 		}
    242 		sprintf( logpaths[0]+logpathlen, ".tmp" );
    243 		rename( logpaths[0], logpaths[1] );
    244 	}
    245 }
    246 
    247 void
    248 logfile_close()
    249 {
    250 	if ( logfile_fd >= 0 ) {
    251 		close( logfile_fd );
    252 		logfile_fd = -1;
    253 	}
    254 	logfile_path[0] = '\0';
    255 }
    256 
    257 int
    258 logfile_open( const char *path )
    259 {
    260 	struct stat st;
    261 	int fd, saved_errno;
    262 
    263 	/* the logfile is for slapd only, not tools */
    264 	if ( !( slapMode & SLAP_SERVER_MODE ))
    265 		return 0;
    266 
    267 	fd = open( path, O_CREAT|O_WRONLY|O_APPEND, 0640 );
    268 	if ( fd < 0 ) {
    269 		saved_errno = errno;
    270 fail:
    271 		logfile_only = 0;	/* make sure something gets output */
    272 		return saved_errno;
    273 	}
    274 
    275 	if ( fstat( fd, &st ) ) {
    276 		saved_errno = errno;
    277 		close( fd );
    278 		goto fail;
    279 	}
    280 
    281 	if ( !logfile_path[0] ) {
    282 		logpathlen = strlen( path );
    283 		if ( logpathlen >= sizeof(logfile_path) ) {
    284 			saved_errno = ENAMETOOLONG;
    285 			goto fail;
    286 		}
    287 		strcpy( logfile_path, path );
    288 		strcpy( logpaths[0], path );
    289 		strcpy( logpaths[1], path );
    290 	}
    291 
    292 	logfile_fsize = st.st_size;
    293 	logfile_fcreated = st.st_ctime;	/* not strictly true but close enough */
    294 	logfile_fd = fd;
    295 
    296 	return 0;
    297 }
    298 
    299 const char *
    300 logfile_name()
    301 {
    302 	return logfile_path[0] ? logfile_path : NULL;
    303 }
    304 
    305 #if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
    306 #ifdef LOG_LOCAL4
    307 int
    308 slap_parse_syslog_user( const char *arg, int *syslogUser )
    309 {
    310 	static slap_verbmasks syslogUsers[] = {
    311 		{ BER_BVC( "LOCAL0" ), LOG_LOCAL0 },
    312 		{ BER_BVC( "LOCAL1" ), LOG_LOCAL1 },
    313 		{ BER_BVC( "LOCAL2" ), LOG_LOCAL2 },
    314 		{ BER_BVC( "LOCAL3" ), LOG_LOCAL3 },
    315 		{ BER_BVC( "LOCAL4" ), LOG_LOCAL4 },
    316 		{ BER_BVC( "LOCAL5" ), LOG_LOCAL5 },
    317 		{ BER_BVC( "LOCAL6" ), LOG_LOCAL6 },
    318 		{ BER_BVC( "LOCAL7" ), LOG_LOCAL7 },
    319 #ifdef LOG_USER
    320 		{ BER_BVC( "USER" ), LOG_USER },
    321 #endif /* LOG_USER */
    322 #ifdef LOG_DAEMON
    323 		{ BER_BVC( "DAEMON" ), LOG_DAEMON },
    324 #endif /* LOG_DAEMON */
    325 		{ BER_BVNULL, 0 }
    326 	};
    327 	int i = verb_to_mask( arg, syslogUsers );
    328 
    329 	if ( BER_BVISNULL( &syslogUsers[ i ].word ) ) {
    330 		Debug( LDAP_DEBUG_ANY,
    331 			"unrecognized syslog user \"%s\".\n",
    332 			arg );
    333 		return 1;
    334 	}
    335 
    336 	*syslogUser = syslogUsers[ i ].mask;
    337 
    338 	return 0;
    339 }
    340 #endif /* LOG_LOCAL4 */
    341 
    342 int
    343 slap_parse_syslog_level( const char *arg, int *levelp )
    344 {
    345 	static slap_verbmasks	str2syslog_level[] = {
    346 		{ BER_BVC( "EMERG" ),	LOG_EMERG },
    347 		{ BER_BVC( "ALERT" ),	LOG_ALERT },
    348 		{ BER_BVC( "CRIT" ),	LOG_CRIT },
    349 		{ BER_BVC( "ERR" ),	LOG_ERR },
    350 		{ BER_BVC( "WARNING" ),	LOG_WARNING },
    351 		{ BER_BVC( "NOTICE" ),	LOG_NOTICE },
    352 		{ BER_BVC( "INFO" ),	LOG_INFO },
    353 		{ BER_BVC( "DEBUG" ),	LOG_DEBUG },
    354 		{ BER_BVNULL, 0 }
    355 	};
    356 	int i = verb_to_mask( arg, str2syslog_level );
    357 	if ( BER_BVISNULL( &str2syslog_level[ i ].word ) ) {
    358 		Debug( LDAP_DEBUG_ANY,
    359 			"unknown syslog level \"%s\".\n",
    360 			arg );
    361 		return 1;
    362 	}
    363 
    364 	*levelp = str2syslog_level[ i ].mask;
    365 
    366 	return 0;
    367 }
    368 #endif /* LDAP_DEBUG && LDAP_SYSLOG */
    369 
    370 static char **debug_unknowns;
    371 static char **syslog_unknowns;
    372 
    373 static int
    374 parse_debug_unknowns( char **unknowns, int *levelp )
    375 {
    376 	int i, level, rc = 0;
    377 
    378 	for ( i = 0; unknowns[ i ] != NULL; i++ ) {
    379 		level = 0;
    380 		if ( str2loglevel( unknowns[ i ], &level )) {
    381 			fprintf( stderr,
    382 				"unrecognized log level \"%s\"\n", unknowns[ i ] );
    383 			rc = 1;
    384 		} else {
    385 			*levelp |= level;
    386 		}
    387 	}
    388 	return rc;
    389 }
    390 
    391 int
    392 slap_parse_debug_level( const char *arg, int *levelp, int which )
    393 {
    394 	int	level;
    395 
    396 	if ( arg && arg[ 0 ] != '-' && !isdigit( (unsigned char) arg[ 0 ] ) )
    397 	{
    398 		int	i;
    399 		char	**levels;
    400 		char	***unknowns = which ? &syslog_unknowns : &debug_unknowns;
    401 
    402 		levels = ldap_str2charray( arg, "," );
    403 
    404 		for ( i = 0; levels[ i ] != NULL; i++ ) {
    405 			level = 0;
    406 
    407 			if ( str2loglevel( levels[ i ], &level ) ) {
    408 				/* remember this for later */
    409 				ldap_charray_add( unknowns, levels[ i ] );
    410 				fprintf( stderr,
    411 					"unrecognized log level \"%s\" (deferred)\n",
    412 					levels[ i ] );
    413 			} else {
    414 				*levelp |= level;
    415 			}
    416 		}
    417 
    418 		ldap_charray_free( levels );
    419 
    420 	} else {
    421 		int rc;
    422 
    423 		if ( arg[0] == '-' ) {
    424 			rc = lutil_atoix( &level, arg, 0 );
    425 		} else {
    426 			unsigned ulevel;
    427 
    428 			rc = lutil_atoux( &ulevel, arg, 0 );
    429 			level = (int)ulevel;
    430 		}
    431 
    432 		if ( rc ) {
    433 			fprintf( stderr,
    434 				"unrecognized log level "
    435 				"\"%s\"\n", arg );
    436 			return 1;
    437 		}
    438 
    439 		if ( level == 0 ) {
    440 			*levelp = 0;
    441 
    442 		} else {
    443 			*levelp |= level;
    444 		}
    445 	}
    446 
    447 	return 0;
    448 }
    449 
    450 int
    451 slap_parse_debug_unknowns() {
    452 	int rc = 0;
    453 	if ( debug_unknowns ) {
    454 		rc = parse_debug_unknowns( debug_unknowns, &slap_debug );
    455 		ldap_charray_free( debug_unknowns );
    456 		debug_unknowns = NULL;
    457 		if ( rc )
    458 			goto leave;
    459 		ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &slap_debug );
    460 		ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &slap_debug );
    461 	}
    462 	if ( syslog_unknowns ) {
    463 		rc = parse_debug_unknowns( syslog_unknowns, &ldap_syslog );
    464 		ldap_charray_free( syslog_unknowns );
    465 		syslog_unknowns = NULL;
    466 	}
    467 leave:
    468 	return rc;
    469 }
    470 
    471 void slap_check_unknown_level( char *levelstr, int level )
    472 {
    473 	int i;
    474 
    475 	if ( debug_unknowns ) {
    476 		for ( i = 0; debug_unknowns[ i ]; i++ ) {
    477 			if ( !strcasecmp( debug_unknowns[ i ], levelstr )) {
    478 				slap_debug |= level;
    479 				break;
    480 			}
    481 		}
    482 	}
    483 
    484 	if ( syslog_unknowns ) {
    485 		for ( i = 0; syslog_unknowns[ i ]; i++ ) {
    486 			if ( !strcasecmp( syslog_unknowns[ i ], levelstr )) {
    487 				ldap_syslog |= level;
    488 				break;
    489 			}
    490 		}
    491 	}
    492 }
    493 
    494 static slap_verbmasks	*loglevel_ops;
    495 
    496 static int
    497 loglevel_init( void )
    498 {
    499 	slap_verbmasks	lo[] = {
    500 		{ BER_BVC("Any"),	(slap_mask_t) LDAP_DEBUG_ANY },
    501 		{ BER_BVC("Trace"),	LDAP_DEBUG_TRACE },
    502 		{ BER_BVC("Packets"),	LDAP_DEBUG_PACKETS },
    503 		{ BER_BVC("Args"),	LDAP_DEBUG_ARGS },
    504 		{ BER_BVC("Conns"),	LDAP_DEBUG_CONNS },
    505 		{ BER_BVC("BER"),	LDAP_DEBUG_BER },
    506 		{ BER_BVC("Filter"),	LDAP_DEBUG_FILTER },
    507 		{ BER_BVC("Config"),	LDAP_DEBUG_CONFIG },
    508 		{ BER_BVC("ACL"),	LDAP_DEBUG_ACL },
    509 		{ BER_BVC("Stats"),	LDAP_DEBUG_STATS },
    510 		{ BER_BVC("Stats2"),	LDAP_DEBUG_STATS2 },
    511 		{ BER_BVC("Shell"),	LDAP_DEBUG_SHELL },
    512 		{ BER_BVC("Parse"),	LDAP_DEBUG_PARSE },
    513 #if 0	/* no longer used (nor supported) */
    514 		{ BER_BVC("Cache"),	LDAP_DEBUG_CACHE },
    515 		{ BER_BVC("Index"),	LDAP_DEBUG_INDEX },
    516 #endif
    517 		{ BER_BVC("Sync"),	LDAP_DEBUG_SYNC },
    518 		{ BER_BVC("None"),	LDAP_DEBUG_NONE },
    519 		{ BER_BVNULL,		0 }
    520 	};
    521 
    522 	return slap_verbmasks_init( &loglevel_ops, lo );
    523 }
    524 
    525 void
    526 slap_loglevel_destroy( void )
    527 {
    528 	if ( loglevel_ops ) {
    529 		(void)slap_verbmasks_destroy( loglevel_ops );
    530 	}
    531 	loglevel_ops = NULL;
    532 }
    533 
    534 static slap_mask_t	loglevel_ignore[] = { -1, 0 };
    535 
    536 int
    537 slap_loglevel_get( struct berval *s, int *l )
    538 {
    539 	int		rc;
    540 	slap_mask_t	m, i;
    541 
    542 	if ( loglevel_ops == NULL ) {
    543 		loglevel_init();
    544 	}
    545 
    546 	for ( m = 0, i = 1; !BER_BVISNULL( &loglevel_ops[ i ].word ); i++ ) {
    547 		m |= loglevel_ops[ i ].mask;
    548 	}
    549 
    550 	for ( i = 1; m & i; i <<= 1 )
    551 		;
    552 
    553 	if ( i == 0 ) {
    554 		return -1;
    555 	}
    556 
    557 	rc = slap_verbmasks_append( &loglevel_ops, i, s, loglevel_ignore );
    558 
    559 	if ( rc != 0 ) {
    560 		Debug( LDAP_DEBUG_ANY, "slap_loglevel_get(%lu, \"%s\") failed\n",
    561 			i, s->bv_val );
    562 
    563 	} else {
    564 		*l = i;
    565 		slap_check_unknown_level( s->bv_val, i );
    566 	}
    567 
    568 	return rc;
    569 }
    570 
    571 int
    572 slap_syslog_get()
    573 {
    574 	return active_syslog;
    575 }
    576 
    577 void
    578 slap_syslog_set( int l )
    579 {
    580 	active_syslog = l;
    581 	if ( logfile_only ) {
    582 		slap_debug |= active_syslog;
    583 		ldap_syslog = 0;
    584 	} else {
    585 		ldap_syslog = active_syslog;
    586 	}
    587 }
    588 
    589 int
    590 slap_debug_get()
    591 {
    592 	return slap_debug_orig;
    593 }
    594 
    595 void
    596 slap_debug_set( int l )
    597 {
    598 	slap_debug_orig = l;
    599 	if ( logfile_only )
    600 		slap_debug = slap_debug_orig | active_syslog;
    601 	else
    602 		slap_debug = slap_debug_orig;
    603 	ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &slap_debug);
    604 	ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &slap_debug);
    605 	ldif_debug = slap_debug;
    606 }
    607 
    608 int
    609 str2loglevel( const char *s, int *l )
    610 {
    611 	int	i;
    612 
    613 	if ( loglevel_ops == NULL ) {
    614 		loglevel_init();
    615 	}
    616 
    617 	i = verb_to_mask( s, loglevel_ops );
    618 
    619 	if ( BER_BVISNULL( &loglevel_ops[ i ].word ) ) {
    620 		return -1;
    621 	}
    622 
    623 	*l = loglevel_ops[ i ].mask;
    624 
    625 	return 0;
    626 }
    627 
    628 const char *
    629 loglevel2str( int l )
    630 {
    631 	struct berval	bv = BER_BVNULL;
    632 
    633 	loglevel2bv( l, &bv );
    634 
    635 	return bv.bv_val;
    636 }
    637 
    638 int
    639 loglevel2bv( int l, struct berval *bv )
    640 {
    641 	if ( loglevel_ops == NULL ) {
    642 		loglevel_init();
    643 	}
    644 
    645 	BER_BVZERO( bv );
    646 
    647 	return enum_to_verb( loglevel_ops, l, bv ) == -1;
    648 }
    649 
    650 int
    651 loglevel2bvarray( int l, BerVarray *bva )
    652 {
    653 	if ( loglevel_ops == NULL ) {
    654 		loglevel_init();
    655 	}
    656 
    657 	if ( l == 0 ) {
    658 		struct berval bv = BER_BVC("0");
    659 		return value_add_one( bva, &bv );
    660 	}
    661 
    662 	return mask_to_verbs( loglevel_ops, l, bva );
    663 }
    664 
    665 int
    666 loglevel_print( FILE *out )
    667 {
    668 	int	i;
    669 
    670 	if ( loglevel_ops == NULL ) {
    671 		loglevel_init();
    672 	}
    673 
    674 	fprintf( out, "Installed log subsystems:\n\n" );
    675 	for ( i = 0; !BER_BVISNULL( &loglevel_ops[ i ].word ); i++ ) {
    676 		unsigned mask = loglevel_ops[ i ].mask & 0xffffffffUL;
    677 		fprintf( out,
    678 			(mask == ((slap_mask_t) -1 & 0xffffffffUL)
    679 			 ? "\t%-30s (-1, 0xffffffff)\n" : "\t%-30s (%u, 0x%x)\n"),
    680 			loglevel_ops[ i ].word.bv_val, mask, mask );
    681 	}
    682 
    683 	fprintf( out, "\nNOTE: custom log subsystems may be later installed "
    684 		"by specific code\n\n" );
    685 
    686 	return 0;
    687 }
    688 
    689 int
    690 config_logging(ConfigArgs *c) {
    691 	int i, rc = 0;
    692 
    693 	if ( loglevel_ops == NULL ) {
    694 		loglevel_init();
    695 	}
    696 
    697 	if (c->op == SLAP_CONFIG_EMIT) {
    698 		switch(c->type) {
    699 		case CFG_LOGLEVEL:
    700 			/* Get default or commandline slapd setting */
    701 			if ( ldap_syslog && !config_syslog )
    702 				config_syslog = ldap_syslog;
    703 			rc = loglevel2bvarray( config_syslog, &c->rvalue_vals );
    704 			break;
    705 
    706 		case CFG_LOGFILE: {
    707 			const char *logfileName = logfile_name();
    708 			if ( logfileName && *logfileName )
    709 				c->value_string = ch_strdup( logfileName );
    710 			else
    711 				rc = 1;
    712 			}
    713 			break;
    714 		case CFG_LOGFILE_FORMAT:
    715 			if ( logfile_format ) {
    716 				value_add_one( &c->rvalue_vals, &logformat_key[logfile_format].word );
    717 			} else {
    718 				rc = 1;
    719 			}
    720 			break;
    721 		case CFG_LOGFILE_ONLY:
    722 			c->value_int = logfile_only;
    723 			break;
    724 		case CFG_LOGFILE_ROTATE:
    725 			rc = 1;
    726 			if ( logfile_max ) {
    727 				char buf[64];
    728 				struct berval bv;
    729 				bv.bv_len = snprintf( buf, sizeof(buf), "%d %ld %ld", logfile_max,
    730 					(long) logfile_fslimit / 1048576, (long) logfile_age / 3600 );
    731 				if ( bv.bv_len > 0 && bv.bv_len < sizeof(buf) ) {
    732 					bv.bv_val = buf;
    733 					value_add_one( &c->rvalue_vals, &bv );
    734 					rc = 0;
    735 				}
    736 			}
    737 			break;
    738 		default:
    739 			rc = 1;
    740 		}
    741 		return rc;
    742 	} else if ( c->op == LDAP_MOD_DELETE ) {
    743 		switch(c->type) {
    744 		case CFG_LOGLEVEL:
    745 			if ( !c->line ) {
    746 				config_syslog = 0;
    747 			} else {
    748 				i = verb_to_mask( c->line, loglevel_ops );
    749 				config_syslog &= ~loglevel_ops[i].mask;
    750 			}
    751 			goto reset;
    752 
    753 		case CFG_LOGFILE:
    754 			logfile_close();
    755 			break;
    756 
    757 		case CFG_LOGFILE_FORMAT:
    758 			logfile_format = 0;
    759 			ch_free( syslog_prefix );
    760 			syslog_prefix = NULL;
    761 			break;
    762 
    763 		case CFG_LOGFILE_ONLY:
    764 			/* remove loglevel from debuglevel */
    765 			slap_debug = slap_debug_orig;
    766 			ldap_syslog = config_syslog;
    767 			break;
    768 
    769 		case CFG_LOGFILE_ROTATE:
    770 			logfile_max = logfile_fslimit = logfile_age = 0;
    771 			break;
    772 		default:
    773 			rc = 1;
    774 		}
    775 		return rc;
    776 	}
    777 
    778 	switch(c->type) {
    779 		case CFG_LOGLEVEL:
    780 			for( i=1; i < c->argc; i++ ) {
    781 				int	level;
    782 
    783 				if ( isdigit((unsigned char)c->argv[i][0]) || c->argv[i][0] == '-' ) {
    784 					if( lutil_atoix( &level, c->argv[i], 0 ) != 0 ) {
    785 						snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unable to parse level", c->argv[0] );
    786 						Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
    787 							c->log, c->cr_msg, c->argv[i]);
    788 						return( 1 );
    789 					}
    790 				} else {
    791 					if ( str2loglevel( c->argv[i], &level ) ) {
    792 						snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unknown level", c->argv[0] );
    793 						Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
    794 							c->log, c->cr_msg, c->argv[i]);
    795 						return( 1 );
    796 					}
    797 				}
    798 				/* Explicitly setting a zero clears all the levels */
    799 				if ( level )
    800 					config_syslog |= level;
    801 				else
    802 					config_syslog = 0;
    803 			}
    804 
    805 reset:
    806 			slap_debug = slap_debug_orig;
    807 			active_syslog = config_syslog;
    808 			if ( slapMode & SLAP_SERVER_MODE ) {
    809 				if ( logfile_only ) {
    810 					slap_debug |= config_syslog;
    811 					ldap_syslog = 0;
    812 				} else {
    813 					ldap_syslog = config_syslog;
    814 				}
    815 			}
    816 			rc = 0;
    817 			break;
    818 
    819 		case CFG_LOGFILE:
    820 			rc = logfile_open( c->value_string );
    821 			if ( rc ) {
    822 				char ebuf[128];
    823 				snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unable to open logfile, err=%d \"%s\"",
    824 					c->argv[0], rc, AC_STRERROR_R( rc, ebuf, sizeof(ebuf) ) );
    825 				Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
    826 					c->log, c->cr_msg, c->argv[1]);
    827 				return( 1 );
    828 			}
    829 			ch_free( c->value_string );
    830 			break;
    831 
    832 		case CFG_LOGFILE_FORMAT: {
    833 			int len;
    834 			i = verb_to_mask( c->argv[1], logformat_key );
    835 
    836 			if ( BER_BVISNULL( &logformat_key[ i ].word ) ) {
    837 				snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unknown format", c->argv[0] );
    838 				Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
    839 					c->log, c->cr_msg, c->argv[1]);
    840 				return( 1 );
    841 			}
    842 			if ( syslog_prefix )
    843 				ch_free( syslog_prefix );
    844 			logfile_format = logformat_key[i].mask;
    845 			len = strlen( global_host ) + 1 + strlen( serverName ) + 1 + sizeof(("[123456789]:")) +
    846 				(( logfile_format & LFMT_RFC3339) ? sizeof( RFC3339_STAMP ) : sizeof( SYSLOG_STAMP ));
    847 			syslog_prefix = ch_malloc( len );
    848 			splen = sprintf( syslog_prefix, "%s %s %s[%d]: ", ( logfile_format & LFMT_RFC3339 ) ?
    849 				RFC3339_STAMP : SYSLOG_STAMP, global_host, serverName, getpid() );
    850 			}
    851 			break;
    852 
    853 		case CFG_LOGFILE_ONLY:
    854 			logfile_only = c->value_int;
    855 			goto reset;
    856 
    857 		case CFG_LOGFILE_ROTATE: {
    858 			unsigned lf_max, lf_mbyte, lf_hour;
    859 			if ( lutil_atoux( &lf_max, c->argv[1], 0 ) != 0 ) {
    860 				snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
    861 					"invalid max value \"%s\"", c->argv[0], c->argv[1] );
    862 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
    863 					c->log, c->cr_msg );
    864 				return 1;
    865 			}
    866 			if ( !lf_max || lf_max > 99 ) {
    867 				snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
    868 					"invalid max value \"%s\" must be 1-99", c->argv[0], c->argv[1] );
    869 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
    870 					c->log, c->cr_msg );
    871 				return 1;
    872 			}
    873 			if ( lutil_atoux( &lf_mbyte, c->argv[2], 0 ) != 0 ) {
    874 				snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
    875 					"invalid Mbyte value \"%s\"", c->argv[0], c->argv[2] );
    876 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
    877 					c->log, c->cr_msg );
    878 				return 1;
    879 			}
    880 			if ( lutil_atoux( &lf_hour, c->argv[3], 0 ) != 0 ) {
    881 				snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
    882 					"invalid hours value \"%s\"", c->argv[0], c->argv[3] );
    883 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
    884 					c->log, c->cr_msg );
    885 				return 1;
    886 			}
    887 			if ( !lf_mbyte && !lf_hour ) {
    888 				snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
    889 					"Mbyte and hours cannot both be zero", c->argv[0] );
    890 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
    891 					c->log, c->cr_msg );
    892 				return 1;
    893 			}
    894 			logfile_max = lf_max;
    895 			logfile_fslimit = lf_mbyte * 1048576;	/* Megabytes to bytes */
    896 			logfile_age = lf_hour * 3600;			/* hours to seconds */
    897 			}
    898 			break;
    899 		default:
    900 			rc = 1;
    901 	}
    902 	return rc;
    903 }
    904