]> git.sur5r.net Git - openldap/blob - servers/slapd/slapadd.c
Additional fix for threaded slapadd
[openldap] / servers / slapd / slapadd.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2012 The OpenLDAP Foundation.
5  * Portions Copyright 1998-2003 Kurt D. Zeilenga.
6  * Portions Copyright 2003 IBM Corporation.
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 file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Kurt Zeilenga for inclusion
19  * in OpenLDAP Software.  Additional signficant contributors include
20  *    Jong Hyuk Choi
21  *    Pierangelo Masarati
22  */
23
24 #include "portable.h"
25
26 #include <stdio.h>
27
28 #include <ac/stdlib.h>
29
30 #include <ac/ctype.h>
31 #include <ac/string.h>
32 #include <ac/socket.h>
33 #include <ac/unistd.h>
34
35 #include <lber.h>
36 #include <ldif.h>
37 #include <lutil.h>
38 #include <lutil_meter.h>
39 #include <sys/stat.h>
40
41 #include "slapcommon.h"
42
43 static char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
44
45 typedef struct Erec {
46         Entry *e;
47         int lineno;
48         int nextline;
49 } Erec;
50
51 typedef struct Trec {
52         Entry *e;
53         int lineno;
54         int nextline;
55         int rc;
56         int ready;
57 } Trec;
58
59 static Trec trec;
60 static unsigned long sid = SLAP_SYNC_SID_MAX + 1;
61 static int checkvals;
62 static int enable_meter;
63 static lutil_meter_t meter;
64 static const char *progname = "slapadd";
65 static OperationBuffer opbuf;
66 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 static int add_stop;
72
73 /* returns:
74  *      1: got a record
75  *      0: EOF
76  * -1: read failure
77  * -2: parse failure
78  */
79 static int
80 getrec0(Erec *erec)
81 {
82         const char *text;
83         int ldifrc;
84         char textbuf[SLAP_TEXT_BUFLEN] = { '\0' };
85         size_t textlen = sizeof textbuf;
86         struct berval csn;
87         Operation *op = &opbuf.ob_op;
88         op->o_hdr = &opbuf.ob_hdr;
89
90 again:
91         erec->lineno = erec->nextline+1;
92         /* nextline is the line number of the end of the current entry */
93         ldifrc = ldif_read_record( ldiffp, &erec->nextline, &buf, &lmax );
94         if (ldifrc < 1)
95                 return ldifrc < 0 ? -1 : 0;
96         {
97                 BackendDB *bd;
98                 Entry *e;
99
100                 if ( erec->lineno < jumpline )
101                         goto again;
102
103                 e = str2entry2( buf, checkvals );
104
105                 if ( enable_meter )
106                         lutil_meter_update( &meter,
107                                          ftell( ldiffp->fp ),
108                                          0);
109
110                 if( e == NULL ) {
111                         fprintf( stderr, "%s: could not parse entry (line=%d)\n",
112                                 progname, erec->lineno );
113                         return -2;
114                 }
115
116                 /* make sure the DN is not empty */
117                 if( BER_BVISEMPTY( &e->e_nname ) &&
118                         !BER_BVISEMPTY( be->be_nsuffix ))
119                 {
120                         fprintf( stderr, "%s: line %d: "
121                                 "cannot add entry with empty dn=\"%s\"",
122                                 progname, erec->lineno, e->e_dn );
123                         bd = select_backend( &e->e_nname, nosubordinates );
124                         if ( bd ) {
125                                 BackendDB *bdtmp;
126                                 int dbidx = 0;
127                                 LDAP_STAILQ_FOREACH( bdtmp, &backendDB, be_next ) {
128                                         if ( bdtmp == bd ) break;
129                                         dbidx++;
130                                 }
131
132                                 assert( bdtmp != NULL );
133                                 
134                                 fprintf( stderr, "; did you mean to use database #%d (%s)?",
135                                         dbidx,
136                                         bd->be_suffix[0].bv_val );
137
138                         }
139                         fprintf( stderr, "\n" );
140                         entry_free( e );
141                         return -2;
142                 }
143
144                 /* check backend */
145                 bd = select_backend( &e->e_nname, nosubordinates );
146                 if ( bd != be ) {
147                         fprintf( stderr, "%s: line %d: "
148                                 "database #%d (%s) not configured to hold \"%s\"",
149                                 progname, erec->lineno,
150                                 dbnum,
151                                 be->be_suffix[0].bv_val,
152                                 e->e_dn );
153                         if ( bd ) {
154                                 BackendDB *bdtmp;
155                                 int dbidx = 0;
156                                 LDAP_STAILQ_FOREACH( bdtmp, &backendDB, be_next ) {
157                                         if ( bdtmp == bd ) break;
158                                         dbidx++;
159                                 }
160
161                                 assert( bdtmp != NULL );
162                                 
163                                 fprintf( stderr, "; did you mean to use database #%d (%s)?",
164                                         dbidx,
165                                         bd->be_suffix[0].bv_val );
166
167                         } else {
168                                 fprintf( stderr, "; no database configured for that naming context" );
169                         }
170                         fprintf( stderr, "\n" );
171                         entry_free( e );
172                         return -2;
173                 }
174
175                 if ( slap_tool_entry_check( progname, op, e, erec->lineno, &text, textbuf, textlen ) !=
176                         LDAP_SUCCESS ) {
177                         entry_free( e );
178                         return -2;
179                 }
180
181                 if ( SLAP_LASTMOD(be) ) {
182                         time_t now = slap_get_time();
183                         char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
184                         struct berval vals[ 2 ];
185
186                         struct berval name, timestamp;
187
188                         struct berval nvals[ 2 ];
189                         struct berval nname;
190                         char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
191
192                         enum {
193                                 GOT_NONE = 0x0,
194                                 GOT_CSN = 0x1,
195                                 GOT_UUID = 0x2,
196                                 GOT_ALL = (GOT_CSN|GOT_UUID)
197                         } got = GOT_ALL;
198
199                         vals[1].bv_len = 0;
200                         vals[1].bv_val = NULL;
201
202                         nvals[1].bv_len = 0;
203                         nvals[1].bv_val = NULL;
204
205                         csn.bv_len = ldap_pvt_csnstr( csnbuf, sizeof( csnbuf ), csnsid, 0 );
206                         csn.bv_val = csnbuf;
207
208                         timestamp.bv_val = timebuf;
209                         timestamp.bv_len = sizeof(timebuf);
210
211                         slap_timestamp( &now, &timestamp );
212
213                         if ( BER_BVISEMPTY( &be->be_rootndn ) ) {
214                                 BER_BVSTR( &name, SLAPD_ANONYMOUS );
215                                 nname = name;
216                         } else {
217                                 name = be->be_rootdn;
218                                 nname = be->be_rootndn;
219                         }
220
221                         if( attr_find( e->e_attrs, slap_schema.si_ad_entryUUID )
222                                 == NULL )
223                         {
224                                 got &= ~GOT_UUID;
225                                 vals[0].bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
226                                 vals[0].bv_val = uuidbuf;
227                                 attr_merge_normalize_one( e, slap_schema.si_ad_entryUUID, vals, NULL );
228                         }
229
230                         if( attr_find( e->e_attrs, slap_schema.si_ad_creatorsName )
231                                 == NULL )
232                         {
233                                 vals[0] = name;
234                                 nvals[0] = nname;
235                                 attr_merge( e, slap_schema.si_ad_creatorsName, vals, nvals );
236                         }
237
238                         if( attr_find( e->e_attrs, slap_schema.si_ad_createTimestamp )
239                                 == NULL )
240                         {
241                                 vals[0] = timestamp;
242                                 attr_merge( e, slap_schema.si_ad_createTimestamp, vals, NULL );
243                         }
244
245                         if( attr_find( e->e_attrs, slap_schema.si_ad_entryCSN )
246                                 == NULL )
247                         {
248                                 got &= ~GOT_CSN;
249                                 vals[0] = csn;
250                                 attr_merge( e, slap_schema.si_ad_entryCSN, vals, NULL );
251                         }
252
253                         if( attr_find( e->e_attrs, slap_schema.si_ad_modifiersName )
254                                 == NULL )
255                         {
256                                 vals[0] = name;
257                                 nvals[0] = nname;
258                                 attr_merge( e, slap_schema.si_ad_modifiersName, vals, nvals );
259                         }
260
261                         if( attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp )
262                                 == NULL )
263                         {
264                                 vals[0] = timestamp;
265                                 attr_merge( e, slap_schema.si_ad_modifyTimestamp, vals, NULL );
266                         }
267
268                         if ( SLAP_SINGLE_SHADOW(be) && got != GOT_ALL ) {
269                                 char buf[SLAP_TEXT_BUFLEN];
270
271                                 snprintf( buf, sizeof(buf),
272                                         "%s%s%s",
273                                         ( !(got & GOT_UUID) ? slap_schema.si_ad_entryUUID->ad_cname.bv_val : "" ),
274                                         ( !(got & GOT_CSN) ? "," : "" ),
275                                         ( !(got & GOT_CSN) ? slap_schema.si_ad_entryCSN->ad_cname.bv_val : "" ) );
276
277                                 Debug( LDAP_DEBUG_ANY, "%s: warning, missing attrs %s from entry dn=\"%s\"\n",
278                                         progname, buf, e->e_name.bv_val );
279                         }
280
281                         sid = slap_tool_update_ctxcsn_check( progname, e );
282                 }
283                 erec->e = e;
284         }
285         return 1;
286 }
287
288 static void *
289 getrec_thr(void *ctx)
290 {
291         ldap_pvt_thread_mutex_lock( &add_mutex );
292         while (!add_stop) {
293                 trec.rc = getrec0((Erec *)&trec);
294                 trec.ready = 1;
295                 while (trec.ready)
296                         ldap_pvt_thread_cond_wait( &add_cond, &add_mutex );
297                 /* eof or read failure */
298                 if ( trec.rc == 0 || trec.rc == -1 )
299                         break;
300         }
301         ldap_pvt_thread_mutex_unlock( &add_mutex );
302         return NULL;
303 }
304
305 static int ldif_threaded;
306
307 static int
308 getrec(Erec *erec)
309 {
310         int rc;
311         if ( !ldif_threaded )
312                 return getrec0(erec);
313
314         while (!trec.ready)
315                 ldap_pvt_thread_yield();
316         erec->e = trec.e;
317         erec->lineno = trec.lineno;
318         erec->nextline = trec.nextline;
319         trec.ready = 0;
320         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         return rc;
325 }
326
327 int
328 slapadd( int argc, char **argv )
329 {
330         char textbuf[SLAP_TEXT_BUFLEN] = { '\0' };
331         size_t textlen = sizeof textbuf;
332         Erec erec;
333         struct berval bvtext;
334         ldap_pvt_thread_t thr;
335         ID id;
336
337         int ldifrc;
338         int rc = EXIT_SUCCESS;
339
340         struct stat stat_buf;
341
342         /* default "000" */
343         csnsid = 0;
344
345         if ( isatty (2) ) enable_meter = 1;
346         slap_tool_init( progname, SLAPADD, argc, argv );
347
348         if( !be->be_entry_open ||
349                 !be->be_entry_close ||
350                 !be->be_entry_put ||
351                 (update_ctxcsn &&
352                  (!be->be_dn2id_get ||
353                   !be->be_entry_get ||
354                   !be->be_entry_modify)) )
355         {
356                 fprintf( stderr, "%s: database doesn't support necessary operations.\n",
357                         progname );
358                 if ( dryrun ) {
359                         fprintf( stderr, "\t(dry) continuing...\n" );
360
361                 } else {
362                         exit( EXIT_FAILURE );
363                 }
364         }
365
366         checkvals = (slapMode & SLAP_TOOL_QUICK) ? 0 : 1;
367
368         /* do not check values in quick mode */
369         if ( slapMode & SLAP_TOOL_QUICK ) {
370                 if ( slapMode & SLAP_TOOL_VALUE_CHECK ) {
371                         fprintf( stderr, "%s: value-check incompatible with quick mode; disabled.\n", progname );
372                         slapMode &= ~SLAP_TOOL_VALUE_CHECK;
373                 }
374         }
375
376         /* enforce schema checking unless not disabled */
377         if ( (slapMode & SLAP_TOOL_NO_SCHEMA_CHECK) == 0) {
378                 SLAP_DBFLAGS(be) &= ~(SLAP_DBFLAG_NO_SCHEMA_CHECK);
379         }
380
381         if( !dryrun && be->be_entry_open( be, 1 ) != 0 ) {
382                 fprintf( stderr, "%s: could not open database.\n",
383                         progname );
384                 exit( EXIT_FAILURE );
385         }
386
387         (void)slap_tool_update_ctxcsn_init();
388
389         if ( enable_meter
390 #ifdef LDAP_DEBUG
391                 /* tools default to "none" */
392                 && slap_debug == LDAP_DEBUG_NONE
393 #endif
394                 && !fstat ( fileno ( ldiffp->fp ), &stat_buf )
395                 && S_ISREG(stat_buf.st_mode) ) {
396                 enable_meter = !lutil_meter_open(
397                         &meter,
398                         &lutil_meter_text_display,
399                         &lutil_meter_linear_estimator,
400                         stat_buf.st_size);
401         } else {
402                 enable_meter = 0;
403         }
404
405         if ( slap_tool_thread_max > 1 ) {
406                 ldap_pvt_thread_mutex_init( &add_mutex );
407                 ldap_pvt_thread_cond_init( &add_cond );
408                 ldap_pvt_thread_create( &thr, 0, getrec_thr, NULL );
409                 ldif_threaded = 1;
410         }
411
412         erec.nextline = 0;
413         erec.e = NULL;
414
415         for (;;) {
416                 ldifrc = getrec( &erec );
417                 if ( ldifrc < 1 ) {
418                         if ( ldifrc == -2 && continuemode )
419                                 continue;
420                         break;
421                 }
422
423                 if ( !dryrun ) {
424                         /*
425                          * Initialize text buffer
426                          */
427                         bvtext.bv_len = textlen;
428                         bvtext.bv_val = textbuf;
429                         bvtext.bv_val[0] = '\0';
430
431                         id = be->be_entry_put( be, erec.e, &bvtext );
432                         if( id == NOID ) {
433                                 fprintf( stderr, "%s: could not add entry dn=\"%s\" "
434                                                                  "(line=%d): %s\n", progname, erec.e->e_dn,
435                                                                  erec.lineno, bvtext.bv_val );
436                                 rc = EXIT_FAILURE;
437                                 entry_free( erec.e );
438                                 if( continuemode ) continue;
439                                 break;
440                         }
441                         if ( verbose )
442                                 fprintf( stderr, "added: \"%s\" (%08lx)\n",
443                                         erec.e->e_dn, (long) id );
444                 } else {
445                         if ( verbose )
446                                 fprintf( stderr, "added: \"%s\"\n",
447                                         erec.e->e_dn );
448                 }
449
450                 entry_free( erec.e );
451         }
452
453         if ( ldif_threaded ) {
454                 ldap_pvt_thread_mutex_lock( &add_mutex );
455                 add_stop = 1;
456                 trec.ready = 0;
457                 ldap_pvt_thread_cond_signal( &add_cond );
458                 ldap_pvt_thread_mutex_unlock( &add_mutex );
459                 ldap_pvt_thread_join( thr, NULL );
460         }
461
462         if ( ldifrc < 0 )
463                 rc = EXIT_FAILURE;
464
465         bvtext.bv_len = textlen;
466         bvtext.bv_val = textbuf;
467         bvtext.bv_val[0] = '\0';
468
469         if ( enable_meter ) {
470                 lutil_meter_update( &meter, ftell( ldiffp->fp ), 1);
471                 lutil_meter_close( &meter );
472         }
473
474         if ( rc == EXIT_SUCCESS ) {
475                 rc = slap_tool_update_ctxcsn( progname, sid, &bvtext );
476         }
477
478         ch_free( buf );
479
480         if ( !dryrun ) {
481                 if ( enable_meter ) {
482                         fprintf( stderr, "Closing DB..." );
483                 }
484                 if( be->be_entry_close( be ) ) {
485                         rc = EXIT_FAILURE;
486                 }
487
488                 if( be->be_sync ) {
489                         be->be_sync( be );
490                 }
491                 if ( enable_meter ) {
492                         fprintf( stderr, "\n" );
493                 }
494         }
495
496         if ( slap_tool_destroy())
497                 rc = EXIT_FAILURE;
498
499         return rc;
500 }
501