slapadd.c 11.2 KB
Newer Older
1
2
3
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
4
 * Copyright 1998-2013 The OpenLDAP Foundation.
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 * Portions Copyright 1998-2003 Kurt D. Zeilenga.
 * Portions Copyright 2003 IBM Corporation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by Kurt Zeilenga for inclusion
 * in OpenLDAP Software.  Additional signficant contributors include
 *    Jong Hyuk Choi
 *    Pierangelo Masarati
 */

#include "portable.h"

#include <stdio.h>

#include <ac/stdlib.h>

#include <ac/ctype.h>
#include <ac/string.h>
#include <ac/socket.h>
#include <ac/unistd.h>

#include <lber.h>
#include <ldif.h>
#include <lutil.h>
Howard Chu's avatar
Howard Chu committed
38
39
#include <lutil_meter.h>
#include <sys/stat.h>
40
41
42

#include "slapcommon.h"

43
static char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
44

45
46
typedef struct Erec {
	Entry *e;
47
48
	unsigned long lineno;
	unsigned long nextline;
49
50
51
52
} Erec;

typedef struct Trec {
	Entry *e;
53
54
	unsigned long lineno;
	unsigned long nextline;
55
56
57
58
59
60
61
62
63
64
65
66
	int rc;
	int ready;
} Trec;

static Trec trec;
static unsigned long sid = SLAP_SYNC_SID_MAX + 1;
static int checkvals;
static int enable_meter;
static lutil_meter_t meter;
static const char *progname = "slapadd";
static OperationBuffer opbuf;
static char *buf;
67
static int lmax;
68
69

static ldap_pvt_thread_mutex_t add_mutex;
70
static ldap_pvt_thread_cond_t add_cond;
71
72
73
74
75
76
77
78
79
80
static int add_stop;

/* returns:
 *	1: got a record
 *	0: EOF
 * -1: read failure
 * -2: parse failure
 */
