]> git.sur5r.net Git - openldap/blob - servers/slapd/backglue.c
1bf44af818379b4a3098a3cab0b0425a1610e4a3
[openldap] / servers / slapd / backglue.c
1 /* backglue.c - backend glue routines */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 2001 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  * The only configuration items that are needed for this backend are
14  * the suffixes, and they should be identical to suffixes of other
15  * backends that are being configured. The suffixes must be listed
16  * in order from longest to shortest, (most-specific to least-specific)
17  * in order for the selection to work. Every backend that is being glued
18  * must be fully configured as usual.
19  *
20  * The purpose of this backend is to allow you to split a single database
21  * into pieces (for load balancing purposes, whatever) but still be able
22  * to treat it as a single database after it's been split. As such, each
23  * of the glued backends should have identical rootdn and rootpw.
24  *
25  * If you need more elaborate configuration, you probably should be using
26  * back-meta instead.
27  *  -- Howard Chu
28  */
29
30 #include "portable.h"
31
32 #include <stdio.h>
33
34 #include <ac/socket.h>
35
36 #define SLAPD_TOOLS
37 #include "slap.h"
38
39 typedef struct gluenode {
40         BackendDB *be;
41         char *pdn;
42 } gluenode;
43
44 typedef struct glueinfo {
45         int nodes;
46         gluenode n[1];
47 } glueinfo;
48
49 /* Just like select_backend, but only for our backends */
50 static BackendDB *
51 glue_back_select (
52         BackendDB *be,
53         const char *dn
54 )
55 {
56         glueinfo *gi = (glueinfo *) be->be_private;
57         int i;
58
59         for (i = 0; be->be_nsuffix[i]; i++) {
60                 if (dn_issuffix (dn, be->be_nsuffix[i]->bv_val))
61                         return gi->n[i].be;
62         }
63         return NULL;
64 }
65
66 static int
67 glue_back_db_open (
68         BackendDB *be
69 )
70 {
71         glueinfo *gi;
72         int i, j, k;
73         int ok;
74
75         /*
76          * Done already? 
77          */
78         if (be->be_private)
79                 return 0;
80
81         for (i = 0; be->be_nsuffix[i]; i++);
82
83         gi = (struct glueinfo *) ch_calloc (1, sizeof (glueinfo) +
84                 (i-1) * sizeof (gluenode) );
85
86         be->be_private = gi;
87
88         if (!gi)
89                 return 1;
90
91         gi->nodes = i;
92         be->be_glueflags = SLAP_GLUE_INSTANCE;
93
94         /*
95          * For each of our suffixes, find the real backend that handles this 
96          * suffix. 
97          */
98         for (i = 0; be->be_nsuffix[i]; i++) {
99                 for (j = 0; j < nbackends; j++) {
100                         if (be == &backends[j])
101                                 continue;
102                         ok = 0;
103                         for (k = 0; backends[j].be_nsuffix &&
104                              backends[j].be_nsuffix[k]; k++) {
105                                 if (be->be_nsuffix[i]->bv_len !=
106                                     backends[j].be_nsuffix[k]->bv_len)
107                                         continue;
108                                 if (!strcmp (backends[j].be_nsuffix[k]->bv_val,
109                                              be->be_nsuffix[i]->bv_val)) {
110                                         ok = 1;
111                                         break;
112                                 }
113                         }
114                         if (ok) {
115                                 gi->n[i].be = &backends[j];
116                                 gi->n[i].pdn = dn_parent (NULL,
117                                                  be->be_nsuffix[i]->bv_val);
118                                 if (i < gi->nodes - 1)
119                                         gi->n[i].be->be_glueflags =
120                                                 SLAP_GLUE_SUBORDINATE;
121                                 break;
122                         }
123                 }
124         }
125
126         /* If we were invoked in tool mode, open all the underlying backends */
127         if (slapMode & SLAP_TOOL_MODE) {
128                 for (i = 0; be->be_nsuffix[i]; i++) {
129                         backend_startup (gi->n[i].be);
130                 }
131         }
132         return 0;
133 }
134
135 static int
136 glue_back_db_close (
137         BackendDB *be
138 )
139 {
140         glueinfo *gi = (glueinfo *) be->be_private;
141
142         if (slapMode & SLAP_TOOL_MODE) {
143                 int i;
144                 for (i = 0; be->be_nsuffix[i]; i++) {
145                         backend_shutdown (gi->n[i].be);
146                 }
147         }
148         return 0;
149 }
150 int
151 glue_back_db_destroy (
152         BackendDB *be
153 )
154 {
155         free (be->be_private);
156         return 0;
157 }
158
159 int
160 glue_back_bind (
161         BackendDB *b0,
162         Connection *conn,
163         Operation *op,
164         const char *dn,
165         const char *ndn,
166         int method,
167         struct berval *cred,
168         char **edn
169 )
170 {
171         BackendDB *be;
172         int rc;
173
174         be = glue_back_select (b0, ndn);
175
176         if (be && be->be_bind) {
177                 conn->c_authz_backend = be;
178                 rc = be->be_bind (be, conn, op, dn, ndn, method, cred, edn);
179         } else {
180                 rc = LDAP_UNWILLING_TO_PERFORM;
181                 send_ldap_result (conn, op, rc, NULL, "No bind target found",
182                                   NULL, NULL);
183         }
184         return rc;
185 }
186
187 typedef struct glue_state {
188         int err;
189         int nentries;
190         int matchlen;
191         char *matched;
192         int nrefs;
193         struct berval **refs;
194 } glue_state;
195
196 void
197 glue_back_response (
198         Connection *conn,
199         Operation *op,
200         ber_tag_t tag,
201         ber_int_t msgid,
202         ber_int_t err,
203         const char *matched,
204         const char *text,
205         struct berval **ref,
206         const char *resoid,
207         struct berval *resdata,
208         struct berval *sasldata,
209         LDAPControl **ctrls
210 )
211 {
212         glue_state *gs = op->o_glue;
213
214         if (err == LDAP_SUCCESS || gs->err != LDAP_SUCCESS)
215                 gs->err = err;
216         if (gs->err == LDAP_SUCCESS && gs->matched) {
217                 free (gs->matched);
218                 gs->matchlen = 0;
219         }
220         if (gs->err != LDAP_SUCCESS && matched) {
221                 int len;
222                 len = strlen (matched);
223                 if (len > gs->matchlen) {
224                         if (gs->matched)
225                                 free (gs->matched);
226                         gs->matched = ch_strdup (matched);
227                         gs->matchlen = len;
228                 }
229         }
230         if (ref) {
231                 int i, j, k;
232                 struct berval **new;
233
234                 for (i=0; ref[i]; i++);
235
236                 j = gs->nrefs;
237                 if (!j) {
238                         new = ch_malloc ((i+1)*sizeof(struct berval *));
239                         gs->nrefs = i;
240                 } else {
241                         new = ch_realloc(gs->refs,
242                                 (j+i+1)*sizeof(struct berval *));
243                 }
244                 for (k=0; k<i; j++,k++) {
245                         new[j] = ber_bvdup(ref[k]);
246                 }
247                 new[j] = NULL;
248                 gs->nrefs = j;
249                 gs->refs = new;
250         }
251 }
252
253 void
254 glue_back_sresult (
255         Connection *c,
256         Operation *op,
257         ber_int_t err,
258         const char *matched,
259         const char *text,
260         struct berval **refs,
261         LDAPControl **ctrls,
262         int nentries
263 )
264 {
265         glue_state *gs = op->o_glue;
266
267         gs->nentries += nentries;
268         glue_back_response (c, op, 0, 0, err, matched, text, refs,
269                             NULL, NULL, NULL, ctrls);
270 }
271
272 int
273 glue_back_search (
274         BackendDB *b0,
275         Connection *conn,
276         Operation *op,
277         const char *dn,
278         const char *ndn,
279         int scope,
280         int deref,
281         int slimit,
282         int tlimit,
283         Filter *filter,
284         const char *filterstr,
285         char **attrs,
286         int attrsonly
287 )
288 {
289         glueinfo *gi = (glueinfo *) b0->be_private;
290         BackendDB *be;
291         int i, rc, t2limit = 0, s2limit = 0;
292         long stoptime = 0;
293         glue_state gs = {0};
294
295
296         if (tlimit)
297                 stoptime = slap_get_time () + tlimit;
298
299         switch (scope) {
300         case LDAP_SCOPE_BASE:
301                 be = glue_back_select (b0, ndn);
302
303                 if (be && be->be_search) {
304                         rc = be->be_search (be, conn, op, dn, ndn, scope,
305                                    deref, slimit, tlimit, filter, filterstr,
306                                             attrs, attrsonly);
307                 } else {
308                         rc = LDAP_UNWILLING_TO_PERFORM;
309                         send_ldap_result (conn, op, rc, NULL,
310                                       "No search target found", NULL, NULL);
311                 }
312                 return rc;
313
314         case LDAP_SCOPE_ONELEVEL:
315                 op->o_glue = &gs;
316                 op->o_sresult = glue_back_sresult;
317                 op->o_response = glue_back_response;
318
319                 /*
320                  * Execute in reverse order, most general first 
321                  */
322                 for (i = gi->nodes-1; i >= 0; i--) {
323                         if (!gi->n[i].be->be_search)
324                                 continue;
325                         if (tlimit) {
326                                 t2limit = stoptime - slap_get_time ();
327                                 if (t2limit <= 0)
328                                         break;
329                         }
330                         if (slimit) {
331                                 s2limit = slimit - gs.nentries;
332                                 if (s2limit <= 0)
333                                         break;
334                         }
335                         /*
336                          * check for abandon 
337                          */
338                         ldap_pvt_thread_mutex_lock (&op->o_abandonmutex);
339                         rc = op->o_abandon;
340                         ldap_pvt_thread_mutex_unlock (&op->o_abandonmutex);
341                         if (rc) {
342                                 rc = 0;
343                                 goto done;
344                         }
345                         if (!strcmp (gi->n[i].pdn, ndn)) {
346                                 be = gi->n[i].be;
347                                 rc = be->be_search (be, conn, op,
348                                                     b0->be_suffix[i],
349                                                   b0->be_nsuffix[i]->bv_val,
350                                                     LDAP_SCOPE_BASE, deref,
351                                         s2limit, t2limit, filter, filterstr,
352                                                     attrs, attrsonly);
353                         } else if (dn_issuffix (ndn, b0->be_nsuffix[i]->bv_val)) {
354                                 be = gi->n[i].be;
355                                 rc = be->be_search (be, conn, op,
356                                                     dn, ndn, scope, deref,
357                                         s2limit, t2limit, filter, filterstr,
358                                                     attrs, attrsonly);
359                         }
360                 }
361                 break;
362
363         case LDAP_SCOPE_SUBTREE:
364                 op->o_glue = &gs;
365                 op->o_sresult = glue_back_sresult;
366                 op->o_response = glue_back_response;
367
368                 /*
369                  * Execute in reverse order, most general first 
370                  */
371                 for (i = gi->nodes-1; i >= 0; i--) {
372                         if (!gi->n[i].be->be_search)
373                                 continue;
374                         if (tlimit) {
375                                 t2limit = stoptime - slap_get_time ();
376                                 if (t2limit <= 0)
377                                         break;
378                         }
379                         if (slimit) {
380                                 s2limit = slimit - gs.nentries;
381                                 if (s2limit <= 0)
382                                         break;
383                         }
384                         /*
385                          * check for abandon 
386                          */
387                         ldap_pvt_thread_mutex_lock (&op->o_abandonmutex);
388                         rc = op->o_abandon;
389                         ldap_pvt_thread_mutex_unlock (&op->o_abandonmutex);
390                         if (rc) {
391                                 rc = 0;
392                                 goto done;
393                         }
394                         if (dn_issuffix (ndn, b0->be_nsuffix[i]->bv_val)) {
395                                 be = gi->n[i].be;
396                                 rc = be->be_search (be, conn, op,
397                                                     dn, ndn, scope, deref,
398                                         s2limit, t2limit, filter, filterstr,
399                                                     attrs, attrsonly);
400                         } else if (dn_issuffix (b0->be_nsuffix[i]->bv_val, ndn)) {
401                                 be = gi->n[i].be;
402                                 rc = be->be_search (be, conn, op,
403                                                     b0->be_suffix[i],
404                                                   b0->be_nsuffix[i]->bv_val,
405                                                     scope, deref,
406                                         s2limit, t2limit, filter, filterstr,
407                                                     attrs, attrsonly);
408                         }
409                 }
410                 break;
411         }
412         op->o_sresult = NULL;
413         op->o_response = NULL;
414         op->o_glue = NULL;
415
416         send_search_result (conn, op, gs.err, gs.matched, NULL, gs.refs,
417                             NULL, gs.nentries);
418 done:
419         if (gs.matched)
420                 free (gs.matched);
421         if (gs.refs)
422                 ber_bvecfree(gs.refs);
423         return rc;
424 }
425
426 int
427 glue_back_compare (
428         BackendDB *b0,
429         Connection *conn,
430         Operation *op,
431         const char *dn,
432         const char *ndn,
433         AttributeAssertion *ava
434 )
435 {
436         BackendDB *be;
437         int rc;
438
439         be = glue_back_select (b0, ndn);
440
441         if (be && be->be_compare) {
442                 rc = be->be_compare (be, conn, op, dn, ndn, ava);
443         } else {
444                 rc = LDAP_UNWILLING_TO_PERFORM;
445                 send_ldap_result (conn, op, rc, NULL, "No compare target found",
446                                   NULL, NULL);
447         }
448         return rc;
449 }
450
451 int
452 glue_back_modify (
453         BackendDB *b0,
454         Connection *conn,
455         Operation *op,
456         const char *dn,
457         const char *ndn,
458         Modifications *mod
459 )
460 {
461         BackendDB *be;
462         int rc;
463
464         be = glue_back_select (b0, ndn);
465
466         if (be && be->be_modify) {
467                 rc = be->be_modify (be, conn, op, dn, ndn, mod);
468         } else {
469                 rc = LDAP_UNWILLING_TO_PERFORM;
470                 send_ldap_result (conn, op, rc, NULL, "No modify target found",
471                                   NULL, NULL);
472         }
473         return rc;
474 }
475
476 int
477 glue_back_modrdn (
478         BackendDB *b0,
479         Connection *conn,
480         Operation *op,
481         const char *dn,
482         const char *ndn,
483         const char *newrdn,
484         int del,
485         const char *newsup
486 )
487 {
488         BackendDB *be;
489         int rc;
490
491         be = glue_back_select (b0, ndn);
492
493         if (be && be->be_modrdn) {
494                 rc = be->be_modrdn (be, conn, op, dn, ndn, newrdn, del, newsup);
495         } else {
496                 rc = LDAP_UNWILLING_TO_PERFORM;
497                 send_ldap_result (conn, op, rc, NULL, "No modrdn target found",
498                                   NULL, NULL);
499         }
500         return rc;
501 }
502
503 int
504 glue_back_add (
505         BackendDB *b0,
506         Connection *conn,
507         Operation *op,
508         Entry *e
509 )
510 {
511         BackendDB *be;
512         int rc;
513
514         be = glue_back_select (b0, e->e_ndn);
515
516         if (be && be->be_add) {
517                 rc = be->be_add (be, conn, op, e);
518         } else {
519                 rc = LDAP_UNWILLING_TO_PERFORM;
520                 send_ldap_result (conn, op, rc, NULL, "No add target found",
521                                   NULL, NULL);
522         }
523         return rc;
524 }
525
526 int
527 glue_back_delete (
528         BackendDB *b0,
529         Connection *conn,
530         Operation *op,
531         const char *dn,
532         const char *ndn
533 )
534 {
535         BackendDB *be;
536         int rc;
537
538         be = glue_back_select (b0, ndn);
539
540         if (be && be->be_delete) {
541                 rc = be->be_delete (be, conn, op, dn, ndn);
542         } else {
543                 rc = LDAP_UNWILLING_TO_PERFORM;
544                 send_ldap_result (conn, op, rc, NULL, "No delete target found",
545                                   NULL, NULL);
546         }
547         return rc;
548 }
549
550 int
551 glue_back_release_rw (
552         BackendDB *b0,
553         Connection *conn,
554         Operation *op,
555         Entry *e,
556         int rw
557 )
558 {
559         BackendDB *be;
560         int rc;
561
562         be = glue_back_select (b0, e->e_ndn);
563
564         if (be && be->be_release) {
565                 rc = be->be_release (be, conn, op, e, rw);
566         } else {
567                 entry_free (e);
568                 rc = 0;
569         }
570         return rc;
571 }
572
573 int
574 glue_back_group (
575         BackendDB *b0,
576         Connection *conn,
577         Operation *op,
578         Entry *target,
579         const char *ndn,
580         const char *ondn,
581         ObjectClass *oc,
582         AttributeDescription * ad
583 )
584 {
585         BackendDB *be;
586         int rc;
587
588         be = glue_back_select (b0, ndn);
589
590         if (be && be->be_group) {
591                 rc = be->be_group (be, conn, op, target, ndn, ondn, oc, ad);
592         } else {
593                 rc = LDAP_UNWILLING_TO_PERFORM;
594         }
595         return rc;
596 }
597
598 int
599 glue_back_attribute (
600         BackendDB *b0,
601         Connection *conn,
602         Operation *op,
603         Entry *target,
604         const char *ndn,
605         AttributeDescription *ad,
606         struct berval ***vals
607 )
608 {
609         BackendDB *be;
610         int rc;
611
612         be = glue_back_select (b0, ndn);
613
614         if (be && be->be_attribute) {
615                 rc = be->be_attribute (be, conn, op, target, ndn, ad, vals);
616         } else {
617                 rc = LDAP_UNWILLING_TO_PERFORM;
618         }
619         return rc;
620 }
621
622 int
623 glue_back_referrals (
624         BackendDB *b0,
625         Connection *conn,
626         Operation *op,
627         const char *dn,
628         const char *ndn,
629         const char **text
630 )
631 {
632         BackendDB *be;
633         int rc;
634
635         be = glue_back_select (b0, ndn);
636
637         if (be && be->be_chk_referrals) {
638                 rc = be->be_chk_referrals (be, conn, op, dn, ndn, text);
639         } else {
640                 rc = LDAP_SUCCESS;;
641         }
642         return rc;
643 }
644
645 static int glueMode;
646 static int glueBack;
647
648 int
649 glue_tool_entry_open (
650         BackendDB *b0,
651         int mode
652 )
653 {
654         /* We don't know which backend to talk to yet, so just
655          * remember the mode and move on...
656          */
657
658         glueMode = mode;
659         glueBack = -1;
660
661         return 0;
662 }
663
664 int
665 glue_tool_entry_close (
666         BackendDB *b0
667 )
668 {
669         glueinfo *gi = (glueinfo *) b0->be_private;
670         int i, rc = 0;
671
672         i = glueBack;
673         if (i >= 0) {
674                 if (!gi->n[i].be->be_entry_close)
675                         return 0;
676                 rc = gi->n[i].be->be_entry_close (gi->n[i].be);
677                 glueBack = -1;
678         }
679         return rc;
680 }
681
682 ID
683 glue_tool_entry_first (
684         BackendDB *b0
685 )
686 {
687         glueinfo *gi = (glueinfo *) b0->be_private;
688         int i;
689
690         /* If we're starting from scratch, start at the most general */
691         if (glueBack == -1) {
692                 for (i = gi->nodes-1; i >= 0; i--) {
693                         if (gi->n[i].be->be_entry_open &&
694                             gi->n[i].be->be_entry_first)
695                                 break;
696                 }
697         } else {
698                 i = glueBack;
699         }
700         if (gi->n[i].be->be_entry_open (gi->n[i].be, glueMode) != 0)
701                 return NOID;
702         glueBack = i;
703
704         return gi->n[i].be->be_entry_first (gi->n[i].be);
705 }
706
707 ID
708 glue_tool_entry_next (
709         BackendDB *b0
710 )
711 {
712         glueinfo *gi = (glueinfo *) b0->be_private;
713         int i, rc;
714
715         i = glueBack;
716         rc = gi->n[i].be->be_entry_next (gi->n[i].be);
717
718         /* If we ran out of entries in one database, move on to the next */
719         if (rc == NOID) {
720                 gi->n[i].be->be_entry_close (gi->n[i].be);
721                 i--;
722                 glueBack = i;
723                 if (i < 0)
724                         rc = NOID;
725                 else
726                         rc = glue_tool_entry_first (b0);
727         }
728         return rc;
729 }
730
731 Entry *
732 glue_tool_entry_get (
733         BackendDB *b0,
734         ID id
735 )
736 {
737         glueinfo *gi = (glueinfo *) b0->be_private;
738         int i = glueBack;
739
740         return gi->n[i].be->be_entry_get (gi->n[i].be, id);
741 }
742
743 ID
744 glue_tool_entry_put (
745         BackendDB *b0,
746         Entry *e
747 )
748 {
749         glueinfo *gi = (glueinfo *) b0->be_private;
750         BackendDB *be;
751         int i, rc;
752
753         be = glue_back_select (b0, e->e_ndn);
754         if (!be->be_entry_put)
755                 return NOID;
756
757         i = glueBack;
758         if (i < 0) {
759                 rc = be->be_entry_open (be, glueMode);
760                 if (rc != 0)
761                         return NOID;
762                 glueBack = i;
763         } else if (be != gi->n[i].be) {
764                 /* If this entry belongs in a different branch than the
765                  * previous one, close the current database and open the
766                  * new one.
767                  */
768                 gi->n[i].be->be_entry_close (gi->n[i].be);
769                 glueBack = -1;
770                 for (i = 0; b0->be_nsuffix[i]; i++)
771                         if (gi->n[i].be == be)
772                                 break;
773                 rc = be->be_entry_open (be, glueMode);
774                 if (rc != 0)
775                         return NOID;
776                 glueBack = i;
777         }
778         return be->be_entry_put (be, e);
779 }
780
781 int
782 glue_tool_entry_reindex (
783         BackendDB *b0,
784         ID id
785 )
786 {
787         glueinfo *gi = (glueinfo *) b0->be_private;
788         int i = glueBack;
789
790         if (!gi->n[i].be->be_entry_reindex)
791                 return -1;
792
793         return gi->n[i].be->be_entry_reindex (gi->n[i].be, id);
794 }
795
796 int
797 glue_tool_sync (
798         BackendDB *b0
799 )
800 {
801         glueinfo *gi = (glueinfo *) b0->be_private;
802         int i;
803
804         /* just sync everyone */
805         for (i = 0; b0->be_nsuffix[i]; i++)
806                 if (gi->n[i].be->be_sync)
807                         gi->n[i].be->be_sync (gi->n[i].be);
808         return 0;
809 }
810
811 int
812 glue_back_initialize (
813         BackendInfo *bi
814 )
815 {
816         bi->bi_open = 0;
817         bi->bi_config = 0;
818         bi->bi_close = 0;
819         bi->bi_destroy = 0;
820
821         bi->bi_db_init = 0;
822         bi->bi_db_config = 0;
823         bi->bi_db_open = glue_back_db_open;
824         bi->bi_db_close = glue_back_db_close;
825         bi->bi_db_destroy = glue_back_db_destroy;
826
827         bi->bi_op_bind = glue_back_bind;
828         bi->bi_op_unbind = 0;
829         bi->bi_op_search = glue_back_search;
830         bi->bi_op_compare = glue_back_compare;
831         bi->bi_op_modify = glue_back_modify;
832         bi->bi_op_modrdn = glue_back_modrdn;
833         bi->bi_op_add = glue_back_add;
834         bi->bi_op_delete = glue_back_delete;
835         bi->bi_op_abandon = 0;
836
837         bi->bi_extended = 0;
838
839         bi->bi_entry_release_rw = glue_back_release_rw;
840         bi->bi_acl_group = glue_back_group;
841         bi->bi_acl_attribute = glue_back_attribute;
842         bi->bi_chk_referrals = glue_back_referrals;
843
844         /*
845          * hooks for slap tools
846          */
847         bi->bi_tool_entry_open = glue_tool_entry_open;
848         bi->bi_tool_entry_close = glue_tool_entry_close;
849         bi->bi_tool_entry_first = glue_tool_entry_first;
850         bi->bi_tool_entry_next = glue_tool_entry_next;
851         bi->bi_tool_entry_get = glue_tool_entry_get;
852         bi->bi_tool_entry_put = glue_tool_entry_put;
853         bi->bi_tool_entry_reindex = glue_tool_entry_reindex;
854         bi->bi_tool_sync = glue_tool_sync;
855
856         bi->bi_connection_init = 0;
857         bi->bi_connection_destroy = 0;
858
859         return 0;
860 }