]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/glue.c
6c64d6cfddec4d794818bd16ee8b8e8d2f0f11f9
[openldap] / servers / slapd / overlays / glue.c
1 /* glue.c - backend glue overlay */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2001-2004 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
17 /*
18  * Functions to glue a bunch of other backends into a single tree.
19  * All of the glued backends must share a common suffix. E.g., you
20  * can glue o=foo and ou=bar,o=foo but you can't glue o=foo and o=bar.
21  *
22  * The purpose of these functions is to allow you to split a single database
23  * into pieces (for load balancing purposes, whatever) but still be able
24  * to treat it as a single database after it's been split. As such, each
25  * of the glued backends should have identical rootdn.
26  *  -- Howard Chu
27  */
28
29 #include "portable.h"
30
31 #ifdef SLAPD_OVER_GLUE
32
33 #include <stdio.h>
34
35 #include <ac/string.h>
36 #include <ac/socket.h>
37
38 #define SLAPD_TOOLS
39 #include "slap.h"
40
41 typedef struct gluenode {
42         BackendDB *gn_be;
43         int     gn_bx;
44         struct berval gn_pdn;
45         int gn_async;
46 } gluenode;
47
48 typedef struct glueinfo {
49         int gi_nodes;
50         struct berval gi_pdn;
51         gluenode gi_n[1];
52 } glueinfo;
53
54 static slap_overinst    glue;
55
56 static int glueMode;
57 static BackendDB *glueBack;
58
59 static slap_response glue_op_response;
60
61 /* Just like select_backend, but only for our backends */
62 static BackendDB *
63 glue_back_select (
64         BackendDB *be,
65         struct berval *dn
66 )
67 {
68         slap_overinst   *on = (slap_overinst *)be->bd_info;
69         glueinfo                *gi = (glueinfo *)on->on_bi.bi_private;
70         int i;
71
72         for (i = 0; i<gi->gi_nodes; i++) {
73                 assert( gi->gi_n[i].gn_be->be_nsuffix );
74
75                 if (dnIsSuffix(dn, &gi->gi_n[i].gn_be->be_nsuffix[0])) {
76                         return gi->gi_n[i].gn_be;
77                 }
78         }
79         be->bd_info = on->on_info->oi_orig;
80         return be;
81 }
82
83
84 typedef struct glue_state {
85         int err;
86         int slimit;
87         int matchlen;
88         char *matched;
89         int nrefs;
90         BerVarray refs;
91 } glue_state;
92
93 static int
94 glue_op_response ( Operation *op, SlapReply *rs )
95 {
96         glue_state *gs = op->o_callback->sc_private;
97
98         switch(rs->sr_type) {
99         case REP_SEARCH:
100                 if ( gs->slimit != SLAP_NO_LIMIT
101                                 && rs->sr_nentries >= gs->slimit )
102                 {
103                         rs->sr_err = gs->err = LDAP_SIZELIMIT_EXCEEDED;
104                         return -1;
105                 }
106                 /* fallthru */
107         case REP_SEARCHREF:
108                 return SLAP_CB_CONTINUE;
109
110         default:
111                 if (rs->sr_err == LDAP_SUCCESS ||
112                         rs->sr_err == LDAP_SIZELIMIT_EXCEEDED ||
113                         rs->sr_err == LDAP_TIMELIMIT_EXCEEDED ||
114                         rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ||
115                         rs->sr_err == LDAP_NO_SUCH_OBJECT ||
116                         gs->err != LDAP_SUCCESS)
117                         gs->err = rs->sr_err;
118                 if (gs->err == LDAP_SUCCESS && gs->matched) {
119                         ch_free (gs->matched);
120                         gs->matched = NULL;
121                         gs->matchlen = 0;
122                 }
123                 if (gs->err != LDAP_SUCCESS && rs->sr_matched) {
124                         int len;
125                         len = strlen (rs->sr_matched);
126                         if (len > gs->matchlen) {
127                                 if (gs->matched)
128                                         ch_free (gs->matched);
129                                 gs->matched = ch_strdup (rs->sr_matched);
130                                 gs->matchlen = len;
131                         }
132                 }
133                 if (rs->sr_ref) {
134                         int i, j, k;
135                         BerVarray new;
136
137                         for (i=0; rs->sr_ref[i].bv_val; i++);
138
139                         j = gs->nrefs;
140                         if (!j) {
141                                 new = ch_malloc ((i+1)*sizeof(struct berval));
142                         } else {
143                                 new = ch_realloc(gs->refs,
144                                         (j+i+1)*sizeof(struct berval));
145                         }
146                         for (k=0; k<i; j++,k++) {
147                                 ber_dupbv( &new[j], &rs->sr_ref[k] );
148                         }
149                         new[j].bv_val = NULL;
150                         gs->nrefs = j;
151                         gs->refs = new;
152                 }
153         }
154         return 0;
155 }
156
157 static int
158 glue_op_search ( Operation *op, SlapReply *rs )
159 {
160         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
161         glueinfo                *gi = (glueinfo *)on->on_bi.bi_private;
162         BackendDB *b0 = op->o_bd;
163         BackendDB *b1 = NULL, *btmp;
164         BackendInfo *bi0 = op->o_bd->bd_info;
165         int i;
166         long stoptime = 0;
167         glue_state gs = {0, 0, 0, NULL, 0, NULL};
168         slap_callback cb = { NULL, glue_op_response, NULL, NULL };
169         int scope0, slimit0, tlimit0;
170         struct berval dn, ndn, *pdn;
171
172         cb.sc_private = &gs;
173
174         cb.sc_next = op->o_callback;
175
176         stoptime = slap_get_time () + op->ors_tlimit;
177
178         op->o_bd = glue_back_select (b0, &op->o_req_ndn);
179         b0->bd_info = on->on_info->oi_orig;
180
181         switch (op->ors_scope) {
182         case LDAP_SCOPE_BASE:
183                 return SLAP_CB_CONTINUE;
184
185         case LDAP_SCOPE_ONELEVEL:
186         case LDAP_SCOPE_SUBTREE:
187 #ifdef LDAP_SCOPE_SUBORDINATE
188         case LDAP_SCOPE_SUBORDINATE: /* FIXME */
189 #endif
190
191                 if ( op->o_sync ) {
192                         if (op->o_bd && op->o_bd->be_search) {
193                                 rs->sr_err = op->o_bd->be_search( op, rs );
194                         } else {
195                                 send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
196                                                 "No search target found");
197                         }
198                         return rs->sr_err;
199                 }
200
201                 op->o_callback = &cb;
202                 rs->sr_err = gs.err = LDAP_UNWILLING_TO_PERFORM;
203                 scope0 = op->ors_scope;
204                 slimit0 = gs.slimit = op->ors_slimit;
205                 tlimit0 = op->ors_tlimit;
206                 dn = op->o_req_dn;
207                 ndn = op->o_req_ndn;
208                 b1 = op->o_bd;
209
210                 /*
211                  * Execute in reverse order, most general first 
212                  */
213                 for (i = gi->gi_nodes; i >= 0; i--) {
214                         if ( i == gi->gi_nodes ) {
215                                 btmp = b0;
216                                 pdn = &gi->gi_pdn;
217                         } else {
218                                 btmp = gi->gi_n[i].gn_be;
219                                 pdn = &gi->gi_n[i].gn_pdn;
220                         }
221                         if (!btmp || !btmp->be_search)
222                                 continue;
223                         if (!dnIsSuffix(&btmp->be_nsuffix[0], &b1->be_nsuffix[0]))
224                                 continue;
225                         if (tlimit0 != SLAP_NO_LIMIT) {
226                                 op->ors_tlimit = stoptime - slap_get_time ();
227                                 if (op->ors_tlimit <= 0) {
228                                         rs->sr_err = gs.err = LDAP_TIMELIMIT_EXCEEDED;
229                                         break;
230                                 }
231                         }
232                         if (slimit0 != SLAP_NO_LIMIT) {
233                                 op->ors_slimit = slimit0 - rs->sr_nentries;
234                                 if (op->ors_slimit < 0) {
235                                         rs->sr_err = gs.err = LDAP_SIZELIMIT_EXCEEDED;
236                                         break;
237                                 }
238                         }
239                         rs->sr_err = 0;
240                         /*
241                          * check for abandon 
242                          */
243                         if (op->o_abandon) {
244                                 goto end_of_loop;
245                         }
246                         op->o_bd = btmp;
247
248                         assert( op->o_bd->be_suffix );
249                         assert( op->o_bd->be_nsuffix );
250                         
251                         if (scope0 == LDAP_SCOPE_ONELEVEL && 
252                                 dn_match(pdn, &ndn))
253                         {
254                                 op->ors_scope = LDAP_SCOPE_BASE;
255                                 op->o_req_dn = op->o_bd->be_suffix[0];
256                                 op->o_req_ndn = op->o_bd->be_nsuffix[0];
257                                 rs->sr_err = op->o_bd->be_search(op, rs);
258
259                         } else if (scope0 == LDAP_SCOPE_SUBTREE &&
260                                 dn_match(&op->o_bd->be_nsuffix[0], &ndn))
261                         {
262                                 rs->sr_err = op->o_bd->be_search( op, rs );
263
264                         } else if (scope0 == LDAP_SCOPE_SUBTREE &&
265                                 dnIsSuffix(&op->o_bd->be_nsuffix[0], &ndn))
266                         {
267                                 op->o_req_dn = op->o_bd->be_suffix[0];
268                                 op->o_req_ndn = op->o_bd->be_nsuffix[0];
269                                 rs->sr_err = op->o_bd->be_search( op, rs );
270                                 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
271                                         gs.err = LDAP_SUCCESS;
272                                 }
273
274                         } else if (dnIsSuffix(&ndn, &op->o_bd->be_nsuffix[0])) {
275                                 rs->sr_err = op->o_bd->be_search( op, rs );
276                         }
277
278                         switch ( gs.err ) {
279
280                         /*
281                          * Add errors that should result in dropping
282                          * the search
283                          */
284                         case LDAP_SIZELIMIT_EXCEEDED:
285                         case LDAP_TIMELIMIT_EXCEEDED:
286                         case LDAP_ADMINLIMIT_EXCEEDED:
287                         case LDAP_NO_SUCH_OBJECT:
288                                 goto end_of_loop;
289                         
290                         default:
291                                 break;
292                         }
293                 }
294 end_of_loop:;
295                 op->ors_scope = scope0;
296                 op->ors_slimit = slimit0;
297                 op->ors_tlimit = tlimit0;
298                 op->o_req_dn = dn;
299                 op->o_req_ndn = ndn;
300
301                 break;
302         }
303         if ( !op->o_abandon ) {
304                 op->o_callback = cb.sc_next;
305                 rs->sr_err = gs.err;
306                 rs->sr_matched = gs.matched;
307                 rs->sr_ref = gs.refs;
308
309                 send_ldap_result( op, rs );
310         }
311
312         op->o_bd = b0;
313         op->o_bd->bd_info = bi0;
314         if (gs.matched)
315                 free (gs.matched);
316         if (gs.refs)
317                 ber_bvarray_free(gs.refs);
318         return rs->sr_err;
319 }
320
321 static BackendDB toolDB;
322
323 static int
324 glue_tool_entry_open (
325         BackendDB *b0,
326         int mode
327 )
328 {
329         slap_overinfo   *oi = (slap_overinfo *)b0->bd_info;
330
331         /* We don't know which backend to talk to yet, so just
332          * remember the mode and move on...
333          */
334
335         glueMode = mode;
336         glueBack = NULL;
337         toolDB = *b0;
338         toolDB.bd_info = oi->oi_orig;
339
340         return 0;
341 }
342
343 static int
344 glue_tool_entry_close (
345         BackendDB *b0
346 )
347 {
348         int rc = 0;
349
350         if (glueBack) {
351                 if (!glueBack->be_entry_close)
352                         return 0;
353                 rc = glueBack->be_entry_close (glueBack);
354         }
355         return rc;
356 }
357
358 static slap_overinst *
359 glue_tool_inst(
360         BackendInfo *bi
361 )
362 {
363         slap_overinfo   *oi = (slap_overinfo *)bi;
364         slap_overinst   *on;
365
366         for ( on = oi->oi_list; on; on=on->on_next ) {
367                 if ( !strcmp( on->on_bi.bi_type, glue.on_bi.bi_type ))
368                         return on;
369         }
370         return NULL;
371 }
372
373 /* This function will only be called in tool mode */
374 static int
375 glue_open (
376         BackendInfo *bi
377 )
378 {
379         slap_overinst *on = glue_tool_inst( bi );
380         glueinfo                *gi = on->on_bi.bi_private;
381         int i, rc = 0;
382
383         /* If we were invoked in tool mode, open all the underlying backends */
384         if (slapMode & SLAP_TOOL_MODE) {
385                 for ( i=0; i<gi->gi_nodes; i++ ) {
386                         gi->gi_n[i].gn_be = backendDB + gi->gi_n[i].gn_bx;
387                         rc = backend_startup( gi->gi_n[i].gn_be );
388                         if ( rc ) break;
389                 }
390         } /* other case is impossible */
391         return rc;
392 }
393
394 /* This function will only be called in tool mode */
395 static int
396 glue_close (
397         BackendInfo *bi
398 )
399 {
400         slap_overinst *on = glue_tool_inst( bi );
401         glueinfo                *gi = on->on_bi.bi_private;
402         int i, rc = 0;
403
404         if (slapMode & SLAP_TOOL_MODE) {
405                 for ( i=0; i<gi->gi_nodes; i++ ) {
406                         rc = backend_shutdown( gi->gi_n[i].gn_be );
407                         if ( rc ) break;
408                 }
409         }
410         return rc;
411 }
412
413 static ID
414 glue_tool_entry_first (
415         BackendDB *b0
416 )
417 {
418         slap_overinst   *on = glue_tool_inst( b0->bd_info );
419         glueinfo                *gi = on->on_bi.bi_private;
420         int i;
421
422         /* If we're starting from scratch, start at the most general */
423         if (!glueBack) {
424                 if ( toolDB.be_entry_open && toolDB.be_entry_first ) {
425                         glueBack = &toolDB;
426                 } else {
427                         for (i = gi->gi_nodes-1; i >= 0; i--) {
428                                 if (gi->gi_n[i].gn_be->be_entry_open &&
429                                         gi->gi_n[i].gn_be->be_entry_first) {
430                                                 glueBack = gi->gi_n[i].gn_be;
431                                         break;
432                                 }
433                         }
434                 }
435         }
436         if (!glueBack || !glueBack->be_entry_open || !glueBack->be_entry_first ||
437                 glueBack->be_entry_open (glueBack, glueMode) != 0)
438                 return NOID;
439
440         return glueBack->be_entry_first (glueBack);
441 }
442
443 static ID
444 glue_tool_entry_next (
445         BackendDB *b0
446 )
447 {
448         slap_overinst   *on = glue_tool_inst( b0->bd_info );
449         glueinfo                *gi = on->on_bi.bi_private;
450         int i;
451         ID rc;
452
453         if (!glueBack || !glueBack->be_entry_next)
454                 return NOID;
455
456         rc = glueBack->be_entry_next (glueBack);
457
458         /* If we ran out of entries in one database, move on to the next */
459         while (rc == NOID) {
460                 if ( glueBack && glueBack->be_entry_close )
461                         glueBack->be_entry_close (glueBack);
462                 for (i=0; i<gi->gi_nodes; i++) {
463                         if (gi->gi_n[i].gn_be == glueBack)
464                                 break;
465                 }
466                 if (i == 0) {
467                         glueBack = NULL;
468                         break;
469                 } else {
470                         glueBack = gi->gi_n[i-1].gn_be;
471                         rc = glue_tool_entry_first (b0);
472                 }
473         }
474         return rc;
475 }
476
477 static Entry *
478 glue_tool_entry_get (
479         BackendDB *b0,
480         ID id
481 )
482 {
483         if (!glueBack || !glueBack->be_entry_get)
484                 return NULL;
485
486         return glueBack->be_entry_get (glueBack, id);
487 }
488
489 static ID
490 glue_tool_entry_put (
491         BackendDB *b0,
492         Entry *e,
493         struct berval *text
494 )
495 {
496         BackendDB *be, b2;
497         int rc;
498
499         b2 = *b0;
500         b2.bd_info = (BackendInfo *)glue_tool_inst( b0->bd_info );
501         be = glue_back_select (&b2, &e->e_nname);
502         if ( be == &b2 ) be = &toolDB;
503
504         if (!be->be_entry_put)
505                 return NOID;
506
507         if (!glueBack) {
508                 rc = be->be_entry_open (be, glueMode);
509                 if (rc != 0)
510                         return NOID;
511         } else if (be != glueBack) {
512                 /* If this entry belongs in a different branch than the
513                  * previous one, close the current database and open the
514                  * new one.
515                  */
516                 glueBack->be_entry_close (glueBack);
517                 rc = be->be_entry_open (be, glueMode);
518                 if (rc != 0)
519                         return NOID;
520         }
521         glueBack = be;
522         return be->be_entry_put (be, e, text);
523 }
524
525 static int
526 glue_tool_entry_reindex (
527         BackendDB *b0,
528         ID id
529 )
530 {
531         if (!glueBack || !glueBack->be_entry_reindex)
532                 return -1;
533
534         return glueBack->be_entry_reindex (glueBack, id);
535 }
536
537 static int
538 glue_tool_sync (
539         BackendDB *b0
540 )
541 {
542         slap_overinst   *on = glue_tool_inst( b0->bd_info );
543         glueinfo                *gi = on->on_bi.bi_private;
544         int i;
545
546         /* just sync everyone */
547         for (i = 0; i<gi->gi_nodes; i++)
548                 if (gi->gi_n[i].gn_be->be_sync)
549                         gi->gi_n[i].gn_be->be_sync (gi->gi_n[i].gn_be);
550         return 0;
551 }
552
553 static int
554 glue_db_init(
555         BackendDB *be
556 )
557 {
558         slap_overinst   *on = (slap_overinst *)be->bd_info;
559         slap_overinfo   *oi = on->on_info;
560         glueinfo *gi;
561
562         gi = ch_calloc( 1, sizeof(glueinfo));
563         on->on_bi.bi_private = gi;
564         dnParent( be->be_nsuffix, &gi->gi_pdn );
565
566         /* Currently the overlay framework doesn't handle these entry points
567          * but we need them....
568          */
569         oi->oi_bi.bi_open = glue_open;
570         oi->oi_bi.bi_close = glue_close;
571
572         oi->oi_bi.bi_tool_entry_open = glue_tool_entry_open;
573         oi->oi_bi.bi_tool_entry_close = glue_tool_entry_close;
574         oi->oi_bi.bi_tool_entry_first = glue_tool_entry_first;
575         oi->oi_bi.bi_tool_entry_next = glue_tool_entry_next;
576         oi->oi_bi.bi_tool_entry_get = glue_tool_entry_get;
577         oi->oi_bi.bi_tool_entry_put = glue_tool_entry_put;
578         oi->oi_bi.bi_tool_entry_reindex = glue_tool_entry_reindex;
579         oi->oi_bi.bi_tool_sync = glue_tool_sync;
580
581         /*FIXME : need to add support */
582         oi->oi_bi.bi_tool_dn2id_get = 0;
583         oi->oi_bi.bi_tool_id2entry_get = 0;
584         oi->oi_bi.bi_tool_entry_modify = 0;
585
586         return 0;
587 }
588
589 static int
590 glue_db_destroy (
591         BackendDB *be
592 )
593 {
594         slap_overinst   *on = (slap_overinst *)be->bd_info;
595         glueinfo                *gi = (glueinfo *)on->on_bi.bi_private;
596
597         free (gi);
598         return SLAP_CB_CONTINUE;
599 }
600
601 static int
602 glue_db_open (
603         BackendDB *be
604 )
605 {
606         slap_overinst   *on = (slap_overinst *)be->bd_info;
607         glueinfo                *gi = (glueinfo *)on->on_bi.bi_private;
608         int i;
609
610         for ( i=0; i<gi->gi_nodes; i++ ) {
611                 gi->gi_n[i].gn_be = backendDB + gi->gi_n[i].gn_bx;
612         }
613         return 0;
614 }
615
616 static int
617 glue_db_config(
618         BackendDB       *be,
619         const char      *fname,
620         int             lineno,
621         int             argc,
622         char    **argv
623 )
624 {
625         slap_overinst   *on = (slap_overinst *)be->bd_info;
626         glueinfo                *gi = (glueinfo *)on->on_bi.bi_private;
627
628         if ( strcasecmp( argv[0], "glue-sub" ) == 0 ) {
629                 int async = 0;
630                 BackendDB *b2;
631                 struct berval bv, dn;
632                 gluenode *gn;
633
634                 if ( argc < 2 ) {
635                         fprintf( stderr, "%s: line %d: too few arguments in "
636                                 "\"glue-sub <suffixDN> [async]\"\n", fname, lineno );
637                         return -1;
638                 }
639                 if ( argc == 3 ) {
640                         if ( strcasecmp( argv[2], "async" )) {
641                                 fprintf( stderr, "%s: line %d: unrecognized option "
642                                         "\"%s\" ignored.\n", fname, lineno, argv[2] );
643                         } else {
644                                 async = 1;
645                         }
646                 }
647                 ber_str2bv( argv[1], 0, 0, &bv );
648                 if ( dnNormalize( 0, NULL, NULL, &bv, &dn, NULL )) {
649                         fprintf( stderr, "invalid suffixDN \"%s\"\n", argv[1] );
650                         return -1;
651                 }
652                 b2 = select_backend( &dn, 0, 1 );
653                 if ( !b2 ) {
654                         fprintf( stderr, "%s: line %d: unknown suffix \"%s\"\n",
655                                 fname, lineno, argv[1] );
656                         return -1;
657                 }
658                 SLAP_DBFLAGS(b2) |= SLAP_DBFLAG_GLUE_SUBORDINATE;
659                 gi = (glueinfo *)ch_realloc( gi, sizeof(glueinfo) +
660                         gi->gi_nodes * sizeof(gluenode));
661                 gi->gi_n[gi->gi_nodes].gn_bx = b2 - backendDB;
662                 dnParent( &b2->be_nsuffix[0], &gi->gi_n[gi->gi_nodes].gn_pdn );
663                 gi->gi_n[gi->gi_nodes].gn_async = async;
664                 gi->gi_nodes++;
665                 on->on_bi.bi_private = gi;
666                 return 0;
667         }
668         return SLAP_CONF_UNKNOWN;
669 }
670
671 int
672 glue_init()
673 {
674         glue.on_bi.bi_type = "glue";
675
676         glue.on_bi.bi_db_init = glue_db_init;
677         glue.on_bi.bi_db_config = glue_db_config;
678         glue.on_bi.bi_db_open = glue_db_open;
679         glue.on_bi.bi_db_destroy = glue_db_destroy;
680
681         glue.on_bi.bi_op_search = glue_op_search;
682
683         return overlay_register( &glue );
684 }
685
686 #if SLAPD_OVER_GLUE == SLAPD_MOD_DYNAMIC
687 int
688 init_module( int argc, char *argv[] )
689 {
690         return glue_init();
691 }
692 #endif  /* SLAPD_OVER_GLUE == SLAPD_MOD_DYNAMIC */
693
694 #endif  /* defined(SLAPD_OVER_GLUE */