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