static int
getrec0(Erec *erec)
81
82
{
	const char *text;
83
	int ldifrc;
84
85
86
	char textbuf[SLAP_TEXT_BUFLEN] = { '\0' };
	size_t textlen = sizeof textbuf;
	struct berval csn;
87
	Operation *op = &opbuf.ob_op;
88
	op->o_hdr = &opbuf.ob_hdr;
89

90
91
again:
	erec->lineno = erec->nextline+1;
92
	/* nextline is the line number of the end of the current entry */
93
94
95
	ldifrc = ldif_read_record( ldiffp, &erec->nextline, &buf, &lmax );
	if (ldifrc < 1)
		return ldifrc < 0 ? -1 : 0;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
96
97
	{
		BackendDB *bd;
Howard Chu's avatar
Howard Chu committed
98
		Entry *e;
99

100
101
		if ( erec->lineno < jumpline )
			goto again;
102

Howard Chu's avatar
Howard Chu committed
103
		e = str2entry2( buf, checkvals );
104

Howard Chu's avatar
Howard Chu committed
105
106
107
108
109
		if ( enable_meter )
			lutil_meter_update( &meter,
					 ftell( ldiffp->fp ),
					 0);

110
		if( e == NULL ) {
111
			fprintf( stderr, "%s: could not parse entry (line=%lu)\n",
112
113
				progname, erec->lineno );
			return -2;
114
115
116
		}

		/* make sure the DN is not empty */
117
		if( BER_BVISEMPTY( &e->e_nname ) &&
Pierangelo Masarati's avatar
Pierangelo Masarati committed
118
119
			!BER_BVISEMPTY( be->be_nsuffix ))
		{
120
			fprintf( stderr, "%s: line %lu: "
121
				"cannot add entry with empty dn=\"%s\"",
122
				progname, erec->lineno, e->e_dn );
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
			bd = select_backend( &e->e_nname, nosubordinates );
			if ( bd ) {
				BackendDB *bdtmp;
				int dbidx = 0;
				LDAP_STAILQ_FOREACH( bdtmp, &backendDB, be_next ) {
					if ( bdtmp == bd ) break;
					dbidx++;
				}

				assert( bdtmp != NULL );
				
				fprintf( stderr, "; did you mean to use database #%d (%s)?",
					dbidx,
					bd->be_suffix[0].bv_val );

			}
			fprintf( stderr, "\n" );
140
			entry_free( e );
141
			return -2;
142
143
144
		}

		/* check backend */
Pierangelo Masarati's avatar
Pierangelo Masarati committed
145
146
		bd = select_backend( &e->e_nname, nosubordinates );
		if ( bd != be ) {
147
			fprintf( stderr, "%s: line %lu: "
Pierangelo Masarati's avatar
Pierangelo Masarati committed
148
				"database #%d (%s) not configured to hold \"%s\"",
149
				progname, erec->lineno,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
150
151
				dbnum,
				be->be_suffix[0].bv_val,
152
				e->e_dn );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
			if ( bd ) {
				BackendDB *bdtmp;
				int dbidx = 0;
				LDAP_STAILQ_FOREACH( bdtmp, &backendDB, be_next ) {
					if ( bdtmp == bd ) break;
					dbidx++;
				}

				assert( bdtmp != NULL );
				
				fprintf( stderr, "; did you mean to use database #%d (%s)?",
					dbidx,
					bd->be_suffix[0].bv_val );

			} else {
				fprintf( stderr, "; no database configured for that naming context" );
			}
			fprintf( stderr, "\n" );
171
			entry_free( e );
172
			return -2;
173
174
		}

175
176
		if ( slap_tool_entry_check( progname, op, e, erec->lineno, &text, textbuf, textlen ) !=
			LDAP_SUCCESS ) {
177
			entry_free( e );
178
			return -2;
179
180
181
182
183
184
185
186
187
188
189
190
191
		}

		if ( SLAP_LASTMOD(be) ) {
			time_t now = slap_get_time();
			char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
			struct berval vals[ 2 ];

			struct berval name, timestamp;

			struct berval nvals[ 2 ];
			struct berval nname;
			char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];

192
193
194
195
196
197
198
			enum {
				GOT_NONE = 0x0,
				GOT_CSN = 0x1,
				GOT_UUID = 0x2,
				GOT_ALL = (GOT_CSN|GOT_UUID)
			} got = GOT_ALL;

199
200
201
202
203
204
			vals[1].bv_len = 0;
			vals[1].bv_val = NULL;

			nvals[1].bv_len = 0;
			nvals[1].bv_val = NULL;

205
			csn.bv_len = ldap_pvt_csnstr( csnbuf, sizeof( csnbuf ), csnsid, 0 );
206
207
208
			csn.bv_val = csnbuf;

			timestamp.bv_val = timebuf;
209
210
211
			timestamp.bv_len = sizeof(timebuf);

			slap_timestamp( &now, &timestamp );
212

Pierangelo Masarati's avatar
Pierangelo Masarati committed
213
214
215
			if ( BER_BVISEMPTY( &be->be_rootndn ) ) {
				BER_BVSTR( &name, SLAPD_ANONYMOUS );
				nname = name;
216
217
218
219
220
221
222
223
			} else {
				name = be->be_rootdn;
				nname = be->be_rootndn;
			}

			if( attr_find( e->e_attrs, slap_schema.si_ad_entryUUID )
				== NULL )
			{
224
				got &= ~GOT_UUID;
225
226
				vals[0].bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
				vals[0].bv_val = uuidbuf;
227
				attr_merge_normalize_one( e, slap_schema.si_ad_entryUUID, vals, NULL );
228
229
230
231
232
233
234
235
236
237
			}

			if( attr_find( e->e_attrs, slap_schema.si_ad_creatorsName )
				== NULL )
			{
				vals[0] = name;
				nvals[0] = nname;
				attr_merge( e, slap_schema.si_ad_creatorsName, vals, nvals );
			}

Howard Chu's avatar
Howard Chu committed
238
			if( attr_find( e->e_attrs, slap_schema.si_ad_createTimestamp )
239
240
				== NULL )
			{
Howard Chu's avatar
Howard Chu committed
241
242
				vals[0] = timestamp;
				attr_merge( e, slap_schema.si_ad_createTimestamp, vals, NULL );
243
244
			}

Howard Chu's avatar
Howard Chu committed
245
			if( attr_find( e->e_attrs, slap_schema.si_ad_entryCSN )
246
247
				== NULL )
			{
248
				got &= ~GOT_CSN;
Howard Chu's avatar
Howard Chu committed
249
250
				vals[0] = csn;
				attr_merge( e, slap_schema.si_ad_entryCSN, vals, NULL );
251
252
			}

Howard Chu's avatar
Howard Chu committed
253
			if( attr_find( e->e_attrs, slap_schema.si_ad_modifiersName )
254
255
				== NULL )
			{
Howard Chu's avatar
Howard Chu committed
256
257
258
				vals[0] = name;
				nvals[0] = nname;
				attr_merge( e, slap_schema.si_ad_modifiersName, vals, nvals );
259
260
			}

Howard Chu's avatar
Howard Chu committed
261
			if( attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp )
262
263
				== NULL )
			{
Howard Chu's avatar
Howard Chu committed
264
265
				vals[0] = timestamp;
				attr_merge( e, slap_schema.si_ad_modifyTimestamp, vals, NULL );
266
267
			}

268
269
270
271
272
273
274
275
276
277
278
279
280
			if ( SLAP_SINGLE_SHADOW(be) && got != GOT_ALL ) {
				char buf[SLAP_TEXT_BUFLEN];

				snprintf( buf, sizeof(buf),
					"%s%s%s",
					( !(got & GOT_UUID) ? slap_schema.si_ad_entryUUID->ad_cname.bv_val : "" ),
					( !(got & GOT_CSN) ? "," : "" ),
					( !(got & GOT_CSN) ? slap_schema.si_ad_entryCSN->ad_cname.bv_val : "" ) );

				Debug( LDAP_DEBUG_ANY, "%s: warning, missing attrs %s from entry dn=\"%s\"\n",
					progname, buf, e->e_name.bv_val );
			}

281
			sid = slap_tool_update_ctxcsn_check( progname, e );
282
		}
