1 /* init.cpp - initialize ndb backend */
2 /* OpenLDAP: pkg/ldap/servers/slapd/back-ndb/init.cpp,v 1.4.2.4 2010/04/13 20:23:35 kurt Exp */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2008-2010 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Howard Chu for inclusion
18  * in OpenLDAP Software. This work was sponsored by MySQL.
19  */
20 
21 #include "portable.h"
22 
23 #include <stdio.h>
24 #include <ac/string.h>
25 #include <ac/unistd.h>
26 #include <ac/stdlib.h>
27 #include <ac/errno.h>
28 #include <sys/stat.h>
29 #include "back-ndb.h"
30 #include <lutil.h>
31 #include "config.h"
32 
33 extern "C" {
34 	static BI_db_init ndb_db_init;
35 	static BI_db_close ndb_db_close;
36 	static BI_db_open ndb_db_open;
37 	static BI_db_destroy ndb_db_destroy;
38 }
39 
40 static struct berval ndb_optable = BER_BVC("OL_opattrs");
41 
42 static struct berval ndb_opattrs[] = {
43 	BER_BVC("structuralObjectClass"),
44 	BER_BVC("entryUUID"),
45 	BER_BVC("creatorsName"),
46 	BER_BVC("createTimestamp"),
47 	BER_BVC("entryCSN"),
48 	BER_BVC("modifiersName"),
49 	BER_BVC("modifyTimestamp"),
50 	BER_BVNULL
51 };
52 
53 static int ndb_oplens[] = {
54 	0,	/* structuralOC, default */
55 	36,	/* entryUUID */
56 	0,	/* creatorsName, default */
57 	26,	/* createTimestamp */
58 	40,	/* entryCSN */
59 	0,	/* modifiersName, default */
60 	26,	/* modifyTimestamp */
61 	-1
62 };
63 
64 static Uint32 ndb_lastrow[1];
65 NdbInterpretedCode *ndb_lastrow_code;
66 
67 static int
68 ndb_db_init( BackendDB *be, ConfigReply *cr )
69 {
70 	struct ndb_info	*ni;
71 	int rc = 0;
72 
73 	Debug( LDAP_DEBUG_TRACE,
74 		LDAP_XSTRING(ndb_db_init) ": Initializing ndb database\n",
75 		0, 0, 0 );
76 
77 	/* allocate backend-database-specific stuff */
78 	ni = (struct ndb_info *) ch_calloc( 1, sizeof(struct ndb_info) );
79 
80 	be->be_private = ni;
81 	be->be_cf_ocs = be->bd_info->bi_cf_ocs;
82 
83 	ni->ni_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
84 
85 	ldap_pvt_thread_rdwr_init( &ni->ni_ai_rwlock );
86 	ldap_pvt_thread_rdwr_init( &ni->ni_oc_rwlock );
87 	ldap_pvt_thread_mutex_init( &ni->ni_conn_mutex );
88 
89 #ifdef DO_MONITORING
90 	rc = ndb_monitor_db_init( be );
91 #endif
92 
93 	return rc;
94 }
95 
96 static int
97 ndb_db_close( BackendDB *be, ConfigReply *cr );
98 
99 static int
100 ndb_db_open( BackendDB *be, ConfigReply *cr )
101 {
102 	struct ndb_info *ni = (struct ndb_info *) be->be_private;
103 	char sqlbuf[BUFSIZ], *ptr;
104 	int rc, i;
105 
106 	if ( be->be_suffix == NULL ) {
107 		snprintf( cr->msg, sizeof( cr->msg ),
108 			"ndb_db_open: need suffix" );
109 		Debug( LDAP_DEBUG_ANY, "%s\n",
110 			cr->msg, 0, 0 );
111 		return -1;
112 	}
113 
114 	Debug( LDAP_DEBUG_ARGS,
115 		LDAP_XSTRING(ndb_db_open) ": \"%s\"\n",
116 		be->be_suffix[0].bv_val, 0, 0 );
117 
118 	if ( ni->ni_nconns < 1 )
119 		ni->ni_nconns = 1;
120 
121 	ni->ni_cluster = (Ndb_cluster_connection **)ch_calloc( ni->ni_nconns, sizeof( Ndb_cluster_connection *));
122 	for ( i=0; i<ni->ni_nconns; i++ ) {
123 		ni->ni_cluster[i] = new Ndb_cluster_connection( ni->ni_connectstr );
124 		rc = ni->ni_cluster[i]->connect( 20, 5, 1 );
125 		if ( rc ) {
126 			snprintf( cr->msg, sizeof( cr->msg ),
127 				"ndb_db_open: ni_cluster[%d]->connect failed (%d)",
128 				i, rc );
129 			goto fail;
130 		}
131 	}
132 	for ( i=0; i<ni->ni_nconns; i++ ) {
133 		rc = ni->ni_cluster[i]->wait_until_ready( 30, 30 );
134 		if ( rc ) {
135 			snprintf( cr->msg, sizeof( cr->msg ),
136 				"ndb_db_open: ni_cluster[%d]->wait failed (%d)",
137 				i, rc );
138 			goto fail;
139 		}
140 	}
141 
142 	mysql_init( &ni->ni_sql );
143 	if ( !mysql_real_connect( &ni->ni_sql, ni->ni_hostname, ni->ni_username, ni->ni_password,
144 		"", ni->ni_port, ni->ni_socket, ni->ni_clflag )) {
145 		snprintf( cr->msg, sizeof( cr->msg ),
146 			"ndb_db_open: mysql_real_connect failed, %s (%d)",
147 			mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
148 		rc = -1;
149 		goto fail;
150 	}
151 
152 	sprintf( sqlbuf, "CREATE DATABASE IF NOT EXISTS %s", ni->ni_dbname );
153 	rc = mysql_query( &ni->ni_sql, sqlbuf );
154 	if ( rc ) {
155 		snprintf( cr->msg, sizeof( cr->msg ),
156 			"ndb_db_open: CREATE DATABASE %s failed, %s (%d)",
157 			ni->ni_dbname, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
158 		goto fail;
159 	}
160 
161 	sprintf( sqlbuf, "USE %s", ni->ni_dbname );
162 	rc = mysql_query( &ni->ni_sql, sqlbuf );
163 	if ( rc ) {
164 		snprintf( cr->msg, sizeof( cr->msg ),
165 			"ndb_db_open: USE DATABASE %s failed, %s (%d)",
166 			ni->ni_dbname, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
167 		goto fail;
168 	}
169 
170 	ptr = sqlbuf;
171 	ptr += sprintf( ptr, "CREATE TABLE IF NOT EXISTS " DN2ID_TABLE " ("
172 		"eid bigint unsigned NOT NULL, "
173 		"object_classes VARCHAR(1024) NOT NULL, "
174 		"a0 VARCHAR(128) NOT NULL DEFAULT '', "
175 		"a1 VARCHAR(128) NOT NULL DEFAULT '', "
176 		"a2 VARCHAR(128) NOT NULL DEFAULT '', "
177 		"a3 VARCHAR(128) NOT NULL DEFAULT '', "
178 		"a4 VARCHAR(128) NOT NULL DEFAULT '', "
179 		"a5 VARCHAR(128) NOT NULL DEFAULT '', "
180 		"a6 VARCHAR(128) NOT NULL DEFAULT '', "
181 		"a7 VARCHAR(128) NOT NULL DEFAULT '', "
182 		"a8 VARCHAR(128) NOT NULL DEFAULT '', "
183 		"a9 VARCHAR(128) NOT NULL DEFAULT '', "
184 		"a10 VARCHAR(128) NOT NULL DEFAULT '', "
185 		"a11 VARCHAR(128) NOT NULL DEFAULT '', "
186 		"a12 VARCHAR(128) NOT NULL DEFAULT '', "
187 		"a13 VARCHAR(128) NOT NULL DEFAULT '', "
188 		"a14 VARCHAR(128) NOT NULL DEFAULT '', "
189 		"a15 VARCHAR(128) NOT NULL DEFAULT '', "
190 		"PRIMARY KEY (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15), "
191 		"UNIQUE KEY eid (eid) USING HASH" );
192 	/* Create index columns */
193 	if ( ni->ni_attridxs ) {
194 		ListNode *ln;
195 		int newcol = 0;
196 
197 		*ptr++ = ',';
198 		*ptr++ = ' ';
199 		for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
200 			NdbAttrInfo *ai = (NdbAttrInfo *)ln->ln_data;
201 			ptr += sprintf( ptr, "`%s` VARCHAR(%d), ",
202 				ai->na_name.bv_val, ai->na_len );
203 		}
204 		ptr = lutil_strcopy(ptr, "KEY " INDEX_NAME " (" );
205 
206 		for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
207 			NdbAttrInfo *ai = (NdbAttrInfo *)ln->ln_data;
208 			if ( newcol ) *ptr++ = ',';
209 			*ptr++ = '`';
210 			ptr = lutil_strcopy( ptr, ai->na_name.bv_val );
211 			*ptr++ = '`';
212 			ai->na_ixcol = newcol + 18;
213 			newcol++;
214 		}
215 		*ptr++ = ')';
216 	}
217 	strcpy( ptr, ") ENGINE=ndb" );
218 	rc = mysql_query( &ni->ni_sql, sqlbuf );
219 	if ( rc ) {
220 		snprintf( cr->msg, sizeof( cr->msg ),
221 			"ndb_db_open: CREATE TABLE " DN2ID_TABLE " failed, %s (%d)",
222 			mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
223 		goto fail;
224 	}
225 
226 	rc = mysql_query( &ni->ni_sql, "CREATE TABLE IF NOT EXISTS " NEXTID_TABLE " ("
227 		"a bigint unsigned AUTO_INCREMENT PRIMARY KEY ) ENGINE=ndb" );
228 	if ( rc ) {
229 		snprintf( cr->msg, sizeof( cr->msg ),
230 			"ndb_db_open: CREATE TABLE " NEXTID_TABLE " failed, %s (%d)",
231 			mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
232 		goto fail;
233 	}
234 
235 	{
236 		NdbOcInfo *oci;
237 
238 		rc = ndb_aset_get( ni, &ndb_optable, ndb_opattrs, &oci );
239 		if ( rc ) {
240 			snprintf( cr->msg, sizeof( cr->msg ),
241 				"ndb_db_open: ndb_aset_get( %s ) failed (%d)",
242 				ndb_optable.bv_val, rc );
243 			goto fail;
244 		}
245 		for ( i=0; ndb_oplens[i] >= 0; i++ ) {
246 			if ( ndb_oplens[i] )
247 				oci->no_attrs[i]->na_len = ndb_oplens[i];
248 		}
249 		rc = ndb_aset_create( ni, oci );
250 		if ( rc ) {
251 			snprintf( cr->msg, sizeof( cr->msg ),
252 				"ndb_db_open: ndb_aset_create( %s ) failed (%d)",
253 				ndb_optable.bv_val, rc );
254 			goto fail;
255 		}
256 		ni->ni_opattrs = oci;
257 	}
258 	/* Create attribute sets */
259 	{
260 		ListNode *ln;
261 
262 		for ( ln = ni->ni_attrsets; ln; ln=ln->ln_next ) {
263 			NdbOcInfo *oci = (NdbOcInfo *)ln->ln_data;
264 			rc = ndb_aset_create( ni, oci );
265 			if ( rc ) {
266 				snprintf( cr->msg, sizeof( cr->msg ),
267 					"ndb_db_open: ndb_aset_create( %s ) failed (%d)",
268 					oci->no_name.bv_val, rc );
269 				goto fail;
270 			}
271 		}
272 	}
273 	/* Initialize any currently used objectClasses */
274 	{
275 		Ndb *ndb;
276 		const NdbDictionary::Dictionary *myDict;
277 
278 		ndb = new Ndb( ni->ni_cluster[0], ni->ni_dbname );
279 		ndb->init(1024);
280 
281 		myDict = ndb->getDictionary();
282 		ndb_oc_read( ni, myDict );
283 		delete ndb;
284 	}
285 
286 #ifdef DO_MONITORING
287 	/* monitor setup */
288 	rc = ndb_monitor_db_open( be );
289 	if ( rc != 0 ) {
290 		goto fail;
291 	}
292 #endif
293 
294 	return 0;
295 
296 fail:
297 	Debug( LDAP_DEBUG_ANY, "%s\n",
298 		cr->msg, 0, 0 );
299 	ndb_db_close( be, NULL );
300 	return rc;
301 }
302 
303 static int
304 ndb_db_close( BackendDB *be, ConfigReply *cr )
305 {
306 	int i;
307 	struct ndb_info *ni = (struct ndb_info *) be->be_private;
308 
309 	mysql_close( &ni->ni_sql );
310 	if ( ni->ni_cluster ) {
311 		for ( i=0; i<ni->ni_nconns; i++ ) {
312 			if ( ni->ni_cluster[i] ) {
313 				delete ni->ni_cluster[i];
314 				ni->ni_cluster[i] = NULL;
315 			}
316 		}
317 		ch_free( ni->ni_cluster );
318 		ni->ni_cluster = NULL;
319 	}
320 
321 #ifdef DO_MONITORING
322 	/* monitor handling */
323 	(void)ndb_monitor_db_close( be );
324 #endif
325 
326 	return 0;
327 }
328 
329 static int
330 ndb_db_destroy( BackendDB *be, ConfigReply *cr )
331 {
332 	struct ndb_info *ni = (struct ndb_info *) be->be_private;
333 
334 #ifdef DO_MONITORING
335 	/* monitor handling */
336 	(void)ndb_monitor_db_destroy( be );
337 #endif
338 
339 	ldap_pvt_thread_mutex_destroy( &ni->ni_conn_mutex );
340 	ldap_pvt_thread_rdwr_destroy( &ni->ni_ai_rwlock );
341 	ldap_pvt_thread_rdwr_destroy( &ni->ni_oc_rwlock );
342 
343 	ch_free( ni );
344 	be->be_private = NULL;
345 
346 	return 0;
347 }
348 
349 extern "C" int
350 ndb_back_initialize(
351 	BackendInfo	*bi )
352 {
353 	static char *controls[] = {
354 		LDAP_CONTROL_ASSERT,
355 		LDAP_CONTROL_MANAGEDSAIT,
356 		LDAP_CONTROL_NOOP,
357 		LDAP_CONTROL_PAGEDRESULTS,
358 		LDAP_CONTROL_PRE_READ,
359 		LDAP_CONTROL_POST_READ,
360 		LDAP_CONTROL_SUBENTRIES,
361 		LDAP_CONTROL_X_PERMISSIVE_MODIFY,
362 #ifdef LDAP_X_TXN
363 		LDAP_CONTROL_X_TXN_SPEC,
364 #endif
365 		NULL
366 	};
367 
368 	int rc = 0;
369 
370 	/* initialize the underlying database system */
371 	Debug( LDAP_DEBUG_TRACE,
372 		LDAP_XSTRING(ndb_back_initialize) ": initialize ndb backend\n", 0, 0, 0 );
373 
374 	ndb_init();
375 
376 	ndb_lastrow_code = new NdbInterpretedCode( NULL, ndb_lastrow, 1 );
377 	ndb_lastrow_code->interpret_exit_last_row();
378 	ndb_lastrow_code->finalise();
379 
380 	bi->bi_flags |=
381 		SLAP_BFLAG_INCREMENT |
382 		SLAP_BFLAG_SUBENTRIES |
383 		SLAP_BFLAG_ALIASES |
384 		SLAP_BFLAG_REFERRALS;
385 
386 	bi->bi_controls = controls;
387 
388 	bi->bi_open = 0;
389 	bi->bi_close = 0;
390 	bi->bi_config = 0;
391 	bi->bi_destroy = 0;
392 
393 	bi->bi_db_init = ndb_db_init;
394 	bi->bi_db_config = config_generic_wrapper;
395 	bi->bi_db_open = ndb_db_open;
396 	bi->bi_db_close = ndb_db_close;
397 	bi->bi_db_destroy = ndb_db_destroy;
398 
399 	bi->bi_op_add = ndb_back_add;
400 	bi->bi_op_bind = ndb_back_bind;
401 	bi->bi_op_compare = ndb_back_compare;
402 	bi->bi_op_delete = ndb_back_delete;
403 	bi->bi_op_modify = ndb_back_modify;
404 	bi->bi_op_modrdn = ndb_back_modrdn;
405 	bi->bi_op_search = ndb_back_search;
406 
407 	bi->bi_op_unbind = 0;
408 
409 #if 0
410 	bi->bi_extended = ndb_extended;
411 
412 	bi->bi_chk_referrals = ndb_referrals;
413 #endif
414 	bi->bi_operational = ndb_operational;
415 	bi->bi_has_subordinates = ndb_has_subordinates;
416 	bi->bi_entry_release_rw = 0;
417 	bi->bi_entry_get_rw = ndb_entry_get;
418 
419 	/*
420 	 * hooks for slap tools
421 	 */
422 	bi->bi_tool_entry_open = ndb_tool_entry_open;
423 	bi->bi_tool_entry_close = ndb_tool_entry_close;
424 	bi->bi_tool_entry_first = ndb_tool_entry_first;
425 	bi->bi_tool_entry_next = ndb_tool_entry_next;
426 	bi->bi_tool_entry_get = ndb_tool_entry_get;
427 	bi->bi_tool_entry_put = ndb_tool_entry_put;
428 #if 0
429 	bi->bi_tool_entry_reindex = ndb_tool_entry_reindex;
430 	bi->bi_tool_sync = 0;
431 	bi->bi_tool_dn2id_get = ndb_tool_dn2id_get;
432 	bi->bi_tool_entry_modify = ndb_tool_entry_modify;
433 #endif
434 
435 	bi->bi_connection_init = 0;
436 	bi->bi_connection_destroy = 0;
437 
438 	rc = ndb_back_init_cf( bi );
439 
440 	return rc;
441 }
442 
443 #if	SLAPD_NDB == SLAPD_MOD_DYNAMIC
444 
445 /* conditionally define the init_module() function */
446 extern "C" { int init_module( int argc, char *argv[] ); }
447 
448 SLAP_BACKEND_INIT_MODULE( ndb )
449 
450 #endif /* SLAPD_NDB == SLAPD_MOD_DYNAMIC */
451 
452