]> git.sur5r.net Git - openldap/blob - servers/slapd/backglue.c
fix format (ITS#2640?)
[openldap] / servers / slapd / backglue.c
1 /* backglue.c - backend glue routines */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 2001-2003 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 /*
9  * Functions to glue a bunch of other backends into a single tree.
10  * All of the glued backends must share a common suffix. E.g., you
11  * can glue o=foo and ou=bar,o=foo but you can't glue o=foo and o=bar.
12  *
13  * This uses the backend structures and routines extensively, but is
14  * not an actual backend of its own. To use it you must add a "subordinate"
15  * keyword to the configuration of other backends. Subordinates will
16  * automatically be connected to their parent backend.
17  *
18  * The purpose of these functions is to allow you to split a single database
19  * into pieces (for load balancing purposes, whatever) but still be able
20  * to treat it as a single database after it's been split. As such, each
21  * of the glued backends should have identical rootdn and rootpw.
22  *
23  * If you need more elaborate configuration, you probably should be using
24  * back-meta instead.
25  *  -- Howard Chu
26  */
27
28 #include "portable.h"
29
30 #include <stdio.h>
31
32 #include <ac/string.h>
33 #include <ac/socket.h>
34
35 #define SLAPD_TOOLS
36 #include "slap.h"
37
38 typedef struct gluenode {
39         BackendDB *be;
40         struct berval pdn;
41 } gluenode;
42
43 typedef struct glueinfo {
44         BackendInfo bi;
45         BackendDB bd;
46         int nodes;
47         gluenode n[1];
48 } glueinfo;
49
50 static int glueMode;
51 static BackendDB *glueBack;
52
53 static slap_response glue_back_response;
54
55 /* Just like select_backend, but only for our backends */
56 static BackendDB *
57 glue_back_select (
58         BackendDB *be,
59         const char *dn
60 )
61 {
62         glueinfo *gi = (glueinfo *) be->bd_info;
63         struct berval bv;
64         int i;
65
66         bv.bv_len = strlen(dn);
67         bv.bv_val = (char *) dn;
68
69         for (i = 0; i<gi->nodes; i++) {
70                 if (dnIsSuffix(&bv, &gi->n[i].be->be_nsuffix[0])) {
71                         return gi->n[i].be;
72                 }
73         }
74         return NULL;
75 }
76
77 /* This function will only be called in tool mode */
78 static int
79 glue_back_open (
80         BackendInfo *bi
81 )
82 {
83         int rc = 0;
84         static int glueOpened = 0;
85
86         if (glueOpened) return 0;
87
88         glueOpened = 1;
89
90         /* If we were invoked in tool mode, open all the underlying backends */
91         if (slapMode & SLAP_TOOL_MODE) {
92                 rc = backend_startup (NULL);
93         } /* other case is impossible */
94         return rc;
95 }
96
97 /* This function will only be called in tool mode */
98 static int
99 glue_back_close (
100         BackendInfo *bi
101 )
102 {
103         static int glueClosed = 0;
104         int rc = 0;
105
106         if (glueClosed) return 0;
107
108         glueClosed = 1;
109
110         if (slapMode & SLAP_TOOL_MODE) {
111                 rc = backend_shutdown (NULL);
112         }
113         return rc;
114 }
115
116 static int
117 glue_back_db_open (
118         BackendDB *be
119 )
120 {
121         glueinfo *gi = (glueinfo *) be->bd_info;
122         static int glueOpened = 0;
123         int rc = 0;
124
125         if (glueOpened) return 0;
126
127         glueOpened = 1;
128
129         gi->bd.be_acl = be->be_acl;
130
131         if (gi->bd.bd_info->bi_db_open)
132                 rc = gi->bd.bd_info->bi_db_open(&gi->bd);
133
134         return rc;
135 }
136
137 static int
138 glue_back_db_close (
139         BackendDB *be
140 )
141 {
142         glueinfo *gi = (glueinfo *) be->bd_info;
143         static int glueClosed = 0;
144
145         if (glueClosed) return 0;
146
147         glueClosed = 1;
148
149         /* Close the master */
150         if (gi->bd.bd_info->bi_db_close)
151                 gi->bd.bd_info->bi_db_close( &gi->bd );
152
153         return 0;
154 }
155
156 static int
157 glue_back_db_destroy (
158         BackendDB *be
159 )
160 {
161         glueinfo *gi = (glueinfo *) be->bd_info;
162
163         if (gi->bd.bd_info->bi_db_destroy)
164                 gi->bd.bd_info->bi_db_destroy( &gi->bd );
165         free (gi);
166         return 0;
167 }
168
169 typedef struct glue_state {
170         int err;
171         int matchlen;
172         char *matched;
173         int nrefs;
174         BerVarray refs;
175         slap_callback *prevcb;
176 } glue_state;
177
178 static int
179 glue_back_response ( Operation *op, SlapReply *rs )
180 {
181         glue_state *gs = op->o_callback->sc_private;
182         slap_callback *tmp = op->o_callback;
183
184         switch(rs->sr_type) {
185         case REP_SEARCH:
186         case REP_SEARCHREF:
187                 op->o_callback = gs->prevcb;
188                 if (op->o_callback && op->o_callback->sc_response) {
189                         rs->sr_err = op->o_callback->sc_response( op, rs );
190                 } else if (rs->sr_type == REP_SEARCH) {
191                         rs->sr_err = send_search_entry( op, rs );
192                 } else {
193                         rs->sr_err = send_search_reference( op, rs );
194                 }
195                 op->o_callback = tmp;
196                 return rs->sr_err;
197
198         default:
199                 if (rs->sr_err == LDAP_SUCCESS || gs->err != LDAP_SUCCESS)
200                         gs->err = rs->sr_err;
201                 if (gs->err == LDAP_SUCCESS && gs->matched) {
202                         ch_free (gs->matched);
203                         gs->matched = NULL;
204                         gs->matchlen = 0;
205                 }
206                 if (gs->err != LDAP_SUCCESS && rs->sr_matched) {
207                         int len;
208                         len = strlen (rs->sr_matched);
209                         if (len > gs->matchlen) {
210                                 if (gs->matched)
211                                         ch_free (gs->matched);
212                                 gs->matched = ch_strdup (rs->sr_matched);
213                                 gs->matchlen = len;
214                         }
215                 }
216                 if (rs->sr_ref) {
217                         int i, j, k;
218                         BerVarray new;
219
220                         for (i=0; rs->sr_ref[i].bv_val; i++);
221
222                         j = gs->nrefs;
223                         if (!j) {
224                                 new = ch_malloc ((i+1)*sizeof(struct berval));
225                         } else {
226                                 new = ch_realloc(gs->refs,
227                                         (j+i+1)*sizeof(struct berval));
228                         }
229                         for (k=0; k<i; j++,k++) {
230                                 ber_dupbv( &new[j], &rs->sr_ref[k] );
231                         }
232                         new[j].bv_val = NULL;
233                         gs->nrefs = j;
234                         gs->refs = new;
235                 }
236         }
237         return 0;
238 }
239
240 static int
241 glue_back_search ( Operation *op, SlapReply *rs )
242 {
243         BackendDB *b0 = op->o_bd;
244         glueinfo *gi = (glueinfo *) b0->bd_info;
245         int i;
246         long stoptime = 0;
247         glue_state gs = {0, 0, NULL, 0, NULL, NULL};
248         slap_callback cb = { glue_back_response, NULL };
249         int scope0, slimit0, tlimit0;
250         struct berval dn, ndn;
251
252         cb.sc_private = &gs;
253
254         gs.prevcb = op->o_callback;
255
256         if (op->ors_tlimit) {
257                 stoptime = slap_get_time () + op->ors_tlimit;
258         }
259
260         switch (op->ors_scope) {
261         case LDAP_SCOPE_BASE:
262                 op->o_bd = glue_back_select (b0, op->o_req_ndn.bv_val);
263
264                 if (op->o_bd && op->o_bd->be_search) {
265                         rs->sr_err = op->o_bd->be_search( op, rs );
266                 } else {
267                         send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
268                                       "No search target found");
269                 }
270                 return rs->sr_err;
271
272         case LDAP_SCOPE_ONELEVEL:
273         case LDAP_SCOPE_SUBTREE:
274                 op->o_callback = &cb;
275                 rs->sr_err = gs.err = LDAP_UNWILLING_TO_PERFORM;
276                 scope0 = op->ors_scope;
277                 slimit0 = op->ors_slimit;
278                 tlimit0 = op->ors_tlimit;
279                 dn = op->o_req_dn;
280                 ndn = op->o_req_ndn;
281
282                 /*
283                  * Execute in reverse order, most general first 
284                  */
285                 for (i = gi->nodes-1; i >= 0; i--) {
286                         if (!gi->n[i].be || !gi->n[i].be->be_search)
287                                 continue;
288                         if (tlimit0) {
289                                 op->ors_tlimit = stoptime - slap_get_time ();
290                                 if (op->ors_tlimit <= 0) {
291                                         rs->sr_err = gs.err = LDAP_TIMELIMIT_EXCEEDED;
292                                         break;
293                                 }
294                         }
295                         if (slimit0) {
296                                 op->ors_slimit = slimit0 - rs->sr_nentries;
297                                 if (op->ors_slimit <= 0) {
298                                         rs->sr_err = gs.err = LDAP_SIZELIMIT_EXCEEDED;
299                                         break;
300                                 }
301                         }
302                         rs->sr_err = 0;
303                         /*
304                          * check for abandon 
305                          */
306                         if (op->o_abandon) {
307                                 goto done;
308                         }
309                         op->o_bd = gi->n[i].be;
310                         if (scope0 == LDAP_SCOPE_ONELEVEL && 
311                                 dn_match(&gi->n[i].pdn, &ndn)) {
312                                 op->ors_scope = LDAP_SCOPE_BASE;
313                                 op->o_req_dn = op->o_bd->be_suffix[0];
314                                 op->o_req_ndn = op->o_bd->be_nsuffix[0];
315                                 rs->sr_err = op->o_bd->be_search(op, rs);
316
317                         } else if (scope0 == LDAP_SCOPE_SUBTREE &&
318                                 dnIsSuffix(&op->o_bd->be_nsuffix[0], &ndn)) {
319                                 op->o_req_dn = op->o_bd->be_suffix[0];
320                                 op->o_req_ndn = op->o_bd->be_nsuffix[0];
321                                 rs->sr_err = op->o_bd->be_search( op, rs );
322
323                         } else if (dnIsSuffix(&ndn, &op->o_bd->be_nsuffix[0])) {
324                                 rs->sr_err = op->o_bd->be_search( op, rs );
325                         }
326
327                         switch ( gs.err ) {
328
329                         /*
330                          * Add errors that should result in dropping
331                          * the search
332                          */
333                         case LDAP_SIZELIMIT_EXCEEDED:
334                         case LDAP_TIMELIMIT_EXCEEDED:
335                         case LDAP_ADMINLIMIT_EXCEEDED:
336                                 goto end_of_loop;
337                         
338                         default:
339                                 break;
340                         }
341                 }
342 end_of_loop:;
343                 op->ors_scope = scope0;
344                 op->ors_slimit = slimit0;
345                 op->ors_tlimit = tlimit0;
346                 op->o_req_dn = dn;
347                 op->o_req_ndn = ndn;
348
349                 break;
350         }
351         op->o_callback = gs.prevcb;
352         rs->sr_err = gs.err;
353         rs->sr_matched = gs.matched;
354         rs->sr_ref = gs.refs;
355
356         send_ldap_result( op, rs );
357
358 done:
359         op->o_bd = b0;
360         if (gs.matched)
361                 free (gs.matched);
362         if (gs.refs)
363                 ber_bvarray_free(gs.refs);
364         return rs->sr_err;
365 }
366
367
368 static int
369 glue_tool_entry_open (
370         BackendDB *b0,
371         int mode
372 )
373 {
374         /* We don't know which backend to talk to yet, so just
375          * remember the mode and move on...
376          */
377
378         glueMode = mode;
379         glueBack = NULL;
380
381         return 0;
382 }
383
384 static int
385 glue_tool_entry_close (
386         BackendDB *b0
387 )
388 {
389         int rc = 0;
390
391         if (glueBack) {
392                 if (!glueBack->be_entry_close)
393                         return 0;
394                 rc = glueBack->be_entry_close (glueBack);
395         }
396         return rc;
397 }
398
399 static ID
400 glue_tool_entry_first (
401         BackendDB *b0
402 )
403 {
404         glueinfo *gi = (glueinfo *) b0->bd_info;
405         int i;
406
407         /* If we're starting from scratch, start at the most general */
408         if (!glueBack) {
409                 for (i = gi->nodes-1; i >= 0; i--) {
410                         if (gi->n[i].be->be_entry_open &&
411                             gi->n[i].be->be_entry_first) {
412                                 glueBack = gi->n[i].be;
413                                 break;
414                         }
415                 }
416
417         }
418         if (!glueBack || glueBack->be_entry_open (glueBack, glueMode) != 0)
419                 return NOID;
420
421         return glueBack->be_entry_first (glueBack);
422 }
423
424 static ID
425 glue_tool_entry_next (
426         BackendDB *b0
427 )
428 {
429         glueinfo *gi = (glueinfo *) b0->bd_info;
430         int i;
431         ID rc;
432
433         if (!glueBack || !glueBack->be_entry_next)
434                 return NOID;
435
436         rc = glueBack->be_entry_next (glueBack);
437
438         /* If we ran out of entries in one database, move on to the next */
439         if (rc == NOID) {
440                 glueBack->be_entry_close (glueBack);
441                 for (i=0; i<gi->nodes; i++) {
442                         if (gi->n[i].be == glueBack)
443                                 break;
444                 }
445                 if (i == 0) {
446                         glueBack = NULL;
447                         rc = NOID;
448                 } else {
449                         glueBack = gi->n[i-1].be;
450                         rc = glue_tool_entry_first (b0);
451                 }
452         }
453         return rc;
454 }
455
456 static Entry *
457 glue_tool_entry_get (
458         BackendDB *b0,
459         ID id
460 )
461 {
462         if (!glueBack || !glueBack->be_entry_get)
463                 return NULL;
464
465         return glueBack->be_entry_get (glueBack, id);
466 }
467
468 static ID
469 glue_tool_entry_put (
470         BackendDB *b0,
471         Entry *e,
472         struct berval *text
473 )
474 {
475         BackendDB *be;
476         int rc;
477
478         be = glue_back_select (b0, e->e_ndn);
479         if (!be->be_entry_put)
480                 return NOID;
481
482         if (!glueBack) {
483                 rc = be->be_entry_open (be, glueMode);
484                 if (rc != 0)
485                         return NOID;
486         } else if (be != glueBack) {
487                 /* If this entry belongs in a different branch than the
488                  * previous one, close the current database and open the
489                  * new one.
490                  */
491                 glueBack->be_entry_close (glueBack);
492                 rc = be->be_entry_open (be, glueMode);
493                 if (rc != 0)
494                         return NOID;
495         }
496         glueBack = be;
497         return be->be_entry_put (be, e, text);
498 }
499
500 static int
501 glue_tool_entry_reindex (
502         BackendDB *b0,
503         ID id
504 )
505 {
506         if (!glueBack || !glueBack->be_entry_reindex)
507                 return -1;
508
509         return glueBack->be_entry_reindex (glueBack, id);
510 }
511
512 static int
513 glue_tool_sync (
514         BackendDB *b0
515 )
516 {
517         glueinfo *gi = (glueinfo *) b0->bd_info;
518         int i;
519
520         /* just sync everyone */
521         for (i = 0; i<gi->nodes; i++)
522                 if (gi->n[i].be->be_sync)
523                         gi->n[i].be->be_sync (gi->n[i].be);
524         return 0;
525 }
526
527 int
528 glue_sub_init( )
529 {
530         int i, j;
531         int cont = num_subordinates;
532         BackendDB *b1, *be;
533         BackendInfo *bi = NULL;
534         glueinfo *gi;
535
536         /* While there are subordinate backends, search backwards through the
537          * backends and connect them to their superior.
538          */
539         for (i = nBackendDB - 1, b1=&backendDB[i]; cont && i>=0; b1--,i--) {
540                 if (SLAP_GLUE_SUBORDINATE ( b1 ) ) {
541                         /* The last database cannot be a subordinate of noone */
542                         if (i == nBackendDB - 1) {
543                                 b1->be_flags ^= SLAP_BFLAG_GLUE_SUBORDINATE;
544                         }
545                         continue;
546                 }
547                 gi = NULL;
548                 for (j = i-1, be=&backendDB[j]; j>=0; be--,j--) {
549                         if ( ! SLAP_GLUE_SUBORDINATE( be ) ) {
550                                 continue;
551                         }
552                         /* We will only link it once */
553                         if ( SLAP_GLUE_LINKED( be ) ) {
554                                 continue;
555                         }
556                         if (!dnIsSuffix(&be->be_nsuffix[0], &b1->be_nsuffix[0])) {
557                                 continue;
558                         }
559                         cont--;
560                         be->be_flags |= SLAP_BFLAG_GLUE_LINKED;
561                         if (gi == NULL) {
562                                 /* We create a copy of the superior's be
563                                  * structure, pointing to all of its original
564                                  * information. Then we replace elements of
565                                  * the superior's info with our own. The copy
566                                  * is used whenever we have operations to pass
567                                  * down to the real database.
568                                  */
569                                 b1->be_flags |= SLAP_BFLAG_GLUE_INSTANCE;
570                                 gi = (glueinfo *)ch_malloc(sizeof(glueinfo));
571                                 gi->nodes = 0;
572                                 gi->bd = *b1;
573                                 gi->bi = *b1->bd_info;
574                                 bi = (BackendInfo *)gi;
575                                 bi->bi_open = glue_back_open;
576                                 bi->bi_close = glue_back_close;
577                                 bi->bi_db_open = glue_back_db_open;
578                                 bi->bi_db_close = glue_back_db_close;
579                                 bi->bi_db_destroy = glue_back_db_destroy;
580
581                                 bi->bi_op_search = glue_back_search;
582
583                                 /*
584                                  * hooks for slap tools
585                                  */
586                                 bi->bi_tool_entry_open = glue_tool_entry_open;
587                                 bi->bi_tool_entry_close = glue_tool_entry_close;
588                                 bi->bi_tool_entry_first = glue_tool_entry_first;
589                                 bi->bi_tool_entry_next = glue_tool_entry_next;
590                                 bi->bi_tool_entry_get = glue_tool_entry_get;
591                                 bi->bi_tool_entry_put = glue_tool_entry_put;
592                                 bi->bi_tool_entry_reindex = glue_tool_entry_reindex;
593                                 bi->bi_tool_sync = glue_tool_sync;
594                         } else {
595                                 gi = (glueinfo *)ch_realloc(gi,
596                                         sizeof(glueinfo) +
597                                         gi->nodes * sizeof(gluenode));
598                         }
599                         gi->n[gi->nodes].be = be;
600                         dnParent( &be->be_nsuffix[0], &gi->n[gi->nodes].pdn ); 
601                         gi->nodes++;
602                 }
603                 if (gi) {
604                         /* One more node for the master */
605                         gi = (glueinfo *)ch_realloc(gi,
606                                 sizeof(glueinfo) + gi->nodes * sizeof(gluenode));
607                         gi->n[gi->nodes].be = &gi->bd;
608                         dnParent( &b1->be_nsuffix[0], &gi->n[gi->nodes].pdn );
609                         gi->nodes++;
610                         b1->bd_info = (BackendInfo *)gi;
611                 }
612         }
613         /* If there are any unresolved subordinates left, something is wrong */
614         return cont;
615 }