283
284
285
286
287
288
289
290
291
292
293
294
295
		erec->e = e;
	}
	return 1;
}

static void *
getrec_thr(void *ctx)
{
	ldap_pvt_thread_mutex_lock( &add_mutex );
	while (!add_stop) {
		trec.rc = getrec0((Erec *)&trec);
		trec.ready = 1;
		while (trec.ready)
296
			ldap_pvt_thread_cond_wait( &add_cond, &add_mutex );
297
298
299
300
301
302
303
304
		/* eof or read failure */
		if ( trec.rc == 0 || trec.rc == -1 )
			break;
	}
	ldap_pvt_thread_mutex_unlock( &add_mutex );
	return NULL;
}

305
306
static int ldif_threaded;

307
308
309
310
static int
getrec(Erec *erec)
{
	int rc;
311
	if ( !ldif_threaded )
312
313
314
		return getrec0(erec);

	while (!trec.ready)
315
		ldap_pvt_thread_yield();
316
317
318
319
320
	erec->e = trec.e;
	erec->lineno = trec.lineno;
	erec->nextline = trec.nextline;
	trec.ready = 0;
	rc = trec.rc;
321
	ldap_pvt_thread_mutex_lock( &add_mutex );
322
	ldap_pvt_thread_mutex_unlock( &add_mutex );
323
	ldap_pvt_thread_cond_signal( &add_cond );
324
325
326
327
328
329
330
331
332
333
334
335
	return rc;
}

