]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/unique.c
c3b280d4c3adf2ac4efb3843fc3e9a26205f1598
[openldap] / servers / slapd / overlays / unique.c
1 /* unique.c - attribute uniqueness module */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2004 The OpenLDAP Foundation.
6  * Portions Copyright 2004 Symas Corporation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Symas Corp. for inclusion in
19  * OpenLDAP Software.  This work was sponsored by Hewlett-Packard.
20  */
21
22 #include "portable.h"
23
24 #ifdef SLAPD_OVER_UNIQUE
25
26 #include <stdio.h>
27
28 #include <ac/string.h>
29 #include <ac/socket.h>
30
31 #include "slap.h"
32
33 static slap_overinst unique;
34
35 typedef struct unique_attrs_s {
36         struct unique_attrs_s *next;            /* list of attrs */
37         AttributeDescription *attr;
38 } unique_attrs;
39
40 typedef struct unique_data_s {
41         const char *message;                    /* breadcrumbs */
42         struct unique_attrs_s *attrs;           /* list of known attrs */
43         struct unique_attrs_s *ignore;          /* list of ignored attrs */
44         BerValue dn;                            /* base of "unique tree" */
45         char strict;                            /* null considered unique too */
46 } unique_data;
47
48 typedef struct unique_counter_s {
49         int count;
50 } unique_counter;
51
52 /*
53 ** allocate new unique_data;
54 ** initialize, copy basedn;
55 ** store in on_bi.bi_private;
56 **
57 */
58
59 static int unique_db_init(
60         BackendDB       *be
61 )
62 {
63         slap_overinst *on = (slap_overinst *)be->bd_info;
64         unique_data *ud   = ch_malloc(sizeof(unique_data));
65
66         /* Debug(LDAP_DEBUG_TRACE, "==> unique_init\n", 0, 0, 0); */
67
68         ud->message     = "_init";
69         ud->attrs       = NULL;
70         ud->ignore      = NULL;
71         ud->strict      = 0;
72
73         /* default to the base of our configured database */
74         ber_dupbv(&ud->dn, &be->be_nsuffix[0]);
75         on->on_bi.bi_private = ud;
76
77         return 0;
78 }
79
80
81 /*
82 ** if command = attributes:
83 **      foreach argument:
84 **              convert to attribute;
85 **              add to configured attribute list;
86 ** elseif command = base:
87 **      set our basedn to argument;
88 ** else complain about invalid directive;
89 **
90 */
91
92 static int unique_config(
93         BackendDB       *be,
94         const char      *fname,
95         int             lineno,
96         int             argc,
97         char            **argv
98 )
99 {
100         slap_overinst *on = (slap_overinst *) be->bd_info;
101         unique_data *ud   = on->on_bi.bi_private;
102         unique_attrs *up;
103         const char *text;
104         AttributeDescription *ad;
105         int i;
106
107         ud->message = "_config";
108         Debug(LDAP_DEBUG_TRACE, "==> unique_config\n", 0, 0, 0);
109
110         if(!strcasecmp(*argv, "unique_attributes") ||
111            !strcasecmp(*argv, "unique_ignore")) {
112                 for(i = 1; i < argc; i++) {
113                         for(up = ud->attrs; up; up = up->next)
114                             if(!strcmp(argv[i], up->attr->ad_cname.bv_val)) {
115                                 Debug(LDAP_DEBUG_ANY,
116                                         "%s: line %d: duplicate attribute <s>, ignored\n",
117                                         fname, lineno, argv[i]);
118                                 continue;
119                         }
120                         ad = NULL;
121                         if(slap_str2ad(argv[i], &ad, &text) != LDAP_SUCCESS) {
122                                 Debug(LDAP_DEBUG_ANY,
123                                         "%s: line %d: bad attribute <%s>, ignored\n",
124                                         fname, lineno, text);
125                                 continue;               /* XXX */
126                         } else if(ad->ad_next) {
127                                 Debug(LDAP_DEBUG_ANY,
128                                         "%s: line %d: multiple attributes match <%s>, ignored\n",
129                                         fname, lineno, argv[i]);
130                                 continue;
131                         }
132                         up = ch_malloc(sizeof(unique_attrs));
133                         up->attr = ad;
134                         if(!strcasecmp(*argv, "unique_ignore")) {
135                                 up->next = ud->ignore;
136                                 ud->ignore = up;
137                         } else {
138                                 up->next = ud->attrs;
139                                 ud->attrs = up;
140                         }
141                         Debug(LDAP_DEBUG_ANY, "%s: line %d: new attribute <%s>\n",
142                                 fname, lineno, argv[i]);
143                 }
144         } else if(!strcasecmp(*argv, "unique_strict")) {
145                 ud->strict = 1;
146         } else if(!strcasecmp(*argv, "unique_base")) {
147                 struct berval bv;
148                 ber_str2bv( argv[1], 0, 0, &bv );
149                 ch_free(ud->dn.bv_val);
150                 dnNormalize(0, NULL, NULL, &bv, &ud->dn, NULL);
151                 Debug(LDAP_DEBUG_ANY, "%s: line %d: new base dn <%s>\n",
152                         fname, lineno, argv[1]);
153         } else {
154                 return(SLAP_CONF_UNKNOWN);
155         }
156
157         return(0);
158 }
159
160
161 /*
162 ** mostly, just print the init message;
163 **
164 */
165
166 static int
167 unique_open(
168         BackendDB *be
169 )
170 {
171         slap_overinst *on       = (slap_overinst *)be->bd_info;
172         unique_data *ud         = on->on_bi.bi_private;
173         ud->message             = "_open";
174
175         Debug(LDAP_DEBUG_TRACE, "unique_open: overlay initialized\n", 0, 0, 0);
176
177         return(0);
178 }
179
180
181 /*
182 ** foreach configured attribute:
183 **      free it;
184 ** free our basedn;
185 ** (do not) free ud->message;
186 ** reset on_bi.bi_private;
187 ** free our config data;
188 **
189 */
190
191 static int
192 unique_close(
193         BackendDB *be
194 )
195 {
196         slap_overinst *on       = (slap_overinst *) be->bd_info;
197         unique_data *ud         = on->on_bi.bi_private;
198         unique_attrs *ii, *ij;
199         ud->message             = "_close";
200
201         Debug(LDAP_DEBUG_TRACE, "==> unique_close\n", 0, 0, 0);
202
203         for(ii = ud->attrs; ii; ii = ij) {
204                 ij = ii->next;
205                 ch_free(ii);
206         }
207
208         for(ii = ud->ignore; ii; ii = ij) {
209                 ij = ii->next;
210                 ch_free(ii);
211         }
212
213         ch_free(ud->dn.bv_val);
214
215         on->on_bi.bi_private = NULL;    /* XXX */
216
217         ch_free(ud);
218
219         return(0);
220 }
221
222
223 /*
224 ** search callback
225 **      if this is a REP_SEARCH, count++;
226 **
227 */
228
229 static int count_attr_cb(
230         Operation *op,
231         SlapReply *rs
232 )
233 {
234         /* because you never know */
235         if(!op || !rs) return(0);
236
237         /* Only search entries are interesting */
238         if(rs->sr_type != REP_SEARCH) return(0);
239
240         Debug(LDAP_DEBUG_TRACE, "==> count_attr_cb <%s>\n",
241                 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
242
243         ((unique_counter*)op->o_callback->sc_private)->count++;
244
245         return(0);
246 }
247
248 /* XXX extraneous (slap_response*) to avoid compiler warning */
249
250 static int unique_add(
251         Operation *op,
252         SlapReply *rs
253 )
254 {
255         Operation nop = *op;
256         SlapReply nrs = { REP_RESULT };
257         slap_callback cb = { NULL, NULL, NULL, NULL }; /* XXX */
258         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
259
260         Attribute *a;
261         BerVarray b = NULL;
262         char *key, *kp;
263         int i, rc, ks = 16;
264         unique_attrs *up;
265         unique_counter uq = { 0 };
266         unique_data *ud = on->on_bi.bi_private;
267
268         Debug(LDAP_DEBUG_TRACE, "==> unique_add <%s>\n", op->o_req_dn.bv_val, 0, 0);
269
270         /* validate backend. Should have already been done, but whatever */
271         nop.o_bd = select_backend(&ud->dn, 0, 1);
272         if(nop.o_bd) {
273                 if (!nop.o_bd->be_search) {
274                         op->o_bd->bd_info = (BackendInfo *) on->on_info;
275                         send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
276                         "backend missing search function");
277                         return(rs->sr_err);
278                 }
279         } else {
280                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
281                 send_ldap_error(op, rs, LDAP_OTHER,
282                         "no known backend? this shouldn't be happening!");
283                 return(rs->sr_err);
284         }
285
286 /*
287 ** count everything first;
288 ** allocate some memory;
289 ** write the search key;
290 **
291 */
292
293         if(!(a = op->ora_e->e_attrs)) {
294                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
295                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
296                         "unique_add() got null op.ora_e.e_attrs");
297                 return(rs->sr_err);
298         } else for(; a; a = a->a_next) {
299                 if(is_at_operational(a->a_desc->ad_type)) continue;
300                 if(ud->ignore) {
301                         for(up = ud->ignore; up; up = up->next)
302                                 if(a->a_desc == up->attr) break;
303                         if(up) continue;
304                 }
305                 if(ud->attrs) {
306                         for(up = ud->attrs; up; up = up->next)
307                                 if(a->a_desc == up->attr) break;
308                         if(!up) continue;
309                 }
310                 if((b = a->a_vals) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
311                         ks += b[i].bv_len + a->a_desc->ad_cname.bv_len + 3;
312                 else if(ud->strict)
313                         ks += a->a_desc->ad_cname.bv_len + 4;   /* (attr=*) */
314         }
315
316         key = ch_malloc(ks);
317
318         kp = key + sprintf(key, "(|");
319
320         for(a = op->ora_e->e_attrs; a; a = a->a_next) {
321                 if(is_at_operational(a->a_desc->ad_type)) continue;
322                 if(ud->ignore) {
323                         for(up = ud->ignore; up; up = up->next)
324                                 if(a->a_desc == up->attr) break;
325                         if(up) continue;
326                 }
327                 if(ud->attrs) {
328                         for(up = ud->attrs; up; up = up->next)
329                                 if(a->a_desc == up->attr) break;
330                         if(!up) continue;
331                 }
332                 if((b = a->a_vals) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
333                         kp += sprintf(kp, "(%s=%s)", a->a_desc->ad_cname.bv_val, b[i].bv_val);
334                 else if(ud->strict)
335                         kp += sprintf(kp, "(%s=*)", a->a_desc->ad_cname.bv_val);
336         }
337
338         kp += sprintf(kp, ")");
339
340         Debug(LDAP_DEBUG_TRACE, "=> unique_add %s\n", key, 0, 0);
341
342         nop.ors_filter = str2filter_x(&nop, key);
343         ber_str2bv(key, 0, 0, &nop.ors_filterstr);
344
345         cb.sc_response  = (slap_response*)count_attr_cb;
346         cb.sc_private   = &uq;
347         nop.o_callback  = &cb;
348         nop.o_tag       = LDAP_REQ_SEARCH;
349         nop.ors_scope   = LDAP_SCOPE_SUBTREE;
350         nop.ors_deref   = LDAP_DEREF_NEVER;
351         nop.ors_slimit  = SLAP_NO_LIMIT;
352         nop.ors_tlimit  = SLAP_NO_LIMIT;
353         nop.o_req_ndn   = ud->dn;
354         nop.o_ndn = op->o_bd->be_rootndn;
355
356         rc = nop.o_bd->be_search(&nop, &nrs);
357         filter_free_x(&nop, nop.ors_filter);
358         ch_free( key );
359
360         if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
361                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
362                 send_ldap_error(op, rs, rc, "unique_add search failed");
363                 return(rs->sr_err);
364         }
365
366         Debug(LDAP_DEBUG_TRACE, "=> unique_add found %d records\n", uq.count, 0, 0);
367
368         if(uq.count) {
369                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
370                 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
371                         "some attributes not unique");
372                 return(rs->sr_err);
373         }
374
375         return(SLAP_CB_CONTINUE);
376 }
377
378
379 static int unique_modify(
380         Operation *op,
381         SlapReply *rs
382 )
383 {
384         Operation nop = *op;
385         SlapReply nrs = { REP_RESULT };
386         slap_callback cb = { NULL, (slap_response*)count_attr_cb, NULL, NULL };
387         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
388
389         BerVarray b = NULL;
390         Modifications *m;
391         char *key, *kp;
392         int i, rc, ks = 16;             /* a handful of extra bytes */
393         unique_attrs *up;
394         unique_counter uq = { 0 };
395         unique_data *ud = on->on_bi.bi_private;
396
397         Debug(LDAP_DEBUG_TRACE, "==> unique_modify <%s>\n", op->o_req_dn.bv_val, 0, 0);
398
399         nop.o_bd = select_backend(&ud->dn, 0, 1);
400         if(nop.o_bd) {
401                 if (!nop.o_bd->be_search) {
402                         op->o_bd->bd_info = (BackendInfo *) on->on_info;
403                         send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
404                         "backend missing search function");
405                         return(rs->sr_err);
406                 }
407         } else {
408                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
409                 send_ldap_error(op, rs, LDAP_OTHER,
410                         "no known backend? this shouldn't be happening!");
411                 return(rs->sr_err);
412         }
413
414 /*
415 ** count everything first;
416 ** allocate some memory;
417 ** write the search key;
418 **
419 */
420
421         if(!(m = op->orm_modlist)) {
422                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
423                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
424                         "unique_modify() got null op.orm_modlist");
425                 return(rs->sr_err);
426         } else for(; m; m = m->sml_next) {
427                 if(is_at_operational(m->sml_desc->ad_type) ||
428                         ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE)) continue;
429                 if(ud->ignore) {
430                         for(up = ud->ignore; up; up = up->next)
431                                 if(m->sml_desc == up->attr) break;
432                         if(up) continue;
433                 }
434                 if(ud->attrs) {
435                         for(up = ud->attrs; up; up = up->next)
436                                 if(m->sml_desc == up->attr) break;
437                         if(!up) continue;
438                 }
439                 if((b = m->sml_values) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
440                         ks += b[i].bv_len + m->sml_desc->ad_cname.bv_len + 3;
441                 else if(ud->strict)
442                         ks += m->sml_desc->ad_cname.bv_len + 4; /* (attr=*) */
443         }
444
445         key = ch_malloc(ks);
446
447         kp = key + sprintf(key, "(|");
448
449         for(m = op->orm_modlist; m; m = m->sml_next) {
450                 if(is_at_operational(m->sml_desc->ad_type) ||
451                         ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE)) continue;
452                 if(ud->ignore) {
453                         for(up = ud->ignore; up; up = up->next)
454                                 if(m->sml_desc == up->attr) break;
455                         if(up) continue;
456                 }
457                 if(ud->attrs) {
458                         for(up = ud->attrs; up; up = up->next)
459                                 if(m->sml_desc == up->attr) break;
460                         if(!up) continue;
461                 }
462                 if((b = m->sml_values) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
463                         kp += sprintf(kp, "(%s=%s)", m->sml_desc->ad_cname.bv_val, b[i].bv_val);
464                 else if(ud->strict)
465                         kp += sprintf(kp, "(%s=*)", m->sml_desc->ad_cname.bv_val);
466         }
467
468         kp += sprintf(kp, ")");
469
470         Debug(LDAP_DEBUG_TRACE, "=> unique_modify %s\n", key, 0, 0);
471
472         nop.ors_filter = str2filter_x(&nop, key);
473         ber_str2bv(key, 0, 0, &nop.ors_filterstr);
474
475         cb.sc_response  = (slap_response*)count_attr_cb;
476         cb.sc_private   = &uq;
477         nop.o_callback  = &cb;
478         nop.o_tag       = LDAP_REQ_SEARCH;
479         nop.ors_scope   = LDAP_SCOPE_SUBTREE;
480         nop.ors_deref   = LDAP_DEREF_NEVER;
481         nop.ors_slimit  = SLAP_NO_LIMIT;
482         nop.ors_tlimit  = SLAP_NO_LIMIT;
483         nop.o_req_ndn   = ud->dn;
484         nop.o_ndn = op->o_bd->be_rootndn;
485
486         rc = nop.o_bd->be_search(&nop, &nrs);
487         ch_free( key );
488
489         if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
490                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
491                 send_ldap_error(op, rs, rc, "unique_modify search failed");
492                 return(rs->sr_err);
493         }
494
495         Debug(LDAP_DEBUG_TRACE, "=> unique_modify found %d records\n", uq.count, 0, 0);
496
497         if(uq.count) {
498                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
499                 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
500                         "some attributes not unique");
501                 return(rs->sr_err);
502         }
503
504         return(SLAP_CB_CONTINUE);
505
506 }
507
508
509 static int unique_modrdn(
510         Operation *op,
511         SlapReply *rs
512 )
513 {
514         Operation nop = *op;
515         SlapReply nrs = { REP_RESULT };
516         slap_callback cb = { NULL, (slap_response*)count_attr_cb, NULL, NULL };
517         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
518
519         char *key, *kp;
520         int i, rc, ks = 16;             /* a handful of extra bytes */
521         unique_attrs *up;
522         unique_counter uq = { 0 };
523         unique_data *ud = on->on_bi.bi_private;
524         LDAPRDN newrdn;
525
526         Debug(LDAP_DEBUG_TRACE, "==> unique_modrdn <%s> <%s>\n",
527                 op->o_req_dn.bv_val, op->orr_newrdn.bv_val, 0);
528
529         nop.o_bd = select_backend(&ud->dn, 0, 1);
530         if(nop.o_bd) {
531                 if (!nop.o_bd->be_search) {
532                         op->o_bd->bd_info = (BackendInfo *) on->on_info;
533                         send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
534                         "backend missing search function");
535                         return(rs->sr_err);
536                 }
537         } else {
538                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
539                 send_ldap_error(op, rs, LDAP_OTHER,
540                         "no known backend? this shouldn't be happening!");
541                 return(rs->sr_err);
542         }
543
544         if(ldap_bv2rdn_x(&op->oq_modrdn.rs_newrdn, &newrdn,
545                 (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx )) {
546                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
547                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
548                         "unknown type(s) used in RDN");
549                 return(rs->sr_err);
550         }
551         for(i = 0; newrdn[i]; i++) {
552                 AttributeDescription *ad = NULL;
553                 if ( slap_bv2ad( &newrdn[i]->la_attr, &ad, &rs->sr_text )) {
554                         ldap_rdnfree_x( newrdn, op->o_tmpmemctx );
555                         rs->sr_err = LDAP_INVALID_SYNTAX;
556                         send_ldap_result( op, rs );
557                         return(rs->sr_err);
558                 }
559                 newrdn[i]->la_private = ad;
560         }
561
562         for(i = 0; newrdn[i]; i++) {
563                 AttributeDescription *ad = newrdn[i]->la_private;
564                 if(ud->ignore) {
565                         for(up = ud->ignore; up; up = up->next)
566                                 if(ad == up->attr) break;
567                         if(up) continue;
568                 }
569                 if(ud->attrs) {
570                         for(up = ud->attrs; up; up = up->next)
571                                 if(ad == up->attr) break;
572                         if(!up) continue;
573                 }
574                 ks += newrdn[i]->la_value.bv_len + ad->ad_cname.bv_len + 3;
575         }
576
577         key = ch_malloc(ks);
578         kp = key + sprintf(key, "(|");
579
580         for(i = 0; newrdn[i]; i++) {
581                 AttributeDescription *ad = newrdn[i]->la_private;
582                 if(ud->ignore) {
583                         for(up = ud->ignore; up; up = up->next)
584                                 if(ad == up->attr) break;
585                         if(up) continue;
586                 }
587                 if(ud->attrs) {
588                         for(up = ud->attrs; up; up = up->next)
589                                 if(ad == up->attr) break;
590                         if(!up) continue;
591                 }
592                 kp += sprintf(kp, "(%s=%s)", ad->ad_cname.bv_val,
593                         newrdn[i]->la_value.bv_val);
594         }
595
596         kp += sprintf(kp, ")");
597
598
599         Debug(LDAP_DEBUG_TRACE, "=> unique_modrdn %s\n", key, 0, 0);
600
601         nop.ors_filter = str2filter_x(&nop, key);
602         ber_str2bv(key, 0, 0, &nop.ors_filterstr);
603
604         cb.sc_response  = (slap_response*)count_attr_cb;
605         cb.sc_private   = &uq;
606         nop.o_callback  = &cb;
607         nop.o_tag       = LDAP_REQ_SEARCH;
608         nop.ors_scope   = LDAP_SCOPE_SUBTREE;
609         nop.ors_deref   = LDAP_DEREF_NEVER;
610         nop.ors_slimit  = SLAP_NO_LIMIT;
611         nop.ors_tlimit  = SLAP_NO_LIMIT;
612         nop.o_req_ndn   = ud->dn;
613         nop.o_ndn = op->o_bd->be_rootndn;
614
615         rc = nop.o_bd->be_search(&nop, &nrs);
616         ch_free( key );
617         ldap_rdnfree_x( newrdn, op->o_tmpmemctx );
618
619         if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
620                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
621                 send_ldap_error(op, rs, rc, "unique_modrdn search failed");
622                 return(rs->sr_err);
623         }
624
625         Debug(LDAP_DEBUG_TRACE, "=> unique_modrdn found %d records\n", uq.count, 0, 0);
626
627         if(uq.count) {
628                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
629                 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
630                         "some attributes not unique");
631                 return(rs->sr_err);
632         }
633
634         return(SLAP_CB_CONTINUE);
635 }
636
637 /*
638 ** init_module is last so the symbols resolve "for free" --
639 ** it expects to be called automagically during dynamic module initialization
640 */
641
642 int unique_init() {
643
644         /* statically declared just after the #includes at top */
645         unique.on_bi.bi_type = "unique";
646         unique.on_bi.bi_db_init = unique_db_init;
647         unique.on_bi.bi_db_config = unique_config;
648         unique.on_bi.bi_db_open = unique_open;
649         unique.on_bi.bi_db_close = unique_close;
650         unique.on_bi.bi_op_add = unique_add;
651         unique.on_bi.bi_op_modify = unique_modify;
652         unique.on_bi.bi_op_modrdn = unique_modrdn;
653         unique.on_bi.bi_op_delete = NULL;
654
655         return(overlay_register(&unique));
656 }
657
658 #if SLAPD_OVER_UNIQUE == SLAPD_MOD_DYNAMIC && defined(PIC)
659 int init_module(int argc, char *argv[]) {
660         return unique_init();
661 }
662 #endif
663
664 #endif /* SLAPD_OVER_UNIQUE */