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