1 /* init.cpp - initialize ndb backend */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2008-2021 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 "slap-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
ndb_db_init(BackendDB * be,ConfigReply * cr)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
ndb_db_open(BackendDB * be,ConfigReply * cr)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
ndb_db_close(BackendDB * be,ConfigReply * cr)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
ndb_db_destroy(BackendDB * be,ConfigReply * cr)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
ndb_back_initialize(BackendInfo * bi)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                     LDAP_CONTROL_TXN_SPEC,
363                     NULL
364           };
365 
366           int rc = 0;
367 
368           /* initialize the underlying database system */
369           Debug( LDAP_DEBUG_TRACE,
370                     LDAP_XSTRING(ndb_back_initialize) ": initialize ndb backend\n", 0, 0, 0 );
371 
372           ndb_init();
373 
374           ndb_lastrow_code = new NdbInterpretedCode( NULL, ndb_lastrow, 1 );
375           ndb_lastrow_code->interpret_exit_last_row();
376           ndb_lastrow_code->finalise();
377 
378           bi->bi_flags |=
379                     SLAP_BFLAG_INCREMENT |
380                     SLAP_BFLAG_SUBENTRIES |
381                     SLAP_BFLAG_ALIASES |
382                     SLAP_BFLAG_REFERRALS;
383 
384           bi->bi_controls = controls;
385 
386           bi->bi_open = 0;
387           bi->bi_close = 0;
388           bi->bi_config = 0;
389           bi->bi_destroy = 0;
390 
391           bi->bi_db_init = ndb_db_init;
392           bi->bi_db_config = config_generic_wrapper;
393           bi->bi_db_open = ndb_db_open;
394           bi->bi_db_close = ndb_db_close;
395           bi->bi_db_destroy = ndb_db_destroy;
396 
397           bi->bi_op_add = ndb_back_add;
398           bi->bi_op_bind = ndb_back_bind;
399           bi->bi_op_compare = ndb_back_compare;
400           bi->bi_op_delete = ndb_back_delete;
401           bi->bi_op_modify = ndb_back_modify;
402           bi->bi_op_modrdn = ndb_back_modrdn;
403           bi->bi_op_search = ndb_back_search;
404 
405           bi->bi_op_unbind = 0;
406 
407 #if 0
408           bi->bi_extended = ndb_extended;
409 
410           bi->bi_chk_referrals = ndb_referrals;
411 #endif
412           bi->bi_operational = ndb_operational;
413           bi->bi_has_subordinates = ndb_has_subordinates;
414           bi->bi_entry_release_rw = 0;
415           bi->bi_entry_get_rw = ndb_entry_get;
416 
417           /*
418            * hooks for slap tools
419            */
420           bi->bi_tool_entry_open = ndb_tool_entry_open;
421           bi->bi_tool_entry_close = ndb_tool_entry_close;
422           bi->bi_tool_entry_first = ndb_tool_entry_first;
423           bi->bi_tool_entry_next = ndb_tool_entry_next;
424           bi->bi_tool_entry_get = ndb_tool_entry_get;
425           bi->bi_tool_entry_put = ndb_tool_entry_put;
426 #if 0
427           bi->bi_tool_entry_reindex = ndb_tool_entry_reindex;
428           bi->bi_tool_sync = 0;
429           bi->bi_tool_dn2id_get = ndb_tool_dn2id_get;
430           bi->bi_tool_entry_modify = ndb_tool_entry_modify;
431 #endif
432 
433           bi->bi_connection_init = 0;
434           bi->bi_connection_destroy = 0;
435 
436           rc = ndb_back_init_cf( bi );
437 
438           return rc;
439 }
440 
441 #if       SLAPD_NDB == SLAPD_MOD_DYNAMIC
442 
443 /* conditionally define the init_module() function */
444 extern "C" { int init_module( int argc, char *argv[] ); }
445 
446 SLAP_BACKEND_INIT_MODULE( ndb )
447 
448 #endif /* SLAPD_NDB == SLAPD_MOD_DYNAMIC */
449 
450