]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/accesslog.c
6106bc00cb8c85782a807a30b448fe206dcc131f
[openldap] / servers / slapd / overlays / accesslog.c
1 /* accesslog.c - log operations for audit/history purposes */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2005-2007 The OpenLDAP Foundation.
6  * Portions copyright 2004-2005 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 Howard Chu for inclusion in
19  * OpenLDAP Software.
20  */
21
22 #include "portable.h"
23
24 #ifdef SLAPD_OVER_ACCESSLOG
25
26 #include <stdio.h>
27
28 #include <ac/string.h>
29 #include <ac/ctype.h>
30
31 #include "slap.h"
32 #include "config.h"
33 #include "lutil.h"
34 #include "ldap_rq.h"
35
36 #define LOG_OP_ADD      0x001
37 #define LOG_OP_DELETE   0x002
38 #define LOG_OP_MODIFY   0x004
39 #define LOG_OP_MODRDN   0x008
40 #define LOG_OP_COMPARE  0x010
41 #define LOG_OP_SEARCH   0x020
42 #define LOG_OP_BIND     0x040
43 #define LOG_OP_UNBIND   0x080
44 #define LOG_OP_ABANDON  0x100
45 #define LOG_OP_EXTENDED 0x200
46 #define LOG_OP_UNKNOWN  0x400
47
48 #define LOG_OP_WRITES   (LOG_OP_ADD|LOG_OP_DELETE|LOG_OP_MODIFY|LOG_OP_MODRDN)
49 #define LOG_OP_READS    (LOG_OP_COMPARE|LOG_OP_SEARCH)
50 #define LOG_OP_SESSION  (LOG_OP_BIND|LOG_OP_UNBIND|LOG_OP_ABANDON)
51 #define LOG_OP_ALL              (LOG_OP_READS|LOG_OP_WRITES|LOG_OP_SESSION| \
52         LOG_OP_EXTENDED|LOG_OP_UNKNOWN)
53
54 typedef struct log_attr {
55         struct log_attr *next;
56         AttributeDescription *attr;
57 } log_attr;
58
59 typedef struct log_info {
60         BackendDB *li_db;
61         struct berval li_db_suffix;
62         slap_mask_t li_ops;
63         int li_age;
64         int li_cycle;
65         struct re_s *li_task;
66         Filter *li_oldf;
67         Entry *li_old;
68         log_attr *li_oldattrs;
69         int li_success;
70         ldap_pvt_thread_rmutex_t li_op_rmutex;
71         ldap_pvt_thread_mutex_t li_log_mutex;
72 } log_info;
73
74 static ConfigDriver log_cf_gen;
75
76 enum {
77         LOG_DB = 1,
78         LOG_OPS,
79         LOG_PURGE,
80         LOG_SUCCESS,
81         LOG_OLD,
82         LOG_OLDATTR
83 };
84
85 static ConfigTable log_cfats[] = {
86         { "logdb", "suffix", 2, 2, 0, ARG_DN|ARG_MAGIC|LOG_DB,
87                 log_cf_gen, "( OLcfgOvAt:4.1 NAME 'olcAccessLogDB' "
88                         "DESC 'Suffix of database for log content' "
89                         "SUP distinguishedName SINGLE-VALUE )", NULL, NULL },
90         { "logops", "op|writes|reads|session|all", 2, 0, 0,
91                 ARG_MAGIC|LOG_OPS,
92                 log_cf_gen, "( OLcfgOvAt:4.2 NAME 'olcAccessLogOps' "
93                         "DESC 'Operation types to log' "
94                         "EQUALITY caseIgnoreMatch "
95                         "SYNTAX OMsDirectoryString )", NULL, NULL },
96         { "logpurge", "age> <interval", 3, 3, 0, ARG_MAGIC|LOG_PURGE,
97                 log_cf_gen, "( OLcfgOvAt:4.3 NAME 'olcAccessLogPurge' "
98                         "DESC 'Log cleanup parameters' "
99                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
100         { "logsuccess", NULL, 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|LOG_SUCCESS,
101                 log_cf_gen, "( OLcfgOvAt:4.4 NAME 'olcAccessLogSuccess' "
102                         "DESC 'Log successful ops only' "
103                         "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
104         { "logold", "filter", 2, 2, 0, ARG_MAGIC|LOG_OLD,
105                 log_cf_gen, "( OLcfgOvAt:4.5 NAME 'olcAccessLogOld' "
106                         "DESC 'Log old values when modifying entries matching the filter' "
107                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
108         { "logoldattr", "attrs", 2, 0, 0, ARG_MAGIC|LOG_OLDATTR,
109                 log_cf_gen, "( OLcfgOvAt:4.6 NAME 'olcAccessLogOldAttr' "
110                         "DESC 'Log old values of these attributes even if unmodified' "
111                         "EQUALITY caseIgnoreMatch "
112                         "SYNTAX OMsDirectoryString )", NULL, NULL },
113         { NULL }
114 };
115
116 static ConfigOCs log_cfocs[] = {
117         { "( OLcfgOvOc:4.1 "
118                 "NAME 'olcAccessLogConfig' "
119                 "DESC 'Access log configuration' "
120                 "SUP olcOverlayConfig "
121                 "MUST olcAccessLogDB "
122                 "MAY ( olcAccessLogOps $ olcAccessLogPurge $ olcAccessLogSuccess $ "
123                         "olcAccessLogOld $ olcAccessLogOldAttr ) )",
124                         Cft_Overlay, log_cfats },
125         { NULL }
126 };
127
128 static slap_verbmasks logops[] = {
129         { BER_BVC("all"),               LOG_OP_ALL },
130         { BER_BVC("writes"),    LOG_OP_WRITES },
131         { BER_BVC("session"),   LOG_OP_SESSION },
132         { BER_BVC("reads"),             LOG_OP_READS },
133         { BER_BVC("add"),               LOG_OP_ADD },
134         { BER_BVC("delete"),    LOG_OP_DELETE },
135         { BER_BVC("modify"),    LOG_OP_MODIFY },
136         { BER_BVC("modrdn"),    LOG_OP_MODRDN },
137         { BER_BVC("compare"),   LOG_OP_COMPARE },
138         { BER_BVC("search"),    LOG_OP_SEARCH },
139         { BER_BVC("bind"),              LOG_OP_BIND },
140         { BER_BVC("unbind"),    LOG_OP_UNBIND },
141         { BER_BVC("abandon"),   LOG_OP_ABANDON },
142         { BER_BVC("extended"),  LOG_OP_EXTENDED },
143         { BER_BVC("unknown"),   LOG_OP_UNKNOWN },
144         { BER_BVNULL, 0 }
145 };
146
147 /* Start with "add" in logops */
148 #define EN_OFFSET       4
149
150 enum {
151         LOG_EN_ADD = 0,
152         LOG_EN_DELETE,
153         LOG_EN_MODIFY,
154         LOG_EN_MODRDN,
155         LOG_EN_COMPARE,
156         LOG_EN_SEARCH,
157         LOG_EN_BIND,
158         LOG_EN_UNBIND,
159         LOG_EN_ABANDON,
160         LOG_EN_EXTENDED,
161         LOG_EN_UNKNOWN,
162         LOG_EN__COUNT
163 };
164
165 static ObjectClass *log_ocs[LOG_EN__COUNT], *log_container;
166
167 #define LOG_SCHEMA_ROOT "1.3.6.1.4.1.4203.666.11.5"
168
169 #define LOG_SCHEMA_AT LOG_SCHEMA_ROOT ".1"
170 #define LOG_SCHEMA_OC LOG_SCHEMA_ROOT ".2"
171
172 static AttributeDescription *ad_reqDN, *ad_reqStart, *ad_reqEnd, *ad_reqType,
173         *ad_reqSession, *ad_reqResult, *ad_reqAuthzID, *ad_reqControls,
174         *ad_reqRespControls, *ad_reqMethod, *ad_reqAssertion, *ad_reqNewRDN,
175         *ad_reqNewSuperior, *ad_reqDeleteOldRDN, *ad_reqMod,
176         *ad_reqScope, *ad_reqFilter, *ad_reqAttr, *ad_reqEntries,
177         *ad_reqSizeLimit, *ad_reqTimeLimit, *ad_reqAttrsOnly, *ad_reqData,
178         *ad_reqId, *ad_reqMessage, *ad_reqVersion, *ad_reqDerefAliases,
179         *ad_reqReferral, *ad_reqOld, *ad_auditContext;
180
181 static struct {
182         char *at;
183         AttributeDescription **ad;
184 } lattrs[] = {
185         { "( " LOG_SCHEMA_AT ".1 NAME 'reqDN' "
186                 "DESC 'Target DN of request' "
187                 "EQUALITY distinguishedNameMatch "
188                 "SYNTAX OMsDN "
189                 "SINGLE-VALUE )", &ad_reqDN },
190         { "( " LOG_SCHEMA_AT ".2 NAME 'reqStart' "
191                 "DESC 'Start time of request' "
192                 "EQUALITY generalizedTimeMatch "
193                 "ORDERING generalizedTimeOrderingMatch "
194                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
195                 "SINGLE-VALUE )", &ad_reqStart },
196         { "( " LOG_SCHEMA_AT ".3 NAME 'reqEnd' "
197                 "DESC 'End time of request' "
198                 "EQUALITY generalizedTimeMatch "
199                 "ORDERING generalizedTimeOrderingMatch "
200                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
201                 "SINGLE-VALUE )", &ad_reqEnd },
202         { "( " LOG_SCHEMA_AT ".4 NAME 'reqType' "
203                 "DESC 'Type of request' "
204                 "EQUALITY caseIgnoreMatch "
205                 "SYNTAX OMsDirectoryString "
206                 "SINGLE-VALUE )", &ad_reqType },
207         { "( " LOG_SCHEMA_AT ".5 NAME 'reqSession' "
208                 "DESC 'Session ID of request' "
209                 "EQUALITY caseIgnoreMatch "
210                 "SYNTAX OMsDirectoryString "
211                 "SINGLE-VALUE )", &ad_reqSession },
212         { "( " LOG_SCHEMA_AT ".6 NAME 'reqAuthzID' "
213                 "DESC 'Authorization ID of requestor' "
214                 "EQUALITY distinguishedNameMatch "
215                 "SYNTAX OMsDN "
216                 "SINGLE-VALUE )", &ad_reqAuthzID },
217         { "( " LOG_SCHEMA_AT ".7 NAME 'reqResult' "
218                 "DESC 'Result code of request' "
219                 "EQUALITY integerMatch "
220                 "ORDERING integerOrderingMatch "
221                 "SYNTAX OMsInteger "
222                 "SINGLE-VALUE )", &ad_reqResult },
223         { "( " LOG_SCHEMA_AT ".8 NAME 'reqMessage' "
224                 "DESC 'Error text of request' "
225                 "EQUALITY caseIgnoreMatch "
226                 "SUBSTR caseIgnoreSubstringsMatch "
227                 "SYNTAX OMsDirectoryString "
228                 "SINGLE-VALUE )", &ad_reqMessage },
229         { "( " LOG_SCHEMA_AT ".9 NAME 'reqReferral' "
230                 "DESC 'Referrals returned for request' "
231                 "SUP labeledURI )", &ad_reqReferral },
232         { "( " LOG_SCHEMA_AT ".10 NAME 'reqControls' "
233                 "DESC 'Request controls' "
234                 "SYNTAX OMsOctetString "
235                 "X-ORDERED 'VALUES' )", &ad_reqControls },
236         { "( " LOG_SCHEMA_AT ".11 NAME 'reqRespControls' "
237                 "DESC 'Response controls of request' "
238                 "SYNTAX OMsOctetString "
239                 "X-ORDERED 'VALUES' )", &ad_reqRespControls },
240         { "( " LOG_SCHEMA_AT ".12 NAME 'reqId' "
241                 "DESC 'ID of Request to Abandon' "
242                 "EQUALITY integerMatch "
243                 "ORDERING integerOrderingMatch "
244                 "SYNTAX OMsInteger "
245                 "SINGLE-VALUE )", &ad_reqId },
246         { "( " LOG_SCHEMA_AT ".13 NAME 'reqVersion' "
247                 "DESC 'Protocol version of Bind request' "
248                 "EQUALITY integerMatch "
249                 "ORDERING integerOrderingMatch "
250                 "SYNTAX OMsInteger "
251                 "SINGLE-VALUE )", &ad_reqVersion },
252         { "( " LOG_SCHEMA_AT ".14 NAME 'reqMethod' "
253                 "DESC 'Bind method of request' "
254                 "EQUALITY caseIgnoreMatch "
255                 "SYNTAX OMsDirectoryString "
256                 "SINGLE-VALUE )", &ad_reqMethod },
257         { "( " LOG_SCHEMA_AT ".15 NAME 'reqAssertion' "
258                 "DESC 'Compare Assertion of request' "
259                 "SYNTAX OMsDirectoryString "
260                 "SINGLE-VALUE )", &ad_reqAssertion },
261         { "( " LOG_SCHEMA_AT ".16 NAME 'reqMod' "
262                 "DESC 'Modifications of request' "
263                 "EQUALITY octetStringMatch "
264                 "SUBSTR octetStringSubstringsMatch "
265                 "SYNTAX OMsOctetString )", &ad_reqMod },
266         { "( " LOG_SCHEMA_AT ".17 NAME 'reqOld' "
267                 "DESC 'Old values of entry before request completed' "
268                 "EQUALITY octetStringMatch "
269                 "SUBSTR octetStringSubstringsMatch "
270                 "SYNTAX OMsOctetString )", &ad_reqOld },
271         { "( " LOG_SCHEMA_AT ".18 NAME 'reqNewRDN' "
272                 "DESC 'New RDN of request' "
273                 "EQUALITY distinguishedNameMatch "
274                 "SYNTAX OMsDN "
275                 "SINGLE-VALUE )", &ad_reqNewRDN },
276         { "( " LOG_SCHEMA_AT ".19 NAME 'reqDeleteOldRDN' "
277                 "DESC 'Delete old RDN' "
278                 "EQUALITY booleanMatch "
279                 "SYNTAX OMsBoolean "
280                 "SINGLE-VALUE )", &ad_reqDeleteOldRDN },
281         { "( " LOG_SCHEMA_AT ".20 NAME 'reqNewSuperior' "
282                 "DESC 'New superior DN of request' "
283                 "EQUALITY distinguishedNameMatch "
284                 "SYNTAX OMsDN "
285                 "SINGLE-VALUE )", &ad_reqNewSuperior },
286         { "( " LOG_SCHEMA_AT ".21 NAME 'reqScope' "
287                 "DESC 'Scope of request' "
288                 "EQUALITY caseIgnoreMatch "
289                 "SYNTAX OMsDirectoryString "
290                 "SINGLE-VALUE )", &ad_reqScope },
291         { "( " LOG_SCHEMA_AT ".22 NAME 'reqDerefAliases' "
292                 "DESC 'Disposition of Aliases in request' "
293                 "EQUALITY caseIgnoreMatch "
294                 "SYNTAX OMsDirectoryString "
295                 "SINGLE-VALUE )", &ad_reqDerefAliases },
296         { "( " LOG_SCHEMA_AT ".23 NAME 'reqAttrsOnly' "
297                 "DESC 'Attributes and values of request' "
298                 "EQUALITY booleanMatch "
299                 "SYNTAX OMsBoolean "
300                 "SINGLE-VALUE )", &ad_reqAttrsOnly },
301         { "( " LOG_SCHEMA_AT ".24 NAME 'reqFilter' "
302                 "DESC 'Filter of request' "
303                 "EQUALITY caseIgnoreMatch "
304                 "SUBSTR caseIgnoreSubstringsMatch "
305                 "SYNTAX OMsDirectoryString "
306                 "SINGLE-VALUE )", &ad_reqFilter },
307         { "( " LOG_SCHEMA_AT ".25 NAME 'reqAttr' "
308                 "DESC 'Attributes of request' "
309                 "EQUALITY caseIgnoreMatch "
310                 "SYNTAX OMsDirectoryString )", &ad_reqAttr },
311         { "( " LOG_SCHEMA_AT ".26 NAME 'reqSizeLimit' "
312                 "DESC 'Size limit of request' "
313                 "EQUALITY integerMatch "
314                 "ORDERING integerOrderingMatch "
315                 "SYNTAX OMsInteger "
316                 "SINGLE-VALUE )", &ad_reqSizeLimit },
317         { "( " LOG_SCHEMA_AT ".27 NAME 'reqTimeLimit' "
318                 "DESC 'Time limit of request' "
319                 "EQUALITY integerMatch "
320                 "ORDERING integerOrderingMatch "
321                 "SYNTAX OMsInteger "
322                 "SINGLE-VALUE )", &ad_reqTimeLimit },
323         { "( " LOG_SCHEMA_AT ".28 NAME 'reqEntries' "
324                 "DESC 'Number of entries returned' "
325                 "EQUALITY integerMatch "
326                 "ORDERING integerOrderingMatch "
327                 "SYNTAX OMsInteger "
328                 "SINGLE-VALUE )", &ad_reqEntries },
329         { "( " LOG_SCHEMA_AT ".29 NAME 'reqData' "
330                 "DESC 'Data of extended request' "
331                 "EQUALITY octetStringMatch "
332                 "SUBSTR octetStringSubstringsMatch "
333                 "SYNTAX OMsOctetString "
334                 "SINGLE-VALUE )", &ad_reqData },
335
336         /*
337          * from <draft-chu-ldap-logschema-01.txt>:
338          *
339
340    ( LOG_SCHEMA_AT .30 NAME 'auditContext'
341    DESC 'DN of auditContainer'
342    EQUALITY distinguishedNameMatch
343    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
344    SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )
345
346          * - removed EQUALITY matchingRule
347          * - changed directoryOperation in dSAOperation
348          */
349         { "( " LOG_SCHEMA_AT ".30 NAME 'auditContext' "
350                 "DESC 'DN of auditContainer' "
351                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
352                 "SINGLE-VALUE "
353                 "NO-USER-MODIFICATION "
354                 "USAGE dSAOperation )", &ad_auditContext },
355         { NULL, NULL }
356 };
357
358 static struct {
359         char *ot;
360         ObjectClass **oc;
361 } locs[] = {
362         { "( " LOG_SCHEMA_OC ".0 NAME 'auditContainer' "
363                 "DESC 'AuditLog container' "
364                 "SUP top STRUCTURAL "
365                 "MAY ( cn $ reqStart $ reqEnd ) )", &log_container },
366         { "( " LOG_SCHEMA_OC ".1 NAME 'auditObject' "
367                 "DESC 'OpenLDAP request auditing' "
368                 "SUP top STRUCTURAL "
369                 "MUST ( reqStart $ reqType $ reqSession ) "
370                 "MAY ( reqDN $ reqAuthzID $ reqControls $ reqRespControls $ reqEnd $ "
371                         "reqResult $ reqMessage $ reqReferral ) )",
372                                 &log_ocs[LOG_EN_UNBIND] },
373         { "( " LOG_SCHEMA_OC ".2 NAME 'auditReadObject' "
374                 "DESC 'OpenLDAP read request record' "
375                 "SUP auditObject STRUCTURAL )", NULL },
376         { "( " LOG_SCHEMA_OC ".3 NAME 'auditWriteObject' "
377                 "DESC 'OpenLDAP write request record' "
378                 "SUP auditObject STRUCTURAL )", NULL },
379         { "( " LOG_SCHEMA_OC ".4 NAME 'auditAbandon' "
380                 "DESC 'Abandon operation' "
381                 "SUP auditObject STRUCTURAL "
382                 "MUST reqId )", &log_ocs[LOG_EN_ABANDON] },
383         { "( " LOG_SCHEMA_OC ".5 NAME 'auditAdd' "
384                 "DESC 'Add operation' "
385                 "SUP auditWriteObject STRUCTURAL "
386                 "MUST reqMod )", &log_ocs[LOG_EN_ADD] },
387         { "( " LOG_SCHEMA_OC ".6 NAME 'auditBind' "
388                 "DESC 'Bind operation' "
389                 "SUP auditObject STRUCTURAL "
390                 "MUST ( reqVersion $ reqMethod ) )", &log_ocs[LOG_EN_BIND] },
391         { "( " LOG_SCHEMA_OC ".7 NAME 'auditCompare' "
392                 "DESC 'Compare operation' "
393                 "SUP auditReadObject STRUCTURAL "
394                 "MUST reqAssertion )", &log_ocs[LOG_EN_COMPARE] },
395         { "( " LOG_SCHEMA_OC ".8 NAME 'auditDelete' "
396                 "DESC 'Delete operation' "
397                 "SUP auditWriteObject STRUCTURAL "
398                 "MAY reqOld )", &log_ocs[LOG_EN_DELETE] },
399         { "( " LOG_SCHEMA_OC ".9 NAME 'auditModify' "
400                 "DESC 'Modify operation' "
401                 "SUP auditWriteObject STRUCTURAL "
402                 "MAY reqOld MUST reqMod )", &log_ocs[LOG_EN_MODIFY] },
403         { "( " LOG_SCHEMA_OC ".10 NAME 'auditModRDN' "
404                 "DESC 'ModRDN operation' "
405                 "SUP auditWriteObject STRUCTURAL "
406                 "MUST ( reqNewRDN $ reqDeleteOldRDN ) "
407                 "MAY ( reqNewSuperior $ reqMod $ reqOld ) )", &log_ocs[LOG_EN_MODRDN] },
408         { "( " LOG_SCHEMA_OC ".11 NAME 'auditSearch' "
409                 "DESC 'Search operation' "
410                 "SUP auditReadObject STRUCTURAL "
411                 "MUST ( reqScope $ reqDerefAliases $ reqAttrsonly ) "
412                 "MAY ( reqFilter $ reqAttr $ reqEntries $ reqSizeLimit $ "
413                         "reqTimeLimit ) )", &log_ocs[LOG_EN_SEARCH] },
414         { "( " LOG_SCHEMA_OC ".12 NAME 'auditExtended' "
415                 "DESC 'Extended operation' "
416                 "SUP auditObject STRUCTURAL "
417                 "MAY reqData )", &log_ocs[LOG_EN_EXTENDED] },
418         { NULL, NULL }
419 };
420
421 #define RDNEQ   "reqStart="
422
423 /* Our time intervals are of the form [ddd+]hh:mm[:ss]
424  * If a field is present, it must be two digits. (Except for
425  * days, which can be arbitrary width.)
426  */
427 static int
428 log_age_parse(char *agestr)
429 {
430         int t1, t2;
431         int gotdays = 0;
432         char *endptr;
433
434         t1 = strtol( agestr, &endptr, 10 );
435         /* Is there a days delimiter? */
436         if ( *endptr == '+' ) {
437                 /* 32 bit time only covers about 68 years */
438                 if ( t1 < 0 || t1 > 25000 )
439                         return -1;
440                 t1 *= 24;
441                 gotdays = 1;
442                 agestr = endptr + 1;
443         } else {
444                 if ( agestr[2] != ':' ) {
445                         /* No valid delimiter found, fail */
446                         return -1;
447                 }
448                 t1 *= 60;
449                 agestr += 3;
450         }
451
452         t2 = atoi( agestr );
453         t1 += t2;
454
455         if ( agestr[2] ) {
456                 /* if there's a delimiter, it can only be a colon */
457                 if ( agestr[2] != ':' )
458                         return -1;
459         } else {
460                 /* If we're at the end of the string, and we started with days,
461                  * fail because we expected to find minutes too.
462                  */
463                 return gotdays ? -1 : t1 * 60;
464         }
465
466         agestr += 3;
467         t2 = atoi( agestr );
468
469         /* last field can only be seconds */
470         if ( agestr[2] && ( agestr[2] != ':' || !gotdays ))
471                 return -1;
472         t1 *= 60;
473         t1 += t2;
474
475         if ( agestr[2] ) {
476                 agestr += 3;
477                 if ( agestr[2] )
478                         return -1;
479                 t1 *= 60;
480                 t1 += atoi( agestr );
481         } else if ( gotdays ) {
482                 /* only got days+hh:mm */
483                 t1 *= 60;
484         }
485         return t1;
486 }
487
488 static void
489 log_age_unparse( int age, struct berval *agebv )
490 {
491         int dd, hh, mm, ss;
492         char *ptr;
493
494         ss = age % 60;
495         age /= 60;
496         mm = age % 60;
497         age /= 60;
498         hh = age % 24;
499         age /= 24;
500         dd = age;
501
502         ptr = agebv->bv_val;
503
504         if ( dd ) 
505                 ptr += sprintf( ptr, "%d+", dd );
506         ptr += sprintf( ptr, "%02d:%02d", hh, mm );
507         if ( ss )
508                 ptr += sprintf( ptr, ":%02d", ss );
509
510         agebv->bv_len = ptr - agebv->bv_val;
511 }
512
513 static slap_callback nullsc = { NULL, NULL, NULL, NULL };
514
515 #define PURGE_INCREMENT 100
516
517 typedef struct purge_data {
518         int slots;
519         int used;
520         BerVarray dn;
521         BerVarray ndn;
522         struct berval csn;      /* an arbitrary old CSN */
523 } purge_data;
524
525 static int
526 log_old_lookup( Operation *op, SlapReply *rs )
527 {
528         purge_data *pd = op->o_callback->sc_private;
529
530         if ( rs->sr_type != REP_SEARCH) return 0;
531
532         if ( slapd_shutdown ) return 0;
533
534         /* Remember old CSN */
535         if ( pd->csn.bv_val[0] == '\0' ) {
536                 Attribute *a = attr_find( rs->sr_entry->e_attrs,
537                         slap_schema.si_ad_entryCSN );
538                 if ( a ) {
539                         int len = a->a_vals[0].bv_len;
540                         if ( len > pd->csn.bv_len )
541                                 len = pd->csn.bv_len;
542                         AC_MEMCPY( pd->csn.bv_val, a->a_vals[0].bv_val, len );
543                         pd->csn.bv_len = len;
544                 }
545         }
546         if ( pd->used >= pd->slots ) {
547                 pd->slots += PURGE_INCREMENT;
548                 pd->dn = ch_realloc( pd->dn, pd->slots * sizeof( struct berval ));
549                 pd->ndn = ch_realloc( pd->ndn, pd->slots * sizeof( struct berval ));
550         }
551         ber_dupbv( &pd->dn[pd->used], &rs->sr_entry->e_name );
552         ber_dupbv( &pd->ndn[pd->used], &rs->sr_entry->e_nname );
553         pd->used++;
554         return 0;
555 }
556
557 /* Periodically search for old entries in the log database and delete them */
558 static void *
559 accesslog_purge( void *ctx, void *arg )
560 {
561         struct re_s *rtask = arg;
562         struct log_info *li = rtask->arg;
563
564         Connection conn = {0};
565         OperationBuffer opbuf;
566         Operation *op;
567         SlapReply rs = {REP_RESULT};
568         slap_callback cb = { NULL, log_old_lookup, NULL, NULL };
569         Filter f;
570         AttributeAssertion ava = {0};
571         purge_data pd = {0};
572         char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
573         char csnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
574         time_t old = slap_get_time();
575
576         connection_fake_init( &conn, &opbuf, ctx );
577         op = &opbuf.ob_op;
578
579         f.f_choice = LDAP_FILTER_LE;
580         f.f_ava = &ava;
581         f.f_next = NULL;
582
583         ava.aa_desc = ad_reqStart;
584         ava.aa_value.bv_val = timebuf;
585         ava.aa_value.bv_len = sizeof(timebuf);
586
587         old -= li->li_age;
588         slap_timestamp( &old, &ava.aa_value );
589
590         op->o_tag = LDAP_REQ_SEARCH;
591         op->o_bd = li->li_db;
592         op->o_dn = li->li_db->be_rootdn;
593         op->o_ndn = li->li_db->be_rootndn;
594         op->o_req_dn = li->li_db->be_suffix[0];
595         op->o_req_ndn = li->li_db->be_nsuffix[0];
596         op->o_callback = &cb;
597         op->ors_scope = LDAP_SCOPE_ONELEVEL;
598         op->ors_deref = LDAP_DEREF_NEVER;
599         op->ors_tlimit = SLAP_NO_LIMIT;
600         op->ors_slimit = SLAP_NO_LIMIT;
601         op->ors_filter = &f;
602         filter2bv_x( op, &f, &op->ors_filterstr );
603         op->ors_attrs = slap_anlist_no_attrs;
604         op->ors_attrsonly = 1;
605         
606         pd.csn.bv_len = sizeof( csnbuf );
607         pd.csn.bv_val = csnbuf;
608         csnbuf[0] = '\0';
609         cb.sc_private = &pd;
610
611         op->o_bd->be_search( op, &rs );
612         op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
613
614         if ( pd.used ) {
615                 int i;
616
617                 op->o_tag = LDAP_REQ_DELETE;
618                 op->o_callback = &nullsc;
619                 op->o_csn = pd.csn;
620
621                 for (i=0; i<pd.used; i++) {
622                         op->o_req_dn = pd.dn[i];
623                         op->o_req_ndn = pd.ndn[i];
624                         if ( !slapd_shutdown )
625                                 op->o_bd->be_delete( op, &rs );
626                         ch_free( pd.ndn[i].bv_val );
627                         ch_free( pd.dn[i].bv_val );
628                 }
629                 ch_free( pd.ndn );
630                 ch_free( pd.dn );
631         }
632
633         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
634         ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
635         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
636
637         return NULL;
638 }
639
640 static int
641 log_cf_gen(ConfigArgs *c)
642 {
643         slap_overinst *on = (slap_overinst *)c->bi;
644         struct log_info *li = on->on_bi.bi_private;
645         int rc = 0;
646         slap_mask_t tmask = 0;
647         char agebuf[2*STRLENOF("ddddd+hh:mm:ss  ")];
648         struct berval agebv, cyclebv;
649
650         switch( c->op ) {
651         case SLAP_CONFIG_EMIT:
652                 switch( c->type ) {
653                 case LOG_DB:
654                         if ( !BER_BVISEMPTY( &li->li_db_suffix )) {
655                                 value_add_one( &c->rvalue_vals, &li->li_db_suffix );
656                                 value_add_one( &c->rvalue_nvals, &li->li_db_suffix );
657                         } else if ( li->li_db ) {
658                                 value_add_one( &c->rvalue_vals, li->li_db->be_suffix );
659                                 value_add_one( &c->rvalue_nvals, li->li_db->be_nsuffix );
660                         } else {
661                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
662                                         "accesslog: \"logdb <suffix>\" must be specified" );
663                                 Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
664                                         c->log, c->cr_msg, c->value_dn.bv_val );
665                                 rc = 1;
666                                 break;
667                         }
668                         break;
669                 case LOG_OPS:
670                         rc = mask_to_verbs( logops, li->li_ops, &c->rvalue_vals );
671                         break;
672                 case LOG_PURGE:
673                         if ( !li->li_age ) {
674                                 rc = 1;
675                                 break;
676                         }
677                         agebv.bv_val = agebuf;
678                         log_age_unparse( li->li_age, &agebv );
679                         agebv.bv_val[agebv.bv_len] = ' ';
680                         agebv.bv_len++;
681                         cyclebv.bv_val = agebv.bv_val + agebv.bv_len;
682                         log_age_unparse( li->li_cycle, &cyclebv );
683                         agebv.bv_len += cyclebv.bv_len;
684                         value_add_one( &c->rvalue_vals, &agebv );
685                         break;
686                 case LOG_SUCCESS:
687                         if ( li->li_success )
688                                 c->value_int = li->li_success;
689                         else
690                                 rc = 1;
691                         break;
692                 case LOG_OLD:
693                         if ( li->li_oldf ) {
694                                 filter2bv( li->li_oldf, &agebv );
695                                 ber_bvarray_add( &c->rvalue_vals, &agebv );
696                         }
697                         else
698                                 rc = 1;
699                         break;
700                 case LOG_OLDATTR:
701                         if ( li->li_oldattrs ) {
702                                 log_attr *la;
703
704                                 for ( la = li->li_oldattrs; la; la=la->next )
705                                         value_add_one( &c->rvalue_vals, &la->attr->ad_cname );
706                         }
707                         else
708                                 rc = 1;
709                         break;
710                 }
711                 break;
712         case LDAP_MOD_DELETE:
713                 switch( c->type ) {
714                 case LOG_DB:
715                         /* noop. this should always be a valid backend. */
716                         break;
717                 case LOG_OPS:
718                         if ( c->valx < 0 ) {
719                                 li->li_ops = 0;
720                         } else {
721                                 rc = verbs_to_mask( 1, &c->line, logops, &tmask );
722                                 if ( rc == 0 )
723                                         li->li_ops &= ~tmask;
724                         }
725                         break;
726                 case LOG_PURGE:
727                         if ( li->li_task ) {
728                                 struct re_s *re = li->li_task;
729                                 li->li_task = NULL;
730                                 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ))
731                                         ldap_pvt_runqueue_stoptask( &slapd_rq, re );
732                                 ldap_pvt_runqueue_remove( &slapd_rq, re );
733                         }
734                         li->li_age = 0;
735                         li->li_cycle = 0;
736                         break;
737                 case LOG_SUCCESS:
738                         li->li_success = 0;
739                         break;
740                 case LOG_OLD:
741                         if ( li->li_oldf ) {
742                                 filter_free( li->li_oldf );
743                                 li->li_oldf = NULL;
744                         }
745                         break;
746                 case LOG_OLDATTR:
747                         if ( c->valx < 0 ) {
748                                 log_attr *la, *ln;
749
750                                 for ( la = li->li_oldattrs; la; la = ln ) {
751                                         ln = la->next;
752                                         ch_free( la );
753                                 }
754                         } else {
755                                 log_attr *la = NULL, **lp;
756                                 int i;
757
758                                 for ( lp = &li->li_oldattrs, i=0; i < c->valx; i++ ) {
759                                         la = *lp;
760                                         lp = &la->next;
761                                 }
762                                 *lp = la->next;
763                                 ch_free( la );
764                         }
765                         break;
766                 }
767                 break;
768         default:
769                 switch( c->type ) {
770                 case LOG_DB:
771                         if ( CONFIG_ONLINE_ADD( c )) {
772                                 li->li_db = select_backend( &c->value_ndn, 0 );
773                                 if ( !li->li_db ) {
774                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
775                                                 "<%s> no matching backend found for suffix",
776                                                 c->argv[0] );
777                                         Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
778                                                 c->log, c->cr_msg, c->value_dn.bv_val );
779                                         rc = 1;
780                                 }
781                                 ch_free( c->value_ndn.bv_val );
782                         } else {
783                                 li->li_db_suffix = c->value_ndn;
784                         }
785                         ch_free( c->value_dn.bv_val );
786                         break;
787                 case LOG_OPS:
788                         rc = verbs_to_mask( c->argc, c->argv, logops, &tmask );
789                         if ( rc == 0 )
790                                 li->li_ops |= tmask;
791                         break;
792                 case LOG_PURGE:
793                         li->li_age = log_age_parse( c->argv[1] );
794                         if ( li->li_age < 1 ) {
795                                 rc = 1;
796                         } else {
797                                 li->li_cycle = log_age_parse( c->argv[2] );
798                                 if ( li->li_cycle < 1 ) {
799                                         rc = 1;
800                                 } else if ( slapMode & SLAP_SERVER_MODE ) {
801                                         struct re_s *re = li->li_task;
802                                         if ( re )
803                                                 re->interval.tv_sec = li->li_cycle;
804                                         else
805                                                 li->li_task = ldap_pvt_runqueue_insert( &slapd_rq,
806                                                         li->li_cycle, accesslog_purge, li,
807                                                         "accesslog_purge", li->li_db ?
808                                                                 li->li_db->be_suffix[0].bv_val :
809                                                                 c->be->be_suffix[0].bv_val );
810                                 }
811                         }
812                         break;
813                 case LOG_SUCCESS:
814                         li->li_success = c->value_int;
815                         break;
816                 case LOG_OLD:
817                         li->li_oldf = str2filter( c->argv[1] );
818                         if ( !li->li_oldf ) {
819                                 sprintf( c->cr_msg, "bad filter!" );
820                                 rc = 1;
821                         }
822                         break;
823                 case LOG_OLDATTR: {
824                         int i;
825                         AttributeDescription *ad;
826                         const char *text;
827
828                         for ( i=1; i< c->argc; i++ ) {
829                                 ad = NULL;
830                                 if ( slap_str2ad( c->argv[i], &ad, &text ) == LDAP_SUCCESS ) {
831                                         log_attr *la = ch_malloc( sizeof( log_attr ));
832                                         la->attr = ad;
833                                         la->next = li->li_oldattrs;
834                                         li->li_oldattrs = la;
835                                 } else {
836                                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s <%s>: %s",
837                                                 c->argv[0], c->argv[i], text );
838                                         Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
839                                                 "%s: %s\n", c->log, c->cr_msg, 0 );
840                                         rc = ARG_BAD_CONF;
841                                         break;
842                                 }
843                         }
844                         }
845                         break;
846                 }
847                 break;
848         }
849         return rc;
850 }
851
852 static int
853 accesslog_ctrls( LDAPControl **ctrls, BerVarray *valsp, void *memctx )
854 {
855         int                     i, rc = 0;
856
857         assert( valsp != NULL );
858         assert( ctrls != NULL );
859
860         *valsp = NULL;
861
862         for ( i = 0; ctrls[ i ] != NULL; i++ ) {
863                 BerElementBuffer        berbuf;
864                 BerElement              *ber = (BerElement *)&berbuf;
865
866                 ber_init2( ber, NULL, LBER_USE_DER );
867
868                 if ( ldap_pvt_put_control( ctrls[ i ], ber ) != LDAP_SUCCESS ) {
869                         rc++;
870
871                 } else {
872                         struct berval           ctrlbv;
873
874                         if ( ber_flatten2( ber, &ctrlbv, 0 ) == -1 ) {
875                                 rc++;
876
877                         } else {
878                                 struct berval   bv;
879                                 char            buf[ 32 ], *ptr;
880
881                                 bv.bv_len = snprintf( buf,
882                                         sizeof( buf ), "{%d}", i );
883                                 bv.bv_len += ctrlbv.bv_len;
884                                 ptr = bv.bv_val = ch_malloc( bv.bv_len + 1 );
885                                 ptr = lutil_strcopy( ptr, buf );
886                                 AC_MEMCPY( ptr, ctrlbv.bv_val, ctrlbv.bv_len );
887                                 bv.bv_val[ bv.bv_len ] = '\0';
888
889                                 ber_bvarray_add_x( valsp, &bv, memctx );
890                         }
891                 }
892
893                 (void)ber_free_buf( ber );
894         }
895
896         return rc;
897 }
898
899 static Entry *accesslog_entry( Operation *op, SlapReply *rs, int logop,
900         Operation *op2 ) {
901         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
902         log_info *li = on->on_bi.bi_private;
903
904         char rdnbuf[STRLENOF(RDNEQ)+LDAP_LUTIL_GENTIME_BUFSIZE+8];
905         char nrdnbuf[STRLENOF(RDNEQ)+LDAP_LUTIL_GENTIME_BUFSIZE+8];
906
907         struct berval rdn, nrdn, timestamp, ntimestamp, bv;
908         slap_verbmasks *lo = logops+logop+EN_OFFSET;
909
910         Entry *e = entry_alloc();
911
912         strcpy( rdnbuf, RDNEQ );
913         rdn.bv_val = rdnbuf;
914         strcpy( nrdnbuf, RDNEQ );
915         nrdn.bv_val = nrdnbuf;
916
917         timestamp.bv_val = rdnbuf+STRLENOF(RDNEQ);
918         timestamp.bv_len = sizeof(rdnbuf) - STRLENOF(RDNEQ);
919         slap_timestamp( &op->o_time, &timestamp );
920         sprintf( timestamp.bv_val + timestamp.bv_len-1, ".%06dZ", op->o_tincr );
921         timestamp.bv_len += 7;
922
923         rdn.bv_len = STRLENOF(RDNEQ)+timestamp.bv_len;
924         ad_reqStart->ad_type->sat_equality->smr_normalize(
925                 SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, ad_reqStart->ad_type->sat_syntax,
926                 ad_reqStart->ad_type->sat_equality, &timestamp, &ntimestamp,
927                 op->o_tmpmemctx );
928
929         strcpy( nrdn.bv_val + STRLENOF(RDNEQ), ntimestamp.bv_val );
930         nrdn.bv_len = STRLENOF(RDNEQ)+ntimestamp.bv_len;
931         build_new_dn( &e->e_name, li->li_db->be_suffix, &rdn, NULL );
932         build_new_dn( &e->e_nname, li->li_db->be_nsuffix, &nrdn, NULL );
933
934         attr_merge_one( e, slap_schema.si_ad_objectClass,
935                 &log_ocs[logop]->soc_cname, NULL );
936         attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
937                 &log_ocs[logop]->soc_cname, NULL );
938         attr_merge_one( e, ad_reqStart, &timestamp, &ntimestamp );
939         op->o_tmpfree( ntimestamp.bv_val, op->o_tmpmemctx );
940
941         slap_op_time( &op2->o_time, &op2->o_tincr );
942
943         timestamp.bv_len = sizeof(rdnbuf) - STRLENOF(RDNEQ);
944         slap_timestamp( &op2->o_time, &timestamp );
945         sprintf( timestamp.bv_val + timestamp.bv_len-1, ".%06dZ", op2->o_tincr );
946         timestamp.bv_len += 7;
947
948         attr_merge_normalize_one( e, ad_reqEnd, &timestamp, op->o_tmpmemctx );
949
950         /* Exops have OID appended */
951         if ( logop == LOG_EN_EXTENDED ) {
952                 bv.bv_len = lo->word.bv_len + op->ore_reqoid.bv_len + 2;
953                 bv.bv_val = ch_malloc( bv.bv_len + 1 );
954                 AC_MEMCPY( bv.bv_val, lo->word.bv_val, lo->word.bv_len );
955                 bv.bv_val[lo->word.bv_len] = '{';
956                 AC_MEMCPY( bv.bv_val+lo->word.bv_len+1, op->ore_reqoid.bv_val,
957                         op->ore_reqoid.bv_len );
958                 bv.bv_val[bv.bv_len-1] = '}';
959                 bv.bv_val[bv.bv_len] = '\0';
960                 attr_merge_one( e, ad_reqType, &bv, NULL );
961         } else {
962                 attr_merge_one( e, ad_reqType, &lo->word, NULL );
963         }
964
965         rdn.bv_len = sprintf( rdn.bv_val, "%lu", op->o_connid );
966         attr_merge_one( e, ad_reqSession, &rdn, NULL );
967
968         if ( BER_BVISNULL( &op->o_dn ) ) {
969                 attr_merge_one( e, ad_reqAuthzID, (struct berval *)&slap_empty_bv,
970                         (struct berval *)&slap_empty_bv );
971         } else {
972                 attr_merge_one( e, ad_reqAuthzID, &op->o_dn, &op->o_ndn );
973         }
974
975         /* FIXME: need to add reqControls and reqRespControls */
976         if ( op->o_ctrls ) {
977                 BerVarray       vals = NULL;
978
979                 (void)accesslog_ctrls( op->o_ctrls, &vals, op->o_tmpmemctx );
980                 if ( vals != NULL ) {
981                         attr_merge( e, ad_reqControls, vals, NULL );
982                         ber_bvarray_free_x( vals, op->o_tmpmemctx );
983                 }
984         }
985
986         if ( rs->sr_ctrls ) {
987                 BerVarray       vals = NULL;
988
989                 (void)accesslog_ctrls( rs->sr_ctrls, &vals, op->o_tmpmemctx );
990                 if ( vals != NULL ) {
991                         attr_merge( e, ad_reqRespControls, vals, NULL );
992                         ber_bvarray_free_x( vals, op->o_tmpmemctx );
993                 }
994
995         }
996
997         return e;
998 }
999
1000 static struct berval scopes[] = {
1001         BER_BVC("base"),
1002         BER_BVC("one"),
1003         BER_BVC("sub"),
1004         BER_BVC("subord")
1005 };
1006
1007 static struct berval derefs[] = {
1008         BER_BVC("never"),
1009         BER_BVC("searching"),
1010         BER_BVC("finding"),
1011         BER_BVC("always")
1012 };
1013
1014 static struct berval simple = BER_BVC("SIMPLE");
1015
1016 static void accesslog_val2val(AttributeDescription *ad, struct berval *val,
1017         char c_op, struct berval *dst) {
1018         char *ptr;
1019
1020         dst->bv_len = ad->ad_cname.bv_len + val->bv_len + 2;
1021         if ( c_op ) dst->bv_len++;
1022
1023         dst->bv_val = ch_malloc( dst->bv_len+1 );
1024
1025         ptr = lutil_strcopy( dst->bv_val, ad->ad_cname.bv_val );
1026         *ptr++ = ':';
1027         if ( c_op )
1028                 *ptr++ = c_op;
1029         *ptr++ = ' ';
1030         AC_MEMCPY( ptr, val->bv_val, val->bv_len );
1031         dst->bv_val[dst->bv_len] = '\0';
1032 }
1033
1034 static int accesslog_response(Operation *op, SlapReply *rs) {
1035         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1036         log_info *li = on->on_bi.bi_private;
1037         Attribute *a, *last_attr;
1038         Modifications *m;
1039         struct berval *b;
1040         int i;
1041         int logop;
1042         slap_verbmasks *lo;
1043         Entry *e = NULL, *old = NULL;
1044         char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE+8];
1045         struct berval bv;
1046         char *ptr;
1047         BerVarray vals;
1048         Operation op2 = {0};
1049         SlapReply rs2 = {REP_RESULT};
1050
1051         if ( rs->sr_type != REP_RESULT && rs->sr_type != REP_EXTENDED )
1052                 return SLAP_CB_CONTINUE;
1053
1054         switch ( op->o_tag ) {
1055         case LDAP_REQ_ADD:              logop = LOG_EN_ADD; break;
1056         case LDAP_REQ_DELETE:   logop = LOG_EN_DELETE; break;
1057         case LDAP_REQ_MODIFY:   logop = LOG_EN_MODIFY; break;
1058         case LDAP_REQ_MODRDN:   logop = LOG_EN_MODRDN; break;
1059         case LDAP_REQ_COMPARE:  logop = LOG_EN_COMPARE; break;
1060         case LDAP_REQ_SEARCH:   logop = LOG_EN_SEARCH; break;
1061         case LDAP_REQ_BIND:             logop = LOG_EN_BIND; break;
1062         case LDAP_REQ_EXTENDED: logop = LOG_EN_EXTENDED; break;
1063         default:        /* unknown operation type */
1064                 logop = LOG_EN_UNKNOWN; break;
1065         }       /* Unbind and Abandon never reach here */
1066
1067         lo = logops+logop+EN_OFFSET;
1068         if ( !( li->li_ops & lo->mask ))
1069                 return SLAP_CB_CONTINUE;
1070
1071         if ( lo->mask & LOG_OP_WRITES ) {
1072                 ldap_pvt_thread_mutex_lock( &li->li_log_mutex );
1073                 old = li->li_old;
1074                 li->li_old = NULL;
1075                 ldap_pvt_thread_rmutex_unlock( &li->li_op_rmutex, op->o_tid );
1076         }
1077
1078         if ( li->li_success && rs->sr_err != LDAP_SUCCESS )
1079                 goto done;
1080
1081         e = accesslog_entry( op, rs, logop, &op2 );
1082
1083         attr_merge_one( e, ad_reqDN, &op->o_req_dn, &op->o_req_ndn );
1084
1085         if ( rs->sr_text ) {
1086                 ber_str2bv( rs->sr_text, 0, 0, &bv );
1087                 attr_merge_one( e, ad_reqMessage, &bv, NULL );
1088         }
1089         bv.bv_len = sprintf( timebuf, "%d", rs->sr_err );
1090         bv.bv_val = timebuf;
1091
1092         attr_merge_one( e, ad_reqResult, &bv, NULL );
1093
1094         last_attr = attr_find( e->e_attrs, ad_reqResult );
1095
1096         switch( logop ) {
1097         case LOG_EN_ADD:
1098         case LOG_EN_DELETE: {
1099                 char c_op;
1100                 Entry *e2;
1101
1102                 if ( logop == LOG_EN_ADD ) {
1103                         e2 = op->ora_e;
1104                         c_op = '+';
1105                 } else {
1106                         if ( !old )
1107                                 break;
1108                         e2 = old;
1109                         c_op = 0;
1110                 }
1111                 /* count all the vals */
1112                 i = 0;
1113                 for ( a=e2->e_attrs; a; a=a->a_next ) {
1114                         if ( a->a_vals ) {
1115                                 for (b=a->a_vals; !BER_BVISNULL( b ); b++) {
1116                                         i++;
1117                                 }
1118                         }
1119                 }
1120                 vals = ch_malloc( (i+1) * sizeof( struct berval ));
1121                 i = 0;
1122                 for ( a=e2->e_attrs; a; a=a->a_next ) {
1123                         if ( a->a_vals ) {
1124                                 for (b=a->a_vals; !BER_BVISNULL( b ); b++,i++) {
1125                                         accesslog_val2val( a->a_desc, b, c_op, &vals[i] );
1126                                 }
1127                         }
1128                 }
1129                 vals[i].bv_val = NULL;
1130                 vals[i].bv_len = 0;
1131                 a = attr_alloc( logop == LOG_EN_ADD ? ad_reqMod : ad_reqOld );
1132                 a->a_vals = vals;
1133                 a->a_nvals = vals;
1134                 last_attr->a_next = a;
1135                 break;
1136         }
1137
1138         case LOG_EN_MODRDN:
1139         case LOG_EN_MODIFY:
1140                 /* count all the mods */
1141                 i = 0;
1142                 for ( m = op->orm_modlist; m; m = m->sml_next ) {
1143                         if ( m->sml_values ) {
1144                                 for ( b = m->sml_values; !BER_BVISNULL( b ); b++ ) {
1145                                         i++;
1146                                 }
1147                         } else if ( m->sml_op == LDAP_MOD_DELETE ||
1148                                 m->sml_op == LDAP_MOD_REPLACE )
1149                         {
1150                                 i++;
1151                         }
1152                 }
1153                 vals = ch_malloc( (i+1) * sizeof( struct berval ));
1154                 i = 0;
1155
1156                 /* init flags on old entry */
1157                 if ( old ) {
1158                         for ( a = old->e_attrs; a; a = a->a_next ) {
1159                                 log_attr *la;
1160                                 a->a_flags = 0;
1161
1162                                 /* look for attrs that are always logged */
1163                                 for ( la = li->li_oldattrs; la; la = la->next ) {
1164                                         if ( a->a_desc == la->attr ) {
1165                                                 a->a_flags = 1;
1166                                         }
1167                                 }
1168                         }
1169                 }
1170
1171                 for ( m = op->orm_modlist; m; m = m->sml_next ) {
1172                         /* Mark this attribute as modified */
1173                         if ( old ) {
1174                                 a = attr_find( old->e_attrs, m->sml_desc );
1175                                 if ( a ) {
1176                                         a->a_flags = 1;
1177                                 }
1178                         }
1179
1180                         /* don't log the RDN mods; they're explicitly logged later */
1181                         if ( logop == LOG_EN_MODRDN &&
1182                                 ( m->sml_op == SLAP_MOD_SOFTADD ||
1183                                   m->sml_op == LDAP_MOD_DELETE ) )
1184                         {
1185                                 continue;
1186                         }
1187
1188                         if ( m->sml_values ) {
1189                                 for ( b = m->sml_values; !BER_BVISNULL( b ); b++, i++ ) {
1190                                         char c_op;
1191
1192                                         switch ( m->sml_op ) {
1193                                         case LDAP_MOD_ADD: c_op = '+'; break;
1194                                         case LDAP_MOD_DELETE:   c_op = '-'; break;
1195                                         case LDAP_MOD_REPLACE:  c_op = '='; break;
1196                                         case LDAP_MOD_INCREMENT:        c_op = '#'; break;
1197
1198                                         /* unknown op. there shouldn't be any of these. we
1199                                          * don't know what to do with it, but we shouldn't just
1200                                          * ignore it.
1201                                          */
1202                                         default: c_op = '?'; break;
1203                                         }
1204                                         accesslog_val2val( m->sml_desc, b, c_op, &vals[i] );
1205                                 }
1206                         } else if ( m->sml_op == LDAP_MOD_DELETE ||
1207                                 m->sml_op == LDAP_MOD_REPLACE )
1208                         {
1209                                 vals[i].bv_len = m->sml_desc->ad_cname.bv_len + 2;
1210                                 vals[i].bv_val = ch_malloc( vals[i].bv_len + 1 );
1211                                 ptr = lutil_strcopy( vals[i].bv_val,
1212                                         m->sml_desc->ad_cname.bv_val );
1213                                 *ptr++ = ':';
1214                                 if ( m->sml_op == LDAP_MOD_DELETE ) {
1215                                         *ptr++ = '-';
1216                                 } else {
1217                                         *ptr++ = '=';
1218                                 }
1219                                 *ptr = '\0';
1220                                 i++;
1221                         }
1222                 }
1223
1224                 if ( i > 0 ) {
1225                         BER_BVZERO( &vals[i] );
1226                         a = attr_alloc( ad_reqMod );
1227                         a->a_vals = vals;
1228                         a->a_nvals = vals;
1229                         last_attr->a_next = a;
1230                         last_attr = a;
1231
1232                 } else {
1233                         ch_free( vals );
1234                 }
1235
1236                 if ( old ) {
1237                         /* count all the vals */
1238                         i = 0;
1239                         for ( a = old->e_attrs; a != NULL; a = a->a_next ) {
1240                                 if ( a->a_vals && a->a_flags ) {
1241                                         for ( b = a->a_vals; !BER_BVISNULL( b ); b++ ) {
1242                                                 i++;
1243                                         }
1244                                 }
1245                         }
1246                         vals = ch_malloc( (i + 1) * sizeof( struct berval ) );
1247                         i = 0;
1248                         for ( a=old->e_attrs; a; a=a->a_next ) {
1249                                 if ( a->a_vals && a->a_flags ) {
1250                                         for (b=a->a_vals; !BER_BVISNULL( b ); b++,i++) {
1251                                                 accesslog_val2val( a->a_desc, b, 0, &vals[i] );
1252                                         }
1253                                 }
1254                         }
1255                         vals[i].bv_val = NULL;
1256                         vals[i].bv_len = 0;
1257                         a = attr_alloc( ad_reqOld );
1258                         a->a_vals = vals;
1259                         a->a_nvals = vals;
1260                         last_attr->a_next = a;
1261                 }
1262                 if ( logop == LOG_EN_MODIFY ) {
1263                         break;
1264                 }
1265
1266                 /* Now log the actual modRDN info */
1267                 attr_merge_one( e, ad_reqNewRDN, &op->orr_newrdn, &op->orr_nnewrdn );
1268                 attr_merge_one( e, ad_reqDeleteOldRDN, op->orr_deleteoldrdn ?
1269                         (struct berval *)&slap_true_bv : (struct berval *)&slap_false_bv,
1270                         NULL );
1271                 if ( op->orr_newSup ) {
1272                         attr_merge_one( e, ad_reqNewSuperior, op->orr_newSup, op->orr_nnewSup );
1273                 }
1274                 break;
1275
1276         case LOG_EN_COMPARE:
1277                 bv.bv_len = op->orc_ava->aa_desc->ad_cname.bv_len + 1 +
1278                         op->orc_ava->aa_value.bv_len;
1279                 bv.bv_val = op->o_tmpalloc( bv.bv_len+1, op->o_tmpmemctx );
1280                 ptr = lutil_strcopy( bv.bv_val, op->orc_ava->aa_desc->ad_cname.bv_val );
1281                 *ptr++ = '=';
1282                 AC_MEMCPY( ptr, op->orc_ava->aa_value.bv_val, op->orc_ava->aa_value.bv_len );
1283                 bv.bv_val[bv.bv_len] = '\0';
1284                 attr_merge_one( e, ad_reqAssertion, &bv, NULL );
1285                 op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
1286                 break;
1287
1288         case LOG_EN_SEARCH:
1289                 attr_merge_one( e, ad_reqScope, &scopes[op->ors_scope], NULL );
1290                 attr_merge_one( e, ad_reqDerefAliases, &derefs[op->ors_deref], NULL );
1291                 attr_merge_one( e, ad_reqAttrsOnly, op->ors_attrsonly ?
1292                         (struct berval *)&slap_true_bv : (struct berval *)&slap_false_bv,
1293                         NULL );
1294                 if ( !BER_BVISEMPTY( &op->ors_filterstr ))
1295                         attr_merge_one( e, ad_reqFilter, &op->ors_filterstr, NULL );
1296                 if ( op->ors_attrs ) {
1297                         /* count them */
1298                         for (i=0; !BER_BVISNULL(&op->ors_attrs[i].an_name );i++)
1299                                 ;
1300                         vals = op->o_tmpalloc( (i+1) * sizeof(struct berval),
1301                                 op->o_tmpmemctx );
1302                         for (i=0; !BER_BVISNULL(&op->ors_attrs[i].an_name );i++)
1303                                 vals[i] = op->ors_attrs[i].an_name;
1304                         vals[i].bv_val = NULL;
1305                         vals[i].bv_len = 0;
1306                         attr_merge( e, ad_reqAttr, vals, NULL );
1307                         op->o_tmpfree( vals, op->o_tmpmemctx );
1308                 }
1309                 bv.bv_val = timebuf;
1310                 bv.bv_len = sprintf( bv.bv_val, "%d", rs->sr_nentries );
1311                 attr_merge_one( e, ad_reqEntries, &bv, NULL );
1312
1313                 bv.bv_len = sprintf( bv.bv_val, "%d", op->ors_tlimit );
1314                 attr_merge_one( e, ad_reqTimeLimit, &bv, NULL );
1315
1316                 bv.bv_len = sprintf( bv.bv_val, "%d", op->ors_slimit );
1317                 attr_merge_one( e, ad_reqSizeLimit, &bv, NULL );
1318                 break;
1319
1320         case LOG_EN_BIND:
1321                 bv.bv_val = timebuf;
1322                 bv.bv_len = sprintf( bv.bv_val, "%d", op->o_protocol );
1323                 attr_merge_one( e, ad_reqVersion, &bv, NULL );
1324                 if ( op->orb_method == LDAP_AUTH_SIMPLE ) {
1325                         attr_merge_one( e, ad_reqMethod, &simple, NULL );
1326                 } else {
1327                         bv.bv_len = STRLENOF("SASL()") + op->orb_mech.bv_len;
1328                         bv.bv_val = op->o_tmpalloc( bv.bv_len + 1, op->o_tmpmemctx );
1329                         ptr = lutil_strcopy( bv.bv_val, "SASL(" );
1330                         ptr = lutil_strcopy( ptr, op->orb_mech.bv_val );
1331                         *ptr++ = ')';
1332                         *ptr = '\0';
1333                         attr_merge_one( e, ad_reqMethod, &bv, NULL );
1334                         op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
1335                 }
1336
1337                 break;
1338
1339         case LOG_EN_EXTENDED:
1340                 if ( op->ore_reqdata ) {
1341                         attr_merge_one( e, ad_reqData, op->ore_reqdata, NULL );
1342                 }
1343                 break;
1344
1345         case LOG_EN_UNKNOWN:
1346                 /* we don't know its parameters, don't add any */
1347                 break;
1348         }
1349
1350         op2.o_hdr = op->o_hdr;
1351         op2.o_tag = LDAP_REQ_ADD;
1352         op2.o_bd = li->li_db;
1353         op2.o_dn = li->li_db->be_rootdn;
1354         op2.o_ndn = li->li_db->be_rootndn;
1355         op2.o_req_dn = e->e_name;
1356         op2.o_req_ndn = e->e_nname;
1357         op2.ora_e = e;
1358         op2.o_callback = &nullsc;
1359
1360         if (( lo->mask & LOG_OP_WRITES ) && !BER_BVISEMPTY( &op->o_csn )) {
1361                 slap_queue_csn( &op2, &op->o_csn );
1362         }
1363
1364         op2.o_bd->be_add( &op2, &rs2 );
1365         if ( e == op2.ora_e ) entry_free( e );
1366         e = NULL;
1367
1368 done:
1369         if ( lo->mask & LOG_OP_WRITES )
1370                 ldap_pvt_thread_mutex_unlock( &li->li_log_mutex );
1371         if ( old ) entry_free( old );
1372         return SLAP_CB_CONTINUE;
1373 }
1374
1375 /* Since Bind success is sent by the frontend, it won't normally enter
1376  * the overlay response callback. Add another callback to make sure it
1377  * gets here.
1378  */
1379 static int
1380 accesslog_bind_resp( Operation *op, SlapReply *rs )
1381 {
1382         BackendDB *be, db;
1383         int rc;
1384         slap_callback *sc;
1385
1386         be = op->o_bd;
1387         db = *be;
1388         op->o_bd = &db;
1389         db.bd_info = op->o_callback->sc_private;
1390         rc = accesslog_response( op, rs );
1391         op->o_bd = be;
1392         sc = op->o_callback;
1393         op->o_callback = sc->sc_next;
1394         op->o_tmpfree( sc, op->o_tmpmemctx );
1395         return rc;
1396 }
1397
1398 static int
1399 accesslog_op_bind( Operation *op, SlapReply *rs )
1400 {
1401         slap_callback *sc;
1402
1403         sc = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
1404         sc->sc_response = accesslog_bind_resp;
1405         sc->sc_private = op->o_bd->bd_info;
1406
1407         if ( op->o_callback ) {
1408                 sc->sc_next = op->o_callback->sc_next;
1409                 op->o_callback->sc_next = sc;
1410         } else {
1411                 op->o_callback = sc;
1412         }
1413         return SLAP_CB_CONTINUE;
1414 }
1415
1416 static int
1417 accesslog_op_mod( Operation *op, SlapReply *rs )
1418 {
1419         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1420         log_info *li = on->on_bi.bi_private;
1421
1422         if ( li->li_ops & LOG_OP_WRITES ) {
1423                 ldap_pvt_thread_rmutex_lock( &li->li_op_rmutex, op->o_tid );
1424                 if ( li->li_oldf && ( op->o_tag == LDAP_REQ_DELETE ||
1425                         op->o_tag == LDAP_REQ_MODIFY ||
1426                         ( op->o_tag == LDAP_REQ_MODRDN && li->li_oldattrs ))) {
1427                         int rc;
1428                         Entry *e;
1429
1430                         op->o_bd->bd_info = on->on_info->oi_orig;
1431                         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1432                         if ( e ) {
1433                                 if ( test_filter( op, e, li->li_oldf ) == LDAP_COMPARE_TRUE )
1434                                         li->li_old = entry_dup( e );
1435                                 be_entry_release_rw( op, e, 0 );
1436                         }
1437                         op->o_bd->bd_info = (BackendInfo *)on;
1438                 }
1439         }
1440         return SLAP_CB_CONTINUE;
1441 }
1442
1443 /* unbinds are broadcast to all backends; we only log it if this
1444  * backend was used for the original bind.
1445  */
1446 static int
1447 accesslog_unbind( Operation *op, SlapReply *rs )
1448 {
1449         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1450         if ( op->o_conn->c_authz_backend == on->on_info->oi_origdb ) {
1451                 log_info *li = on->on_bi.bi_private;
1452                 Operation op2 = {0};
1453                 void *cids[SLAP_MAX_CIDS];
1454                 SlapReply rs2 = {REP_RESULT};
1455                 Entry *e;
1456
1457                 if ( !( li->li_ops & LOG_OP_UNBIND ))
1458                         return SLAP_CB_CONTINUE;
1459
1460                 e = accesslog_entry( op, rs, LOG_EN_UNBIND, &op2 );
1461                 op2.o_hdr = op->o_hdr;
1462                 op2.o_tag = LDAP_REQ_ADD;
1463                 op2.o_bd = li->li_db;
1464                 op2.o_dn = li->li_db->be_rootdn;
1465                 op2.o_ndn = li->li_db->be_rootndn;
1466                 op2.o_req_dn = e->e_name;
1467                 op2.o_req_ndn = e->e_nname;
1468                 op2.ora_e = e;
1469                 op2.o_callback = &nullsc;
1470                 op2.o_controls = cids;
1471                 memset(cids, 0, sizeof( cids ));
1472
1473                 op2.o_bd->be_add( &op2, &rs2 );
1474                 if ( e == op2.ora_e )
1475                         entry_free( e );
1476         }
1477         return SLAP_CB_CONTINUE;
1478 }
1479
1480 static int
1481 accesslog_abandon( Operation *op, SlapReply *rs )
1482 {
1483         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1484         log_info *li = on->on_bi.bi_private;
1485         Operation op2 = {0};
1486         void *cids[SLAP_MAX_CIDS];
1487         SlapReply rs2 = {REP_RESULT};
1488         Entry *e;
1489         char buf[64];
1490         struct berval bv;
1491
1492         if ( !op->o_time || !( li->li_ops & LOG_OP_ABANDON ))
1493                 return SLAP_CB_CONTINUE;
1494
1495         e = accesslog_entry( op, rs, LOG_EN_ABANDON, &op2 );
1496         bv.bv_val = buf;
1497         bv.bv_len = sprintf( buf, "%d", op->orn_msgid );
1498         attr_merge_one( e, ad_reqId, &bv, NULL );
1499
1500         op2.o_hdr = op->o_hdr;
1501         op2.o_tag = LDAP_REQ_ADD;
1502         op2.o_bd = li->li_db;
1503         op2.o_dn = li->li_db->be_rootdn;
1504         op2.o_ndn = li->li_db->be_rootndn;
1505         op2.o_req_dn = e->e_name;
1506         op2.o_req_ndn = e->e_nname;
1507         op2.ora_e = e;
1508         op2.o_callback = &nullsc;
1509         op2.o_controls = cids;
1510         memset(cids, 0, sizeof( cids ));
1511
1512         op2.o_bd->be_add( &op2, &rs2 );
1513         if ( e == op2.ora_e )
1514                 entry_free( e );
1515
1516         return SLAP_CB_CONTINUE;
1517 }
1518
1519 static int
1520 accesslog_operational( Operation *op, SlapReply *rs )
1521 {
1522         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1523         log_info *li = on->on_bi.bi_private;
1524
1525         if ( rs->sr_entry != NULL
1526                 && dn_match( &op->o_bd->be_nsuffix[0], &rs->sr_entry->e_nname ) )
1527         {
1528                 Attribute       **ap;
1529
1530                 for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next )
1531                         /* just count */ ;
1532
1533                 if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
1534                                 ad_inlist( ad_auditContext, rs->sr_attrs ) )
1535                 {
1536                         *ap = attr_alloc( ad_auditContext );
1537                         value_add_one( &(*ap)->a_vals,
1538                                 &li->li_db->be_suffix[0] );
1539                         value_add_one( &(*ap)->a_nvals,
1540                                 &li->li_db->be_nsuffix[0] );
1541                 }
1542         }
1543
1544         return SLAP_CB_CONTINUE;
1545 }
1546
1547 static slap_overinst accesslog;
1548
1549 static int
1550 accesslog_db_init(
1551         BackendDB *be,
1552         ConfigReply *cr
1553 )
1554 {
1555         slap_overinst *on = (slap_overinst *)be->bd_info;
1556         log_info *li = ch_calloc(1, sizeof(log_info));
1557
1558         on->on_bi.bi_private = li;
1559         ldap_pvt_thread_rmutex_init( &li->li_op_rmutex );
1560         ldap_pvt_thread_mutex_init( &li->li_log_mutex );
1561         return 0;
1562 }
1563
1564 static int
1565 accesslog_db_destroy(
1566         BackendDB *be,
1567         ConfigReply *cr
1568 )
1569 {
1570         slap_overinst *on = (slap_overinst *)be->bd_info;
1571         log_info *li = on->on_bi.bi_private;
1572         log_attr *la;
1573
1574         if ( li->li_oldf )
1575                 filter_free( li->li_oldf );
1576         for ( la=li->li_oldattrs; la; la=li->li_oldattrs ) {
1577                 li->li_oldattrs = la->next;
1578                 ch_free( la );
1579         }
1580         ldap_pvt_thread_mutex_destroy( &li->li_log_mutex );
1581         ldap_pvt_thread_rmutex_destroy( &li->li_op_rmutex );
1582         free( li );
1583         return LDAP_SUCCESS;
1584 }
1585
1586 /* Create the logdb's root entry if it's missing */
1587 static void *
1588 accesslog_db_root(
1589         void *ctx,
1590         void *arg )
1591 {
1592         struct re_s *rtask = arg;
1593         slap_overinst *on = rtask->arg;
1594         log_info *li = on->on_bi.bi_private;
1595
1596         Connection conn = {0};
1597         OperationBuffer opbuf;
1598         Operation *op;
1599
1600         Entry *e;
1601         int rc;
1602
1603         connection_fake_init( &conn, &opbuf, ctx );
1604         op = &opbuf.ob_op;
1605         op->o_bd = li->li_db;
1606         op->o_dn = li->li_db->be_rootdn;
1607         op->o_ndn = li->li_db->be_rootndn;
1608         rc = be_entry_get_rw( op, li->li_db->be_nsuffix, NULL, NULL, 0, &e );
1609
1610         if ( e ) {
1611                 be_entry_release_rw( op, e, 0 );
1612
1613         } else {
1614                 SlapReply rs = {REP_RESULT};
1615                 struct berval rdn, nrdn, attr;
1616                 char *ptr;
1617                 AttributeDescription *ad = NULL;
1618                 const char *text = NULL;
1619                 Entry *e_ctx;
1620
1621                 e = entry_alloc();
1622                 ber_dupbv( &e->e_name, li->li_db->be_suffix );
1623                 ber_dupbv( &e->e_nname, li->li_db->be_nsuffix );
1624
1625                 attr_merge_one( e, slap_schema.si_ad_objectClass,
1626                         &log_container->soc_cname, NULL );
1627
1628                 dnRdn( &e->e_name, &rdn );
1629                 dnRdn( &e->e_nname, &nrdn );
1630                 ptr = ber_bvchr( &rdn, '=' );
1631
1632                 assert( ptr != NULL );
1633
1634                 attr.bv_val = rdn.bv_val;
1635                 attr.bv_len = ptr - rdn.bv_val;
1636
1637                 slap_bv2ad( &attr, &ad, &text );
1638
1639                 rdn.bv_val = ptr+1;
1640                 rdn.bv_len -= attr.bv_len + 1;
1641                 ptr = ber_bvchr( &nrdn, '=' );
1642                 nrdn.bv_len -= ptr - nrdn.bv_val + 1;
1643                 nrdn.bv_val = ptr+1;
1644                 attr_merge_one( e, ad, &rdn, &nrdn );
1645
1646                 /* Get contextCSN from main DB */
1647                 op->o_bd = on->on_info->oi_origdb;
1648                 rc = be_entry_get_rw( op, op->o_bd->be_nsuffix, NULL,
1649                         slap_schema.si_ad_contextCSN, 0, &e_ctx );
1650
1651                 if ( e_ctx ) {
1652                         Attribute *a;
1653
1654                         a = attr_find( e_ctx->e_attrs, slap_schema.si_ad_contextCSN );
1655                         if ( a ) {
1656                                 attr_merge( e, slap_schema.si_ad_entryCSN, a->a_vals, NULL );
1657                                 attr_merge( e, a->a_desc, a->a_vals, NULL );
1658                         }
1659                         be_entry_release_rw( op, e_ctx, 0 );
1660                 }
1661                 op->o_bd = li->li_db;
1662
1663                 op->ora_e = e;
1664                 op->o_req_dn = e->e_name;
1665                 op->o_req_ndn = e->e_nname;
1666                 op->o_callback = &nullsc;
1667                 SLAP_DBFLAGS( op->o_bd ) |= SLAP_DBFLAG_NOLASTMOD;
1668                 rc = op->o_bd->be_add( op, &rs );
1669                 SLAP_DBFLAGS( op->o_bd ) ^= SLAP_DBFLAG_NOLASTMOD;
1670                 if ( e == op->ora_e )
1671                         entry_free( e );
1672         }
1673         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1674         ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
1675         ldap_pvt_runqueue_remove( &slapd_rq, rtask );
1676         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1677
1678         return NULL;
1679 }
1680
1681 static int
1682 accesslog_db_open(
1683         BackendDB *be,
1684         ConfigReply *cr
1685 )
1686 {
1687         slap_overinst *on = (slap_overinst *)be->bd_info;
1688         log_info *li = on->on_bi.bi_private;
1689
1690
1691         if ( !BER_BVISEMPTY( &li->li_db_suffix )) {
1692                 li->li_db = select_backend( &li->li_db_suffix, 0 );
1693                 ch_free( li->li_db_suffix.bv_val );
1694                 BER_BVZERO( &li->li_db_suffix );
1695         }
1696         if ( li->li_db == NULL ) {
1697                 Debug( LDAP_DEBUG_ANY,
1698                         "accesslog: \"logdb <suffix>\" missing or invalid.\n",
1699                         0, 0, 0 );
1700                 return 1;
1701         }
1702
1703         if ( slapMode & SLAP_TOOL_MODE )
1704                 return 0;
1705
1706         if ( BER_BVISEMPTY( &li->li_db->be_rootndn )) {
1707                 ber_dupbv( &li->li_db->be_rootdn, li->li_db->be_suffix );
1708                 ber_dupbv( &li->li_db->be_rootndn, li->li_db->be_nsuffix );
1709         }
1710
1711         ldap_pvt_runqueue_insert( &slapd_rq, 3600, accesslog_db_root, on,
1712                 "accesslog_db_root", li->li_db->be_suffix[0].bv_val );
1713
1714         return 0;
1715 }
1716
1717 int accesslog_initialize()
1718 {
1719         int i, rc;
1720
1721         accesslog.on_bi.bi_type = "accesslog";
1722         accesslog.on_bi.bi_db_init = accesslog_db_init;
1723         accesslog.on_bi.bi_db_destroy = accesslog_db_destroy;
1724         accesslog.on_bi.bi_db_open = accesslog_db_open;
1725
1726         accesslog.on_bi.bi_op_add = accesslog_op_mod;
1727         accesslog.on_bi.bi_op_bind = accesslog_op_bind;
1728         accesslog.on_bi.bi_op_delete = accesslog_op_mod;
1729         accesslog.on_bi.bi_op_modify = accesslog_op_mod;
1730         accesslog.on_bi.bi_op_modrdn = accesslog_op_mod;
1731         accesslog.on_bi.bi_op_unbind = accesslog_unbind;
1732         accesslog.on_bi.bi_op_abandon = accesslog_abandon;
1733         accesslog.on_bi.bi_operational = accesslog_operational;
1734         accesslog.on_response = accesslog_response;
1735
1736         accesslog.on_bi.bi_cf_ocs = log_cfocs;
1737
1738         nullsc.sc_response = slap_null_cb;
1739
1740         rc = config_register_schema( log_cfats, log_cfocs );
1741         if ( rc ) return rc;
1742
1743         /* log schema integration */
1744         for ( i=0; lattrs[i].at; i++ ) {
1745                 int code;
1746
1747                 code = register_at( lattrs[i].at, lattrs[i].ad, 0 );
1748                 if ( code ) {
1749                         Debug( LDAP_DEBUG_ANY,
1750                                 "accesslog_init: register_at failed\n", 0, 0, 0 );
1751                         return -1;
1752                 }
1753         }
1754         for ( i=0; locs[i].ot; i++ ) {
1755                 int code;
1756
1757                 code = register_oc( locs[i].ot, locs[i].oc, 0 );
1758                 if ( code ) {
1759                         Debug( LDAP_DEBUG_ANY,
1760                                 "accesslog_init: register_oc failed\n", 0, 0, 0 );
1761                         return -1;
1762                 }
1763         }
1764
1765         return overlay_register(&accesslog);
1766 }
1767
1768 #if SLAPD_OVER_ACCESSLOG == SLAPD_MOD_DYNAMIC
1769 int
1770 init_module( int argc, char *argv[] )
1771 {
1772         return accesslog_initialize();
1773 }
1774 #endif
1775
1776 #endif /* SLAPD_OVER_ACCESSLOG */