]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/unique.c
Reworked fix for ITS#3140 - add access parameter to backend_attribute
[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 + STRLENOF( "(=)" );
312                 else if(ud->strict)
313                         ks += a->a_desc->ad_cname.bv_len + STRLENOF( "(=*)" );
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         /* no attrs! */
354         nop.ors_attrs = slap_anlist_no_attrs;
355         nop.ors_attrsonly = 1;
356         nop.o_sync_slog_size = -1;
357
358         nop.o_req_ndn   = ud->dn;
359         nop.o_ndn = op->o_bd->be_rootndn;
360
361         rc = nop.o_bd->be_search(&nop, &nrs);
362         filter_free_x(&nop, nop.ors_filter);
363         ch_free( key );
364
365         if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
366                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
367                 send_ldap_error(op, rs, rc, "unique_add search failed");
368                 return(rs->sr_err);
369         }
370
371         Debug(LDAP_DEBUG_TRACE, "=> unique_add found %d records\n", uq.count, 0, 0);
372
373         if(uq.count) {
374                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
375                 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
376                         "some attributes not unique");
377                 return(rs->sr_err);
378         }
379
380         return(SLAP_CB_CONTINUE);
381 }
382
383
384 static int unique_modify(
385         Operation *op,
386         SlapReply *rs
387 )
388 {
389         Operation nop = *op;
390         SlapReply nrs = { REP_RESULT };
391         slap_callback cb = { NULL, (slap_response*)count_attr_cb, NULL, NULL };
392         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
393
394         BerVarray b = NULL;
395         Modifications *m;
396         char *key, *kp;
397         int i, rc, ks = 16;             /* a handful of extra bytes */
398         unique_attrs *up;
399         unique_counter uq = { 0 };
400         unique_data *ud = on->on_bi.bi_private;
401
402         Debug(LDAP_DEBUG_TRACE, "==> unique_modify <%s>\n", op->o_req_dn.bv_val, 0, 0);
403
404         nop.o_bd = select_backend(&ud->dn, 0, 1);
405         if(nop.o_bd) {
406                 if (!nop.o_bd->be_search) {
407                         op->o_bd->bd_info = (BackendInfo *) on->on_info;
408                         send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
409                         "backend missing search function");
410                         return(rs->sr_err);
411                 }
412         } else {
413                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
414                 send_ldap_error(op, rs, LDAP_OTHER,
415                         "no known backend? this shouldn't be happening!");
416                 return(rs->sr_err);
417         }
418
419 /*
420 ** count everything first;
421 ** allocate some memory;
422 ** write the search key;
423 **
424 */
425
426         if(!(m = op->orm_modlist)) {
427                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
428                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
429                         "unique_modify() got null op.orm_modlist");
430                 return(rs->sr_err);
431         } else for(; m; m = m->sml_next) {
432                 if(is_at_operational(m->sml_desc->ad_type) ||
433                         ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE)) continue;
434                 if(ud->ignore) {
435                         for(up = ud->ignore; up; up = up->next)
436                                 if(m->sml_desc == up->attr) break;
437                         if(up) continue;
438                 }
439                 if(ud->attrs) {
440                         for(up = ud->attrs; up; up = up->next)
441                                 if(m->sml_desc == up->attr) break;
442                         if(!up) continue;
443                 }
444                 if((b = m->sml_values) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
445                         ks += b[i].bv_len + m->sml_desc->ad_cname.bv_len + 3;
446                 else if(ud->strict)
447                         ks += m->sml_desc->ad_cname.bv_len + 4; /* (attr=*) */
448         }
449
450         key = ch_malloc(ks);
451
452         kp = key + sprintf(key, "(|");
453
454         for(m = op->orm_modlist; m; m = m->sml_next) {
455                 if(is_at_operational(m->sml_desc->ad_type) ||
456                         ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE)) continue;
457                 if(ud->ignore) {
458                         for(up = ud->ignore; up; up = up->next)
459                                 if(m->sml_desc == up->attr) break;
460                         if(up) continue;
461                 }
462                 if(ud->attrs) {
463                         for(up = ud->attrs; up; up = up->next)
464                                 if(m->sml_desc == up->attr) break;
465                         if(!up) continue;
466                 }
467                 if((b = m->sml_values) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
468                         kp += sprintf(kp, "(%s=%s)", m->sml_desc->ad_cname.bv_val, b[i].bv_val);
469                 else if(ud->strict)
470                         kp += sprintf(kp, "(%s=*)", m->sml_desc->ad_cname.bv_val);
471         }
472
473         kp += sprintf(kp, ")");
474
475         Debug(LDAP_DEBUG_TRACE, "=> unique_modify %s\n", key, 0, 0);
476
477         nop.ors_filter = str2filter_x(&nop, key);
478         ber_str2bv(key, 0, 0, &nop.ors_filterstr);
479
480         cb.sc_response  = (slap_response*)count_attr_cb;
481         cb.sc_private   = &uq;
482         nop.o_callback  = &cb;
483         nop.o_tag       = LDAP_REQ_SEARCH;
484         nop.ors_scope   = LDAP_SCOPE_SUBTREE;
485         nop.ors_deref   = LDAP_DEREF_NEVER;
486         nop.ors_slimit  = SLAP_NO_LIMIT;
487         nop.ors_tlimit  = SLAP_NO_LIMIT;
488         nop.o_req_ndn   = ud->dn;
489         nop.o_ndn = op->o_bd->be_rootndn;
490
491         rc = nop.o_bd->be_search(&nop, &nrs);
492         ch_free( key );
493
494         if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
495                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
496                 send_ldap_error(op, rs, rc, "unique_modify search failed");
497                 return(rs->sr_err);
498         }
499
500         Debug(LDAP_DEBUG_TRACE, "=> unique_modify found %d records\n", uq.count, 0, 0);
501
502         if(uq.count) {
503                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
504                 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
505                         "some attributes not unique");
506                 return(rs->sr_err);
507         }
508
509         return(SLAP_CB_CONTINUE);
510
511 }
512
513
514 static int unique_modrdn(
515         Operation *op,
516         SlapReply *rs
517 )
518 {
519         Operation nop = *op;
520         SlapReply nrs = { REP_RESULT };
521         slap_callback cb = { NULL, (slap_response*)count_attr_cb, NULL, NULL };
522         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
523
524         char *key, *kp;
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  = SLAP_NO_LIMIT;
616         nop.ors_tlimit  = SLAP_NO_LIMIT;
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 */