Home | History | Annotate | Line # | Download | only in slapd
      1 /*	$NetBSD: slapcommon.c,v 1.4 2025/09/05 21:16:26 christos Exp $	*/
      2 
      3 /* slapcommon.c - common routine for the slap tools */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 1998-2024 The OpenLDAP Foundation.
      8  * Portions Copyright 1998-2003 Kurt D. Zeilenga.
      9  * Portions Copyright 2003 IBM Corporation.
     10  * All rights reserved.
     11  *
     12  * Redistribution and use in source and binary forms, with or without
     13  * modification, are permitted only as authorized by the OpenLDAP
     14  * Public License.
     15  *
     16  * A copy of this license is available in file LICENSE in the
     17  * top-level directory of the distribution or, alternatively, at
     18  * <http://www.OpenLDAP.org/license.html>.
     19  */
     20 /* ACKNOWLEDGEMENTS:
     21  * This work was initially developed by Kurt Zeilenga for inclusion
     22  * in OpenLDAP Software.  Additional significant contributors include
     23  *    Jong Hyuk Choi
     24  *    Hallvard B. Furuseth
     25  *    Howard Chu
     26  *    Pierangelo Masarati
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __RCSID("$NetBSD: slapcommon.c,v 1.4 2025/09/05 21:16:26 christos Exp $");
     31 
     32 #include "portable.h"
     33 
     34 #include <stdio.h>
     35 
     36 #include <ac/stdlib.h>
     37 #include <ac/ctype.h>
     38 #include <ac/string.h>
     39 #include <ac/socket.h>
     40 #include <ac/unistd.h>
     41 
     42 #include "slapcommon.h"
     43 #include "lutil.h"
     44 #include "ldif.h"
     45 
     46 tool_vars tool_globals;
     47 enum slaptool slapTool;
     48 
     49 #ifdef CSRIMALLOC
     50 static char *leakfilename;
     51 static FILE *leakfile;
     52 #endif
     53 
     54 static LDIFFP dummy;
     55 
     56 #if defined(LDAP_SYSLOG) && defined(LDAP_DEBUG)
     57 int start_syslog;
     58 #ifdef LOG_LOCAL4
     59 static int syslogUser = SLAP_DEFAULT_SYSLOG_USER;
     60 #endif /* LOG_LOCAL4 */
     61 #endif /* LDAP_DEBUG && LDAP_SYSLOG */
     62 
     63 static void
     64 usage( int tool, const char *progname )
     65 {
     66 	char *options = NULL;
     67 	fprintf( stderr,
     68 		"usage: %s [-v] [-d debuglevel] [-f configfile] [-F configdir] [-o <name>[=<value>]]",
     69 		progname );
     70 
     71 	switch( tool ) {
     72 	case SLAPACL:
     73 		options = "\n\t[-U authcID | -D authcDN] [-X authzID | -o authzDN=<DN>]"
     74 			"\n\t-b DN [-u] [attr[/access][:value]] [...]\n";
     75 		break;
     76 
     77 	case SLAPADD:
     78 		options = " [-c]\n\t[-g] [-n databasenumber | -b suffix]\n"
     79 			"\t[-l ldiffile] [-j linenumber] [-q] [-u] [-s] [-w]\n";
     80 		break;
     81 
     82 	case SLAPAUTH:
     83 		options = "\n\t[-U authcID] [-X authzID] [-R realm] [-M mech] ID [...]\n";
     84 		break;
     85 
     86 	case SLAPCAT:
     87 		options = " [-c]\n\t[-g] [-n databasenumber | -b suffix]"
     88 			" [-l ldiffile] [-a filter] [-s subtree] [-H url]\n";
     89 		break;
     90 
     91 	case SLAPDN:
     92 		options = "\n\t[-N | -P] DN [...]\n";
     93 		break;
     94 
     95 	case SLAPINDEX:
     96 		options = " [-c]\n\t[-g] [-n databasenumber | -b suffix] [attr ...] [-q] [-t]\n";
     97 		break;
     98 
     99 	case SLAPMODIFY:
    100 		options = " [-c]\n\t[-g] [-n databasenumber | -b suffix]\n"
    101 			"\t[-l ldiffile] [-j linenumber] [-q] [-u] [-s] [-w]\n";
    102 		break;
    103 
    104 	case SLAPTEST:
    105 		options = " [-n databasenumber] [-u] [-Q]\n";
    106 		break;
    107 
    108 	case SLAPSCHEMA:
    109 		options = " [-c]\n\t[-g] [-n databasenumber | -b suffix]"
    110 			" [-l errorfile] [-a filter] [-s subtree] [-H url]\n";
    111 		break;
    112 	}
    113 
    114 	if ( options != NULL ) {
    115 		fputs( options, stderr );
    116 	}
    117 	exit( EXIT_FAILURE );
    118 }
    119 
    120 static int
    121 parse_slapopt( int tool, int *mode )
    122 {
    123 	size_t	len = 0;
    124 	char	*p;
    125 
    126 	p = strchr( optarg, '=' );
    127 	if ( p != NULL ) {
    128 		len = p - optarg;
    129 		p++;
    130 	}
    131 
    132 	if ( strncasecmp( optarg, "sockurl", len ) == 0 ) {
    133 		if ( !BER_BVISNULL( &listener_url ) ) {
    134 			ber_memfree( listener_url.bv_val );
    135 		}
    136 		ber_str2bv( p, 0, 1, &listener_url );
    137 
    138 	} else if ( strncasecmp( optarg, "domain", len ) == 0 ) {
    139 		if ( !BER_BVISNULL( &peer_domain ) ) {
    140 			ber_memfree( peer_domain.bv_val );
    141 		}
    142 		ber_str2bv( p, 0, 1, &peer_domain );
    143 
    144 	} else if ( strncasecmp( optarg, "peername", len ) == 0 ) {
    145 		if ( !BER_BVISNULL( &peer_name ) ) {
    146 			ber_memfree( peer_name.bv_val );
    147 		}
    148 		ber_str2bv( p, 0, 1, &peer_name );
    149 
    150 	} else if ( strncasecmp( optarg, "sockname", len ) == 0 ) {
    151 		if ( !BER_BVISNULL( &sock_name ) ) {
    152 			ber_memfree( sock_name.bv_val );
    153 		}
    154 		ber_str2bv( p, 0, 1, &sock_name );
    155 
    156 	} else if ( strncasecmp( optarg, "ssf", len ) == 0 ) {
    157 		if ( lutil_atou( &ssf, p ) ) {
    158 			Debug( LDAP_DEBUG_ANY, "unable to parse ssf=\"%s\".\n", p );
    159 			return -1;
    160 		}
    161 
    162 	} else if ( strncasecmp( optarg, "transport_ssf", len ) == 0 ) {
    163 		if ( lutil_atou( &transport_ssf, p ) ) {
    164 			Debug( LDAP_DEBUG_ANY, "unable to parse transport_ssf=\"%s\".\n", p );
    165 			return -1;
    166 		}
    167 
    168 	} else if ( strncasecmp( optarg, "tls_ssf", len ) == 0 ) {
    169 		if ( lutil_atou( &tls_ssf, p ) ) {
    170 			Debug( LDAP_DEBUG_ANY, "unable to parse tls_ssf=\"%s\".\n", p );
    171 			return -1;
    172 		}
    173 
    174 	} else if ( strncasecmp( optarg, "sasl_ssf", len ) == 0 ) {
    175 		if ( lutil_atou( &sasl_ssf, p ) ) {
    176 			Debug( LDAP_DEBUG_ANY, "unable to parse sasl_ssf=\"%s\".\n", p );
    177 			return -1;
    178 		}
    179 
    180 	} else if ( strncasecmp( optarg, "authzDN", len ) == 0 ) {
    181 		ber_str2bv( p, 0, 1, &authzDN );
    182 
    183 #if defined(LDAP_SYSLOG) && defined(LDAP_DEBUG)
    184 	} else if ( strncasecmp( optarg, "syslog", len ) == 0 ) {
    185 		if ( slap_parse_debug_level( p, &ldap_syslog, 1 ) ) {
    186 			return -1;
    187 		}
    188 		start_syslog = 1;
    189 
    190 	} else if ( strncasecmp( optarg, "syslog-level", len ) == 0 ) {
    191 		if ( slap_parse_syslog_level( p, &ldap_syslog_level ) ) {
    192 			return -1;
    193 		}
    194 		start_syslog = 1;
    195 
    196 #ifdef LOG_LOCAL4
    197 	} else if ( strncasecmp( optarg, "syslog-user", len ) == 0 ) {
    198 		if ( slap_parse_syslog_user( p, &syslogUser ) ) {
    199 			return -1;
    200 		}
    201 		start_syslog = 1;
    202 #endif /* LOG_LOCAL4 */
    203 #endif /* LDAP_DEBUG && LDAP_SYSLOG */
    204 
    205 	} else if ( strncasecmp( optarg, "schema-check", len ) == 0 ) {
    206 		switch ( tool ) {
    207 		case SLAPADD:
    208 			if ( strcasecmp( p, "yes" ) == 0 ) {
    209 				*mode &= ~SLAP_TOOL_NO_SCHEMA_CHECK;
    210 			} else if ( strcasecmp( p, "no" ) == 0 ) {
    211 				*mode |= SLAP_TOOL_NO_SCHEMA_CHECK;
    212 			} else {
    213 				Debug( LDAP_DEBUG_ANY, "unable to parse schema-check=\"%s\".\n", p );
    214 				return -1;
    215 			}
    216 			break;
    217 
    218 		default:
    219 			Debug( LDAP_DEBUG_ANY, "schema-check meaningless for tool.\n" );
    220 			break;
    221 		}
    222 
    223 	} else if ( strncasecmp( optarg, "value-check", len ) == 0 ) {
    224 		switch ( tool ) {
    225 		case SLAPADD:
    226 			if ( strcasecmp( p, "yes" ) == 0 ) {
    227 				*mode |= SLAP_TOOL_VALUE_CHECK;
    228 			} else if ( strcasecmp( p, "no" ) == 0 ) {
    229 				*mode &= ~SLAP_TOOL_VALUE_CHECK;
    230 			} else {
    231 				Debug( LDAP_DEBUG_ANY, "unable to parse value-check=\"%s\".\n", p );
    232 				return -1;
    233 			}
    234 			break;
    235 
    236 		default:
    237 			Debug( LDAP_DEBUG_ANY, "value-check meaningless for tool.\n" );
    238 			break;
    239 		}
    240 
    241 	} else if ( ( strncasecmp( optarg, "ldif_wrap", len ) == 0 ) ||
    242 			( strncasecmp( optarg, "ldif-wrap", len ) == 0 ) ) {
    243 		switch ( tool ) {
    244 		case SLAPCAT:
    245 			if ( strcasecmp( p, "no" ) == 0 ) {
    246 				ldif_wrap = LDIF_LINE_WIDTH_MAX;
    247 
    248 			} else {
    249 				unsigned int u;
    250 				if ( lutil_atou( &u, p ) ) {
    251 					Debug( LDAP_DEBUG_ANY, "unable to parse ldif_wrap=\"%s\".\n", p );
    252 					return -1;
    253 				}
    254 				ldif_wrap = (ber_len_t)u;
    255 			}
    256 			break;
    257 
    258 		default:
    259 			Debug( LDAP_DEBUG_ANY, "ldif-wrap meaningless for tool.\n" );
    260 			break;
    261 		}
    262 
    263 	} else {
    264 		return -1;
    265 	}
    266 
    267 	return 0;
    268 }
    269 
    270 /*
    271  * slap_tool_init - initialize slap utility, handle program options.
    272  * arguments:
    273  *	name		program name
    274  *	tool		tool code
    275  *	argc, argv	command line arguments
    276  */
    277 
    278 static int need_shutdown;
    279 
    280 void
    281 slap_tool_init(
    282 	const char* progname,
    283 	int tool,
    284 	int argc, char **argv )
    285 {
    286 	char *options;
    287 	char *conffile = NULL;
    288 	char *confdir = NULL;
    289 	struct berval base = BER_BVNULL;
    290 	char *filterstr = NULL;
    291 	char *subtree = NULL;
    292 	char *ldiffile	= NULL;
    293 	int rc, i;
    294 	int mode = SLAP_TOOL_MODE;
    295 	int truncatemode = 0;
    296 	int use_glue = 1;
    297 	int writer;
    298 
    299 #ifdef LDAP_DEBUG
    300 	/* tools default to "none", so that at least LDAP_DEBUG_ANY
    301 	 * messages show up; use -d 0 to reset */
    302 	slap_debug = LDAP_DEBUG_NONE;
    303 	ldif_debug = slap_debug;
    304 #endif
    305 	ldap_syslog = 0;
    306 	/* make sure libldap gets init'd */
    307 	ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &slap_debug );
    308 
    309 #ifdef CSRIMALLOC
    310 	leakfilename = malloc( strlen( progname ) + STRLENOF( ".leak" ) + 1 );
    311 	sprintf( leakfilename, "%s.leak", progname );
    312 	if( ( leakfile = fopen( leakfilename, "w" )) == NULL ) {
    313 		leakfile = stderr;
    314 	}
    315 	free( leakfilename );
    316 	leakfilename = NULL;
    317 #endif
    318 
    319 	ldif_wrap = LDIF_LINE_WIDTH;
    320 
    321 	scope = LDAP_SCOPE_DEFAULT;
    322 
    323 	switch( tool ) {
    324 	case SLAPADD:
    325 		options = "b:cd:f:F:gj:l:n:o:qsS:uvw";
    326 		break;
    327 
    328 	case SLAPCAT:
    329 		options = "a:b:cd:f:F:gH:l:n:o:s:v";
    330 		mode |= SLAP_TOOL_READMAIN | SLAP_TOOL_READONLY;
    331 		break;
    332 
    333 	case SLAPDN:
    334 		options = "d:f:F:No:Pv";
    335 		mode |= SLAP_TOOL_READMAIN | SLAP_TOOL_READONLY;
    336 		break;
    337 
    338 	case SLAPMODIFY:
    339 		options = "b:cd:f:F:gj:l:n:o:qsS:uvw";
    340 		break;
    341 
    342 	case SLAPSCHEMA:
    343 		options = "a:b:cd:f:F:gH:l:n:o:s:v";
    344 		mode |= SLAP_TOOL_READMAIN | SLAP_TOOL_READONLY;
    345 		break;
    346 
    347 	case SLAPTEST:
    348 		options = "d:f:F:n:o:Quv";
    349 		mode |= SLAP_TOOL_READMAIN | SLAP_TOOL_READONLY;
    350 		break;
    351 
    352 	case SLAPAUTH:
    353 		options = "d:f:F:M:o:R:U:vX:";
    354 		mode |= SLAP_TOOL_READMAIN | SLAP_TOOL_READONLY;
    355 		break;
    356 
    357 	case SLAPINDEX:
    358 		options = "b:cd:f:F:gn:o:qtv";
    359 		mode |= SLAP_TOOL_READMAIN;
    360 		break;
    361 
    362 	case SLAPACL:
    363 		options = "b:D:d:f:F:o:uU:vX:";
    364 		mode |= SLAP_TOOL_READMAIN | SLAP_TOOL_READONLY;
    365 		break;
    366 
    367 	default:
    368 		fprintf( stderr, "%s: unknown tool mode (%d)\n", progname, tool );
    369 		exit( EXIT_FAILURE );
    370 	}
    371 
    372 	dbnum = -1;
    373 	while ( (i = getopt( argc, argv, options )) != EOF ) {
    374 		switch ( i ) {
    375 		case 'a':
    376 			filterstr = optarg;
    377 			break;
    378 
    379 		case 'b':
    380 			ber_str2bv( optarg, 0, 1, &base );
    381 			break;
    382 
    383 		case 'c':	/* enable continue mode */
    384 			continuemode++;
    385 			break;
    386 
    387 		case 'd': {	/* turn on debugging */
    388 			int	level = 0;
    389 
    390 			if ( slap_parse_debug_level( optarg, &level, 0 ) ) {
    391 				usage( tool, progname );
    392 			}
    393 #ifdef LDAP_DEBUG
    394 			if ( level == 0 ) {
    395 				/* allow to reset log level */
    396 				slap_debug = 0;
    397 
    398 			} else {
    399 				slap_debug |= level;
    400 			}
    401 #else
    402 			if ( level != 0 )
    403 				fputs( "must compile with LDAP_DEBUG for debugging\n",
    404 				       stderr );
    405 #endif
    406 			} break;
    407 
    408 		case 'D':
    409 			ber_str2bv( optarg, 0, 1, &authcDN );
    410 			break;
    411 
    412 		case 'f':	/* specify a conf file */
    413 			conffile = optarg;
    414 			break;
    415 
    416 		case 'F':	/* specify a conf dir */
    417 			confdir = optarg;
    418 			break;
    419 
    420 		case 'g':	/* disable subordinate glue */
    421 			use_glue = 0;
    422 			break;
    423 
    424 		case 'H': {
    425 			LDAPURLDesc *ludp;
    426 			int rc;
    427 
    428 			rc = ldap_url_parse_ext( optarg, &ludp,
    429 				LDAP_PVT_URL_PARSE_NOEMPTY_HOST | LDAP_PVT_URL_PARSE_NOEMPTY_DN );
    430 			if ( rc != LDAP_URL_SUCCESS ) {
    431 				fprintf( stderr, "Cannot parse '%s' as LDAP URI.\n", optarg );
    432 				usage( tool, progname );
    433 			}
    434 
    435 			/* don't accept host, port, attrs, extensions */
    436 			if ( ldap_pvt_url_scheme2proto( ludp->lud_scheme ) != LDAP_PROTO_TCP ) {
    437 				fprintf( stderr, "%s URIs need to use ldap:// scheme.\n",
    438 						progname );
    439 				usage( tool, progname );
    440 			}
    441 
    442 			if ( ludp->lud_host != NULL ) {
    443 				fprintf( stderr, "%s URIs cannot carry a host. "
    444 						"Only base, scope and filter are accepted\n",
    445 						progname );
    446 				usage( tool, progname );
    447 			}
    448 
    449 			if ( ludp->lud_port != 0 ) {
    450 				fprintf( stderr, "%s URIs cannot carry a port. "
    451 						"Only base, scope and filter are accepted\n",
    452 						progname );
    453 				usage( tool, progname );
    454 			}
    455 
    456 			if ( ludp->lud_attrs != NULL ) {
    457 				fprintf( stderr, "%s URIs cannot carry an attribute specification. "
    458 						"Only base, scope and filter are accepted\n",
    459 						progname );
    460 				usage( tool, progname );
    461 			}
    462 
    463 			if ( ludp->lud_exts != NULL ) {
    464 				fprintf( stderr, "%s URIs cannot carry an extension specification. "
    465 						"Only base, scope and filter are accepted\n",
    466 						progname );
    467 				usage( tool, progname );
    468 			}
    469 
    470 			if ( ludp->lud_dn != NULL && ludp->lud_dn[0] != '\0' ) {
    471 				ch_free( subtree );
    472 				subtree = ludp->lud_dn;
    473 				ludp->lud_dn = NULL;
    474 			}
    475 
    476 			if ( ludp->lud_filter != NULL && ludp->lud_filter[0] != '\0' ) {
    477 				filterstr = ludp->lud_filter;
    478 				ludp->lud_filter = NULL;
    479 			}
    480 
    481 			scope = ludp->lud_scope;
    482 
    483 			ldap_free_urldesc( ludp );
    484 			} break;
    485 
    486 		case 'j':	/* jump to linenumber */
    487 			if ( lutil_atoul( &jumpline, optarg ) ) {
    488 				fprintf( stderr, "Invalid line number '%s'\n", optarg );
    489 				usage( tool, progname );
    490 			}
    491 			break;
    492 
    493 		case 'l':	/* LDIF file */
    494 			ldiffile = optarg;
    495 			break;
    496 
    497 		case 'M':
    498 			ber_str2bv( optarg, 0, 0, &mech );
    499 			break;
    500 
    501 		case 'N':
    502 			if ( dn_mode && dn_mode != SLAP_TOOL_LDAPDN_NORMAL ) {
    503 				fputs( "Invalid combination of -N/-P provided\n", stderr );
    504 				usage( tool, progname );
    505 			}
    506 			dn_mode = SLAP_TOOL_LDAPDN_NORMAL;
    507 			break;
    508 
    509 		case 'n':	/* which config file db to index */
    510 			if ( lutil_atoi( &dbnum, optarg ) || dbnum < 0 ) {
    511 				fputs( "Invalid database index provided\n", stderr );
    512 				usage( tool, progname );
    513 			}
    514 			break;
    515 
    516 		case 'o':
    517 			if ( parse_slapopt( tool, &mode ) ) {
    518 				usage( tool, progname );
    519 			}
    520 			break;
    521 
    522 		case 'P':
    523 			if ( dn_mode && dn_mode != SLAP_TOOL_LDAPDN_PRETTY ) {
    524 				fputs( "Invalid combination of -N/-P provided\n", stderr );
    525 				usage( tool, progname );
    526 			}
    527 			dn_mode = SLAP_TOOL_LDAPDN_PRETTY;
    528 			break;
    529 
    530 		case 'Q':
    531 			quiet++;
    532 			slap_debug = 0;
    533 			break;
    534 
    535 		case 'q':	/* turn on quick */
    536 			mode |= SLAP_TOOL_QUICK;
    537 			break;
    538 
    539 		case 'R':
    540 			realm = optarg;
    541 			break;
    542 
    543 		case 'S':
    544 			if ( lutil_atou( &csnsid, optarg )
    545 				|| csnsid > SLAP_SYNC_SID_MAX )
    546 			{
    547 				fputs( "Invalid serverid provided\n", stderr );
    548 				usage( tool, progname );
    549 			}
    550 			break;
    551 
    552 		case 's':
    553 			switch ( tool ) {
    554 			case SLAPADD:
    555 			case SLAPMODIFY:
    556 				/* no schema check */
    557 				mode |= SLAP_TOOL_NO_SCHEMA_CHECK;
    558 				break;
    559 
    560 			case SLAPCAT:
    561 			case SLAPSCHEMA:
    562 				/* dump subtree */
    563 				ch_free( subtree );
    564 				subtree = ch_strdup( optarg );
    565 				break;
    566 			}
    567 			break;
    568 
    569 		case 't':	/* turn on truncate */
    570 			truncatemode++;
    571 			mode |= SLAP_TRUNCATE_MODE;
    572 			break;
    573 
    574 		case 'U':
    575 			ber_str2bv( optarg, 0, 0, &authcID );
    576 			break;
    577 
    578 		case 'u':	/* dry run */
    579 			dryrun++;
    580 			mode |= SLAP_TOOL_DRYRUN;
    581 			break;
    582 
    583 		case 'v':	/* turn on verbose */
    584 			verbose++;
    585 			break;
    586 
    587 		case 'w':	/* write context csn at the end */
    588 			update_ctxcsn++;
    589 			break;
    590 
    591 		case 'X':
    592 			ber_str2bv( optarg, 0, 0, &authzID );
    593 			break;
    594 
    595 		default:
    596 			usage( tool, progname );
    597 			break;
    598 		}
    599 	}
    600 	slap_debug_orig = slap_debug;
    601 
    602 #if defined(LDAP_SYSLOG) && defined(LDAP_DEBUG)
    603 	if ( start_syslog ) {
    604 		char *logName;
    605 #ifdef HAVE_EBCDIC
    606 		logName = ch_strdup( progname );
    607 		__atoe( logName );
    608 #else
    609 		logName = (char *)progname;
    610 #endif
    611 
    612 #ifdef LOG_LOCAL4
    613 		openlog( logName, OPENLOG_OPTIONS, syslogUser );
    614 #elif defined LOG_DEBUG
    615 		openlog( logName, OPENLOG_OPTIONS );
    616 #endif
    617 #ifdef HAVE_EBCDIC
    618 		free( logName );
    619 		logName = NULL;
    620 #endif
    621 	}
    622 #endif /* LDAP_DEBUG && LDAP_SYSLOG */
    623 
    624 	switch ( tool ) {
    625 	case SLAPCAT:
    626 	case SLAPSCHEMA:
    627 		writer = 1;
    628 		break;
    629 
    630 	default:
    631 		writer = 0;
    632 		break;
    633 	}
    634 
    635 	switch ( tool ) {
    636 	case SLAPADD:
    637 	case SLAPCAT:
    638 	case SLAPMODIFY:
    639 	case SLAPSCHEMA:
    640 		if ( ( argc != optind ) || (dbnum >= 0 && base.bv_val != NULL ) ) {
    641 			usage( tool, progname );
    642 		}
    643 
    644 		break;
    645 
    646 	case SLAPINDEX:
    647 		if ( dbnum >= 0 && base.bv_val != NULL ) {
    648 			usage( tool, progname );
    649 		}
    650 
    651 		break;
    652 
    653 	case SLAPDN:
    654 		if ( argc == optind ) {
    655 			usage( tool, progname );
    656 		}
    657 		break;
    658 
    659 	case SLAPAUTH:
    660 		if ( argc == optind && BER_BVISNULL( &authcID ) ) {
    661 			usage( tool, progname );
    662 		}
    663 		break;
    664 
    665 	case SLAPTEST:
    666 		if ( argc != optind ) {
    667 			usage( tool, progname );
    668 		}
    669 		break;
    670 
    671 	case SLAPACL:
    672 		if ( !BER_BVISNULL( &authcDN ) && !BER_BVISNULL( &authcID ) ) {
    673 			usage( tool, progname );
    674 		}
    675 		if ( BER_BVISNULL( &base ) ) {
    676 			usage( tool, progname );
    677 		}
    678 		ber_dupbv( &baseDN, &base );
    679 		break;
    680 
    681 	default:
    682 		break;
    683 	}
    684 
    685 	if ( ldiffile == NULL ) {
    686 		dummy.fp = writer ? stdout : stdin;
    687 		ldiffp = &dummy;
    688 
    689 	} else if ((ldiffp = ldif_open( ldiffile, writer ? "w" : "r" ))
    690 		== NULL )
    691 	{
    692 		perror( ldiffile );
    693 		exit( EXIT_FAILURE );
    694 	}
    695 
    696 	/*
    697 	 * initialize stuff and figure out which backend we're dealing with
    698 	 */
    699 
    700 	slapTool = tool;
    701 	rc = slap_init( mode, progname );
    702 	if ( rc != 0 ) {
    703 		fprintf( stderr, "%s: slap_init failed!\n", progname );
    704 		exit( EXIT_FAILURE );
    705 	}
    706 
    707 	rc = read_config( conffile, confdir );
    708 
    709 	if ( rc != 0 ) {
    710 		fprintf( stderr, "%s: bad configuration %s!\n",
    711 			progname, confdir ? "directory" : "file" );
    712 		exit( EXIT_FAILURE );
    713 	}
    714 
    715 	rc = slap_parse_debug_unknowns();
    716 	if ( rc )
    717 		exit( EXIT_FAILURE );
    718 
    719 	at_oc_cache = 1;
    720 
    721 	switch ( tool ) {
    722 	case SLAPADD:
    723 	case SLAPCAT:
    724 	case SLAPINDEX:
    725 	case SLAPMODIFY:
    726 	case SLAPSCHEMA:
    727 		if ( !nbackends ) {
    728 			fprintf( stderr, "No databases found "
    729 					"in config file\n" );
    730 			exit( EXIT_FAILURE );
    731 		}
    732 		break;
    733 
    734 	default:
    735 		break;
    736 	}
    737 
    738 	if ( use_glue ) {
    739 		rc = glue_sub_attach( 0 );
    740 
    741 		if ( rc != 0 ) {
    742 			fprintf( stderr,
    743 				"%s: subordinate configuration error\n", progname );
    744 			exit( EXIT_FAILURE );
    745 		}
    746 	}
    747 
    748 	rc = slap_schema_check();
    749 
    750 	if ( rc != 0 ) {
    751 		fprintf( stderr, "%s: slap_schema_prep failed!\n", progname );
    752 		exit( EXIT_FAILURE );
    753 	}
    754 
    755 	switch ( tool ) {
    756 	case SLAPTEST:
    757 		if ( dbnum >= 0 )
    758 			goto get_db;
    759 		/* FALLTHRU */
    760 	case SLAPDN:
    761 	case SLAPAUTH:
    762 		be = NULL;
    763 		goto startup;
    764 
    765 	default:
    766 		break;
    767 	}
    768 
    769 	if( filterstr ) {
    770 		filter = str2filter( filterstr );
    771 
    772 		if( filter == NULL ) {
    773 			fprintf( stderr, "Invalid filter '%s'\n", filterstr );
    774 			exit( EXIT_FAILURE );
    775 		}
    776 	}
    777 
    778 	if( subtree ) {
    779 		struct berval val;
    780 		ber_str2bv( subtree, 0, 0, &val );
    781 		rc = dnNormalize( 0, NULL, NULL, &val, &sub_ndn, NULL );
    782 		if( rc != LDAP_SUCCESS ) {
    783 			fprintf( stderr, "Invalid subtree DN '%s'\n", subtree );
    784 			exit( EXIT_FAILURE );
    785 		}
    786 
    787 		if ( BER_BVISNULL( &base ) && dbnum == -1 ) {
    788 			base = val;
    789 		} else {
    790 			free( subtree );
    791 			subtree = NULL;
    792 		}
    793 	}
    794 
    795 	if( base.bv_val != NULL ) {
    796 		struct berval nbase;
    797 
    798 		rc = dnNormalize( 0, NULL, NULL, &base, &nbase, NULL );
    799 		if( rc != LDAP_SUCCESS ) {
    800 			fprintf( stderr, "%s: slap_init invalid suffix (\"%s\")\n",
    801 				progname, base.bv_val );
    802 			exit( EXIT_FAILURE );
    803 		}
    804 
    805 		be = select_backend( &nbase, 0 );
    806 		ber_memfree( nbase.bv_val );
    807 		BER_BVZERO( &nbase );
    808 
    809 		if( be == NULL ) {
    810 			fprintf( stderr, "%s: slap_init no backend for \"%s\"\n",
    811 				progname, base.bv_val );
    812 			exit( EXIT_FAILURE );
    813 		}
    814 		switch ( tool ) {
    815 		case SLAPACL:
    816 			goto startup;
    817 
    818 		default:
    819 			break;
    820 		}
    821 
    822 		/* If the named base is a glue primary, operate on the
    823 		 * entire context
    824 		 */
    825 		if ( SLAP_GLUE_INSTANCE( be ) ) {
    826 			nosubordinates = 1;
    827 		}
    828 
    829 		ch_free( base.bv_val );
    830 		BER_BVZERO( &base );
    831 
    832 	} else if ( dbnum == -1 ) {
    833 		/* no suffix and no dbnum specified, just default to
    834 		 * the first available database
    835 		 */
    836 		if ( nbackends <= 0 ) {
    837 			fprintf( stderr, "No available databases\n" );
    838 			exit( EXIT_FAILURE );
    839 		}
    840 		LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
    841 			dbnum++;
    842 
    843 			/* db #0 is cn=config, don't select it as a default */
    844 			if ( dbnum < 1 ) continue;
    845 
    846 		 	if ( SLAP_MONITOR(be))
    847 				continue;
    848 
    849 		/* If just doing the first by default and it is a
    850 		 * glue subordinate, find the primary.
    851 		 */
    852 			if ( SLAP_GLUE_SUBORDINATE(be) ) {
    853 				nosubordinates = 1;
    854 				continue;
    855 			}
    856 			break;
    857 		}
    858 
    859 		if ( !be ) {
    860 			fprintf( stderr, "Available database(s) "
    861 					"do not allow %s\n", progname );
    862 			exit( EXIT_FAILURE );
    863 		}
    864 
    865 		if ( nosubordinates == 0 && dbnum > 1 ) {
    866 			Debug( LDAP_DEBUG_ANY,
    867 				"The first database does not allow %s;"
    868 				" using the first available one (%d)\n",
    869 				progname, dbnum );
    870 		}
    871 
    872 	} else if ( dbnum >= nbackends ) {
    873 		fprintf( stderr,
    874 			"Database number selected via -n is out of range\n"
    875 			"Must be in the range 0 to %d"
    876 			" (the number of configured databases)\n",
    877 			nbackends - 1 );
    878 		exit( EXIT_FAILURE );
    879 
    880 	} else {
    881 get_db:
    882 		LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
    883 			if ( dbnum == 0 ) break;
    884 			dbnum--;
    885 		}
    886 	}
    887 
    888 	if ( scope != LDAP_SCOPE_DEFAULT && BER_BVISNULL( &sub_ndn ) ) {
    889 		if ( be && be->be_nsuffix ) {
    890 			ber_dupbv( &sub_ndn, be->be_nsuffix );
    891 
    892 		} else {
    893 			fprintf( stderr,
    894 				"<scope> needs a DN or a valid database\n" );
    895 			exit( EXIT_FAILURE );
    896 		}
    897 	}
    898 
    899 startup:;
    900 	if ( be ) {
    901 		BackendDB *bdtmp;
    902 
    903 		dbnum = 0;
    904 		LDAP_STAILQ_FOREACH( bdtmp, &backendDB, be_next ) {
    905 			if ( bdtmp == be ) break;
    906 			dbnum++;
    907 		}
    908 	}
    909 
    910 #ifdef CSRIMALLOC
    911 	mal_leaktrace(1);
    912 #endif
    913 
    914 
    915 	/* slapdn doesn't specify a backend to startup */
    916 	if ( !dryrun && tool != SLAPDN ) {
    917 		need_shutdown = 1;
    918 
    919 		if ( slap_startup( be ) ) {
    920 			switch ( tool ) {
    921 			case SLAPTEST:
    922 				fprintf( stderr, "slap_startup failed "
    923 						"(test would succeed using "
    924 						"the -u switch)\n" );
    925 				break;
    926 
    927 			default:
    928 				fprintf( stderr, "slap_startup failed\n" );
    929 				break;
    930 			}
    931 
    932 			exit( EXIT_FAILURE );
    933 		}
    934 	}
    935 }
    936 
    937 int slap_tool_destroy( void )
    938 {
    939 	int rc = 0;
    940 	if ( !dryrun ) {
    941 		if ( need_shutdown ) {
    942 			if ( slap_shutdown( be ))
    943 				rc = EXIT_FAILURE;
    944 		}
    945 		if ( slap_destroy())
    946 			rc = EXIT_FAILURE;
    947 	}
    948 #ifdef SLAPD_MODULES
    949 	if ( slapMode == SLAP_SERVER_MODE ) {
    950 	/* always false. just pulls in necessary symbol references. */
    951 		lutil_uuidstr(NULL, 0);
    952 	}
    953 	module_kill();
    954 #endif
    955 	schema_destroy();
    956 #ifdef HAVE_TLS
    957 	ldap_pvt_tls_destroy();
    958 #endif
    959 	config_destroy();
    960 
    961 #ifdef CSRIMALLOC
    962 	mal_dumpleaktrace( leakfile );
    963 #endif
    964 
    965 	if ( !BER_BVISNULL( &authcDN ) ) {
    966 		ch_free( authcDN.bv_val );
    967 		BER_BVZERO( &authcDN );
    968 	}
    969 
    970 	if ( ldiffp && ldiffp != &dummy ) {
    971 		ldif_close( ldiffp );
    972 	}
    973 	return rc;
    974 }
    975 
    976 int
    977 slap_tool_update_ctxcsn(
    978 	const char *progname,
    979 	unsigned long sid,
    980 	struct berval *bvtext )
    981 {
    982 	struct berval ctxdn;
    983 	ID ctxcsn_id;
    984 	Entry *ctxcsn_e;
    985 	int rc = EXIT_SUCCESS;
    986 
    987 	if ( !(update_ctxcsn && !dryrun && sid != SLAP_SYNC_SID_MAX + 1) ) {
    988 		return rc;
    989 	}
    990 
    991 	if ( SLAP_SYNC_SUBENTRY( be )) {
    992 		build_new_dn( &ctxdn, &be->be_nsuffix[0],
    993 			(struct berval *)&slap_ldapsync_cn_bv, NULL );
    994 	} else {
    995 		ctxdn = be->be_nsuffix[0];
    996 	}
    997 	ctxcsn_id = be->be_dn2id_get( be, &ctxdn );
    998 	if ( ctxcsn_id == NOID ) {
    999 		if ( SLAP_SYNC_SUBENTRY( be )) {
   1000 			ctxcsn_e = slap_create_context_csn_entry( be, NULL );
   1001 			for ( sid = 0; sid <= SLAP_SYNC_SID_MAX; sid++ ) {
   1002 				if ( maxcsn[ sid ].bv_len ) {
   1003 					attr_merge_one( ctxcsn_e, slap_schema.si_ad_contextCSN,
   1004 						&maxcsn[ sid ], NULL );
   1005 				}
   1006 			}
   1007 			ctxcsn_id = be->be_entry_put( be, ctxcsn_e, bvtext );
   1008 			if ( ctxcsn_id == NOID ) {
   1009 				fprintf( stderr, "%s: couldn't create context entry\n", progname );
   1010 				rc = EXIT_FAILURE;
   1011 			}
   1012 			entry_free( ctxcsn_e );
   1013 		} else {
   1014 			fprintf( stderr, "%s: context entry is missing\n", progname );
   1015 			rc = EXIT_FAILURE;
   1016 		}
   1017 	} else {
   1018 		ctxcsn_e = be->be_entry_get( be, ctxcsn_id );
   1019 		if ( ctxcsn_e != NULL ) {
   1020 			Operation op = { 0 };
   1021 			Entry *e = entry_dup( ctxcsn_e );
   1022 			Attribute *attr = attr_find( e->e_attrs, slap_schema.si_ad_contextCSN );
   1023 
   1024 			int change;
   1025 			op.o_bd = be;
   1026 			be_entry_release_r( &op, ctxcsn_e );
   1027 
   1028 			if ( attr ) {
   1029 				int		i;
   1030 
   1031 				change = 0;
   1032 
   1033 				for ( i = 0; !BER_BVISNULL( &attr->a_nvals[ i ] ); i++ ) {
   1034 					int rc_sid;
   1035 					int match;
   1036 					const char *text = NULL;
   1037 
   1038 					rc_sid = slap_parse_csn_sid( &attr->a_nvals[ i ] );
   1039 					if ( rc_sid < 0 ) {
   1040 						Debug( LDAP_DEBUG_ANY,
   1041 							"%s: unable to extract SID "
   1042 							"from #%d contextCSN=%s\n",
   1043 							progname, i,
   1044 							attr->a_nvals[ i ].bv_val );
   1045 						continue;
   1046 					}
   1047 
   1048 					assert( rc_sid <= SLAP_SYNC_SID_MAX );
   1049 
   1050 					sid = (unsigned)rc_sid;
   1051 
   1052 					if ( maxcsn[ sid ].bv_len == 0 ) {
   1053 						match = -1;
   1054 
   1055 					} else {
   1056 						value_match( &match, slap_schema.si_ad_entryCSN,
   1057 							slap_schema.si_ad_entryCSN->ad_type->sat_ordering,
   1058 							SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
   1059 							&maxcsn[ sid ], &attr->a_nvals[i], &text );
   1060 					}
   1061 
   1062 					if ( match > 0 ) {
   1063 						change = 1;
   1064 					} else {
   1065 						AC_MEMCPY( maxcsn[ sid ].bv_val,
   1066 							attr->a_nvals[ i ].bv_val,
   1067 							attr->a_nvals[ i ].bv_len );
   1068 						maxcsn[ sid ].bv_val[ attr->a_nvals[ i ].bv_len ] = '\0';
   1069 						maxcsn[ sid ].bv_len = attr->a_nvals[ i ].bv_len;
   1070 					}
   1071 				}
   1072 
   1073 				if ( change ) {
   1074 					if ( attr->a_nvals != attr->a_vals ) {
   1075 						ber_bvarray_free( attr->a_nvals );
   1076 					}
   1077 					attr->a_nvals = NULL;
   1078 					ber_bvarray_free( attr->a_vals );
   1079 					attr->a_vals = NULL;
   1080 					attr->a_numvals = 0;
   1081 				}
   1082 			} else {
   1083 				change = 1;
   1084 			}
   1085 
   1086 			if ( change ) {
   1087 				for ( sid = 0; sid <= SLAP_SYNC_SID_MAX; sid++ ) {
   1088 					if ( maxcsn[ sid ].bv_len ) {
   1089 						attr_merge_one( e, slap_schema.si_ad_contextCSN,
   1090 							&maxcsn[ sid], NULL );
   1091 					}
   1092 				}
   1093 
   1094 				ctxcsn_id = be->be_entry_modify( be, e, bvtext );
   1095 				if( ctxcsn_id == NOID ) {
   1096 					fprintf( stderr, "%s: could not modify ctxcsn (%s)\n",
   1097 						progname, bvtext->bv_val ? bvtext->bv_val : "" );
   1098 					rc = EXIT_FAILURE;
   1099 				} else if ( verbose ) {
   1100 					fprintf( stderr, "modified: \"%s\" (%08lx)\n",
   1101 						e->e_dn, (long) ctxcsn_id );
   1102 				}
   1103 			}
   1104 			entry_free( e );
   1105 		}
   1106 	}
   1107 
   1108 	return rc;
   1109 }
   1110 
   1111 /*
   1112  * return value:
   1113  *	-1:			update_ctxcsn == 0
   1114  *	SLAP_SYNC_SID_MAX + 1:	unable to extract SID
   1115  *	0 <= SLAP_SYNC_SID_MAX:	the SID
   1116  */
   1117 unsigned long
   1118 slap_tool_update_ctxcsn_check(
   1119 	const char *progname,
   1120 	Entry *e )
   1121 {
   1122 	if ( update_ctxcsn ) {
   1123 		unsigned long sid = SLAP_SYNC_SID_MAX + 1;
   1124 		int rc_sid;
   1125 		Attribute *attr;
   1126 
   1127 		attr = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN );
   1128 		assert( attr != NULL );
   1129 
   1130 		rc_sid = slap_parse_csn_sid( &attr->a_nvals[ 0 ] );
   1131 		if ( rc_sid < 0 ) {
   1132 			Debug( LDAP_DEBUG_ANY, "%s: could not "
   1133 				"extract SID from entryCSN=%s, entry dn=\"%s\"\n",
   1134 				progname, attr->a_nvals[ 0 ].bv_val, e->e_name.bv_val );
   1135 			return (unsigned long)(-1);
   1136 
   1137 		} else {
   1138 			int match;
   1139 			const char *text = NULL;
   1140 
   1141 			assert( rc_sid <= SLAP_SYNC_SID_MAX );
   1142 
   1143 			sid = (unsigned)rc_sid;
   1144 			if ( maxcsn[ sid ].bv_len != 0 ) {
   1145 				match = 0;
   1146 				value_match( &match, slap_schema.si_ad_entryCSN,
   1147 					slap_schema.si_ad_entryCSN->ad_type->sat_ordering,
   1148 					SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
   1149 					&maxcsn[ sid ], &attr->a_nvals[0], &text );
   1150 			} else {
   1151 				match = -1;
   1152 			}
   1153 			if ( match < 0 ) {
   1154 				strcpy( maxcsn[ sid ].bv_val, attr->a_nvals[0].bv_val );
   1155 				maxcsn[ sid ].bv_len = attr->a_nvals[0].bv_len;
   1156 			}
   1157 		}
   1158 	}
   1159 
   1160 	return (unsigned long)(-1);
   1161 }
   1162 
   1163 int
   1164 slap_tool_update_ctxcsn_init(void)
   1165 {
   1166 	if ( update_ctxcsn ) {
   1167 		unsigned long sid;
   1168 		maxcsn[ 0 ].bv_val = maxcsnbuf;
   1169 		for ( sid = 1; sid <= SLAP_SYNC_SID_MAX; sid++ ) {
   1170 			maxcsn[ sid ].bv_val = maxcsn[ sid - 1 ].bv_val + LDAP_PVT_CSNSTR_BUFSIZE;
   1171 			maxcsn[ sid ].bv_len = 0;
   1172 		}
   1173 	}
   1174 
   1175 	return 0;
   1176 }
   1177 
   1178 int
   1179 slap_tool_entry_check(
   1180 	const char *progname,
   1181 	Operation *op,
   1182 	Entry *e,
   1183 	int lineno,
   1184 	const char **text,
   1185 	char *textbuf,
   1186 	size_t textlen )
   1187 {
   1188 	/* NOTE: we may want to conditionally enable manage */
   1189 	int manage = 0;
   1190 
   1191 	Attribute *oc = attr_find( e->e_attrs,
   1192 		slap_schema.si_ad_objectClass );
   1193 
   1194 	if( oc == NULL ) {
   1195 		fprintf( stderr, "%s: dn=\"%s\" (line=%d): %s\n",
   1196 			progname, e->e_dn, lineno,
   1197 			"no objectClass attribute");
   1198 		return LDAP_NO_SUCH_ATTRIBUTE;
   1199 	}
   1200 
   1201 	/* check schema */
   1202 	op->o_bd = be;
   1203 
   1204 	if ( (slapMode & SLAP_TOOL_NO_SCHEMA_CHECK) == 0) {
   1205 		int rc = entry_schema_check( op, e, NULL, manage, 1, NULL,
   1206 			text, textbuf, textlen );
   1207 
   1208 		if( rc != LDAP_SUCCESS ) {
   1209 			fprintf( stderr, "%s: dn=\"%s\" (line=%d): (%d) %s\n",
   1210 				progname, e->e_dn, lineno, rc, *text );
   1211 			return rc;
   1212 		}
   1213 		textbuf[ 0 ] = '\0';
   1214 	}
   1215 
   1216 	if ( (slapMode & SLAP_TOOL_VALUE_CHECK) != 0) {
   1217 		Modifications *ml = NULL;
   1218 
   1219 		int rc = slap_entry2mods( e, &ml, text, textbuf, textlen );
   1220 		if ( rc != LDAP_SUCCESS ) {
   1221 			fprintf( stderr, "%s: dn=\"%s\" (line=%d): (%d) %s\n",
   1222 				progname, e->e_dn, lineno, rc, *text );
   1223 			return rc;
   1224 		}
   1225 		textbuf[ 0 ] = '\0';
   1226 
   1227 		rc = slap_mods_check( op, ml, text, textbuf, textlen, NULL );
   1228 		slap_mods_free( ml, 1 );
   1229 		if ( rc != LDAP_SUCCESS ) {
   1230 			fprintf( stderr, "%s: dn=\"%s\" (line=%d): (%d) %s\n",
   1231 				progname, e->e_dn, lineno, rc, *text );
   1232 			return rc;
   1233 		}
   1234 		textbuf[ 0 ] = '\0';
   1235 	}
   1236 
   1237 	return LDAP_SUCCESS;
   1238 }
   1239 
   1240