int
slapadd( int argc, char **argv )
{
	char textbuf[SLAP_TEXT_BUFLEN] = { '\0' };
	size_t textlen = sizeof textbuf;
	Erec erec;
	struct berval bvtext;
	ldap_pvt_thread_t thr;
	ID id;
336
	Entry *prev = NULL;
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407

	int ldifrc;
	int rc = EXIT_SUCCESS;

	struct stat stat_buf;

	/* default "000" */
	csnsid = 0;

	if ( isatty (2) ) enable_meter = 1;
	slap_tool_init( progname, SLAPADD, argc, argv );

	if( !be->be_entry_open ||
		!be->be_entry_close ||
		!be->be_entry_put ||
		(update_ctxcsn &&
		 (!be->be_dn2id_get ||
		  !be->be_entry_get ||
		  !be->be_entry_modify)) )
	{
		fprintf( stderr, "%s: database doesn't support necessary operations.\n",
			progname );
		if ( dryrun ) {
			fprintf( stderr, "\t(dry) continuing...\n" );

		} else {
			exit( EXIT_FAILURE );
		}
	}

	checkvals = (slapMode & SLAP_TOOL_QUICK) ? 0 : 1;

	/* do not check values in quick mode */
	if ( slapMode & SLAP_TOOL_QUICK ) {
		if ( slapMode & SLAP_TOOL_VALUE_CHECK ) {
			fprintf( stderr, "%s: value-check incompatible with quick mode; disabled.\n", progname );
			slapMode &= ~SLAP_TOOL_VALUE_CHECK;
		}
	}

	/* enforce schema checking unless not disabled */
	if ( (slapMode & SLAP_TOOL_NO_SCHEMA_CHECK) == 0) {
		SLAP_DBFLAGS(be) &= ~(SLAP_DBFLAG_NO_SCHEMA_CHECK);
	}

	if( !dryrun && be->be_entry_open( be, 1 ) != 0 ) {
		fprintf( stderr, "%s: could not open database.\n",
			progname );
		exit( EXIT_FAILURE );
	}

	(void)slap_tool_update_ctxcsn_init();

	if ( enable_meter
#ifdef LDAP_DEBUG
		/* tools default to "none" */
		&& slap_debug == LDAP_DEBUG_NONE
#endif
		&& !fstat ( fileno ( ldiffp->fp ), &stat_buf )
		&& S_ISREG(stat_buf.st_mode) ) {
		enable_meter = !lutil_meter_open(
			&meter,
			&lutil_meter_text_display,
			&lutil_meter_linear_estimator,
			stat_buf.st_size);
	} else {
		enable_meter = 0;
	}

	if ( slap_tool_thread_max > 1 ) {
		ldap_pvt_thread_mutex_init( &add_mutex );
408
		ldap_pvt_thread_cond_init( &add_cond );
409
		ldap_pvt_thread_create( &thr, 0, getrec_thr, NULL );
410
		ldif_threaded = 1;
411
412
413
414
415
416
417
418
419
420
421
422
	}

	erec.nextline = 0;
	erec.e = NULL;

	for (;;) {
		ldifrc = getrec( &erec );
		if ( ldifrc < 1 ) {
			if ( ldifrc == -2 && continuemode )
				continue;
			break;
		}
423

424
		if ( !dryrun ) {
425
426
427
428
429
430
431
432
			/*
			 * Initialize text buffer
			 */
			bvtext.bv_len = textlen;
			bvtext.bv_val = textbuf;
			bvtext.bv_val[0] = '\0';

			id = be->be_entry_put( be, erec.e, &bvtext );
433
434
			if( id == NOID ) {
				fprintf( stderr, "%s: could not add entry dn=\"%s\" "
435
								 "(line=%lu): %s\n", progname, erec.e->e_dn,
436
								 erec.lineno, bvtext.bv_val );
437
				rc = EXIT_FAILURE;
438
439
440
441
442
				if( continuemode ) {
					if ( prev ) entry_free( prev );
					prev = erec.e;
					continue;
				}
443
				break;
444
			}
445
			if ( verbose )
446
				fprintf( stderr, "added: \"%s\" (%08lx)\n",
447
					erec.e->e_dn, (long) id );
448
449
450
		} else {
			if ( verbose )
				fprintf( stderr, "added: \"%s\"\n",
451
					erec.e->e_dn );
452
453
		}

454
455
		if ( prev ) entry_free( prev );
		prev = erec.e;
456
457
	}

458
	if ( ldif_threaded ) {
459
		ldap_pvt_thread_mutex_lock( &add_mutex );
460
461
		add_stop = 1;
		trec.ready = 0;
462
		ldap_pvt_thread_cond_signal( &add_cond );
463
		ldap_pvt_thread_mutex_unlock( &add_mutex );
464
		ldap_pvt_thread_join( thr, NULL );
465
	}
466
	if ( erec.e ) entry_free( erec.e );
467

468
469
470
	if ( ldifrc < 0 )
		rc = EXIT_FAILURE;

471
472
473
474
	bvtext.bv_len = textlen;
	bvtext.bv_val = textbuf;
	bvtext.bv_val[0] = '\0';

Howard Chu's avatar
Howard Chu committed
475
476
477
478
479
	if ( enable_meter ) {
		lutil_meter_update( &meter, ftell( ldiffp->fp ), 1);
		lutil_meter_close( &meter );
	}

480
481
	if ( rc == EXIT_SUCCESS ) {
		rc = slap_tool_update_ctxcsn( progname, sid, &bvtext );
482
483
484
485
	}

	ch_free( buf );

Pierangelo Masarati's avatar
Pierangelo Masarati committed
486
	if ( !dryrun ) {
487
		if ( enable_meter ) {
488
			fprintf( stderr, "Closing DB..." );
489
		}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
490
491
492
		if( be->be_entry_close( be ) ) {
			rc = EXIT_FAILURE;
		}
493

Pierangelo Masarati's avatar
Pierangelo Masarati committed
494
495
496
		if( be->be_sync ) {
			be->be_sync( be );
		}
497
		if ( enable_meter ) {
498
			fprintf( stderr, "\n" );
499
		}
500
501
	}

502
503
	if ( slap_tool_destroy())
		rc = EXIT_FAILURE;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
504

505
506
	return rc;
}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
507