~ubuntu-branches/ubuntu/wily/389-ds-base/wily

« back to all changes in this revision

Viewing changes to ldap/servers/plugins/retrocl/retrocl_trim.c

  • Committer: Package Import Robot
  • Author(s): Krzysztof Klimonda
  • Date: 2012-03-27 14:26:16 UTC
  • Revision ID: package-import@ubuntu.com-20120327142616-xt24t6nffm3f7ybz
Tags: upstream-1.2.11.7
ImportĀ upstreamĀ versionĀ 1.2.11.7

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** BEGIN COPYRIGHT BLOCK
 
2
 * This Program is free software; you can redistribute it and/or modify it under
 
3
 * the terms of the GNU General Public License as published by the Free Software
 
4
 * Foundation; version 2 of the License.
 
5
 * 
 
6
 * This Program is distributed in the hope that it will be useful, but WITHOUT
 
7
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
8
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 
9
 * 
 
10
 * You should have received a copy of the GNU General Public License along with
 
11
 * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
 
12
 * Place, Suite 330, Boston, MA 02111-1307 USA.
 
13
 * 
 
14
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 
15
 * right to link the code of this Program with code not covered under the GNU
 
16
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 
17
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 
18
 * permitted under this exception must only link to the code of this Program
 
19
 * through those well defined interfaces identified in the file named EXCEPTION
 
20
 * found in the source code files (the "Approved Interfaces"). The files of
 
21
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 
22
 * the Approved Interfaces without causing the resulting work to be covered by
 
23
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 
24
 * additions to the list of Approved Interfaces. You must obey the GNU General
 
25
 * Public License in all respects for all of the Program code and other code used
 
26
 * in conjunction with the Program except the Non-GPL Code covered by this
 
27
 * exception. If you modify this file, you may extend this exception to your
 
28
 * version of the file, but you are not obligated to do so. If you do not wish to
 
29
 * provide this exception without modification, you must delete this exception
 
30
 * statement from your version and license this file solely under the GPL without
 
31
 * exception. 
 
32
 * 
 
33
 * 
 
34
 * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
 
35
 * Copyright (C) 2005 Red Hat, Inc.
 
36
 * All rights reserved.
 
37
 * END COPYRIGHT BLOCK **/
 
38
 
 
39
#ifdef HAVE_CONFIG_H
 
40
#  include <config.h>
 
41
#endif
 
42
 
 
43
 
 
44
 
 
45
#include "retrocl.h"
 
46
 
 
47
typedef struct _trim_status {
 
48
    time_t              ts_c_max_age;           /* Constraint  - max age of a changelog entry */
 
49
    time_t              ts_s_last_trim;         /* Status - last time we trimmed */
 
50
    int                 ts_s_initialized;       /* Status - non-zero if initialized */
 
51
    int                 ts_s_trimming;          /* non-zero if trimming in progress */
 
52
    PRLock              *ts_s_trim_mutex;       /* protects ts_s_trimming */
 
53
} trim_status;
 
54
static trim_status ts = {0L, 0L, 0, 0, NULL};
 
55
 
 
56
/*
 
57
 * All standard changeLogEntry attributes (initialized in get_cleattrs)
 
58
 */
 
59
static const char *cleattrs[ 10 ] = { NULL, NULL, NULL, NULL, NULL, NULL,
 
60
                                      NULL, NULL, NULL };
 
61
 
 
62
static int retrocl_trimming = 0;
 
63
static Slapi_Eq_Context retrocl_trim_ctx = NULL;
 
64
 
 
65
/*
 
66
 * Function: get_cleattrs
 
67
 *
 
68
 * Returns: an array of pointers to attribute names.
 
69
 *
 
70
 * Arguments: None.
 
71
 *
 
72
 * Description: Initializes, if necessary, and returns an array of char *s
 
73
 *              with attribute names used for retrieving changeLogEntry
 
74
 *              entries from the directory.
 
75
 */
 
76
static const char **get_cleattrs(void)
 
77
{
 
78
    if ( cleattrs[ 0 ] == NULL ) {
 
79
        cleattrs[ 0 ] = attr_objectclass;
 
80
        cleattrs[ 1 ] = attr_changenumber;
 
81
        cleattrs[ 2 ] = attr_targetdn;
 
82
        cleattrs[ 3 ] = attr_changetype;
 
83
        cleattrs[ 4 ] = attr_newrdn;
 
84
        cleattrs[ 5 ] = attr_deleteoldrdn;
 
85
        cleattrs[ 6 ] = attr_changes;
 
86
        cleattrs[ 7 ] = attr_newsuperior;
 
87
        cleattrs[ 8 ] = attr_changetime;
 
88
        cleattrs[ 9 ] = NULL;
 
89
    }
 
90
    return cleattrs;
 
91
}
 
92
 
 
93
/*
 
94
 * Function: delete_changerecord
 
95
 *
 
96
 * Returns: LDAP_ error code
 
97
 * 
 
98
 * Arguments: the number of the change to delete
 
99
 *
 
100
 * Description:
 
101
 *
 
102
 */
 
103
 
 
104
static int
 
105
delete_changerecord( changeNumber cnum )
 
106
{
 
107
    Slapi_PBlock *pb;
 
108
    char        *dnbuf;
 
109
    int         delrc;
 
110
 
 
111
    dnbuf = slapi_ch_smprintf("%s=%ld, %s", attr_changenumber, cnum, 
 
112
             RETROCL_CHANGELOG_DN);
 
113
    pb = slapi_pblock_new ();
 
114
    slapi_delete_internal_set_pb ( pb, dnbuf, NULL /*controls*/, NULL /* uniqueid */,
 
115
                                                                   g_plg_identity[PLUGIN_RETROCL], 0 /* actions */ );
 
116
    slapi_delete_internal_pb (pb);
 
117
    slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &delrc );
 
118
    slapi_pblock_destroy( pb );
 
119
    
 
120
    if ( delrc != LDAP_SUCCESS ) {
 
121
        slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "delete_changerecord: could not delete "
 
122
                "change record %lu\n", cnum );
 
123
    } else {
 
124
        slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
 
125
                "delete_changerecord: deleted changelog entry \"%s\"\n", dnbuf);
 
126
    }
 
127
    slapi_ch_free((void **) &dnbuf );
 
128
    return delrc;
 
129
}
 
130
 
 
131
/*
 
132
 * Function: handle_getchangerecord_result
 
133
 * Arguments: op - pointer to Operation struct for this operation
 
134
 *            err - error code returned from search
 
135
 * Returns: nothing
 
136
 * Description: result handler for get_changerecord().  Sets the crt_err
 
137
 *              field of the cnum_result_t struct to the error returned
 
138
 *              from the backend.
 
139
 */
 
140
static void
 
141
handle_getchangerecord_result( int err, void *callback_data )
 
142
{
 
143
    cnum_result_t *crt = callback_data;
 
144
 
 
145
    if ( crt == NULL ) {
 
146
        slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
 
147
                "handle_getchangerecord_result: callback_data NULL\n" );
 
148
    } else {
 
149
        crt->crt_err = err;
 
150
    }
 
151
}
 
152
 
 
153
/*
 
154
 * Function: handle_getchangerecord_search
 
155
 * Arguments: op - pointer to Operation struct for this operation
 
156
 *            e - entry returned by backend
 
157
 * Returns: 0 in all cases
 
158
 * Description: Search result operation handler for get_changerecord().
 
159
 *              Sets fields in the cnum_result_t struct pointed to by
 
160
 *              op->o_handler_data.
 
161
 */
 
162
static int
 
163
handle_getchangerecord_search( Slapi_Entry *e, void *callback_data)
 
164
{
 
165
    cnum_result_t *crt = callback_data;
 
166
 
 
167
    if ( crt == NULL ) {
 
168
        slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
 
169
                "handle_getchangerecord_search: op->o_handler_data NULL\n" );
 
170
    } else if ( crt->crt_nentries > 0 ) {
 
171
        /* only return the first entry, I guess */
 
172
        slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
 
173
                "handle_getchangerecord_search: multiple entries returned\n" );
 
174
    } else {
 
175
        crt->crt_nentries++;
 
176
        crt->crt_entry = e;
 
177
    }
 
178
 
 
179
    return 0;
 
180
}
 
181
 
 
182
 
 
183
/*
 
184
 * Function: get_changerecord
 
185
 * Arguments: cnum - number of change record to retrieve
 
186
 * Returns: Pointer to an entry structure.  The caller must free the entry.
 
187
 *          If "err" is non-NULL, an error code is returned in the memory
 
188
 *          location it points to.
 
189
 * Description: Retrieve the change record entry whose number is "cnum".
 
190
 */
 
191
static Slapi_Entry *get_changerecord( changeNumber cnum, int *err )
 
192
{
 
193
    cnum_result_t       crt, *crtp = &crt;
 
194
    char                fstr[ 16 + CNUMSTR_LEN + 2 ];
 
195
    Slapi_PBlock *pb;
 
196
 
 
197
    if ( cnum == 0UL ) {
 
198
        if ( err != NULL ) {
 
199
            *err = LDAP_PARAM_ERROR;
 
200
        }
 
201
        return NULL;
 
202
    }
 
203
    crtp->crt_nentries = crtp->crt_err = 0; crtp->crt_entry = NULL;
 
204
    PR_snprintf( fstr, sizeof(fstr), "%s=%ld", attr_changenumber, cnum );
 
205
    
 
206
    pb = slapi_pblock_new ();
 
207
    slapi_search_internal_set_pb (pb, RETROCL_CHANGELOG_DN, 
 
208
                                  LDAP_SCOPE_SUBTREE, fstr,
 
209
                                  (char **)get_cleattrs(),  /* cast const */
 
210
                                  0 /* attrsonly */,
 
211
                                  NULL /* controls */, NULL /* uniqueid */,
 
212
                                  g_plg_identity[PLUGIN_RETROCL], 
 
213
                                  0 /* actions */);
 
214
 
 
215
    slapi_search_internal_callback_pb (pb, crtp, 
 
216
                                       handle_getchangerecord_result, 
 
217
                                       handle_getchangerecord_search, NULL );
 
218
    if ( err != NULL ) {
 
219
        *err = crtp->crt_err;
 
220
    }
 
221
 
 
222
    slapi_pblock_destroy (pb);
 
223
 
 
224
    return( crtp->crt_entry );
 
225
}
 
226
 
 
227
/*
 
228
 * Function: trim_changelog
 
229
 *
 
230
 * Arguments: none
 
231
 *
 
232
 * Returns: 0 on success, -1 on failure
 
233
 *
 
234
 * Description: Trims the changelog, according to the constraints
 
235
 * described by the ts structure.
 
236
 */
 
237
static int trim_changelog(void)
 
238
{
 
239
    int                 rc = 0, ldrc, done;
 
240
    time_t              now;
 
241
    changeNumber        first_in_log = 0, last_in_log = 0;
 
242
    Slapi_Entry         *e = NULL;
 
243
    int                 num_deleted = 0;
 
244
    int me,lt;
 
245
    
 
246
 
 
247
    now = current_time();
 
248
 
 
249
    PR_Lock( ts.ts_s_trim_mutex );
 
250
    me = ts.ts_c_max_age;
 
251
    lt = ts.ts_s_last_trim;
 
252
    PR_Unlock( ts.ts_s_trim_mutex );
 
253
 
 
254
    if ( now - lt >= (CHANGELOGDB_TRIM_INTERVAL / 1000) ) {
 
255
 
 
256
        /*
 
257
         * Trim the changelog.  Read sequentially through all the
 
258
         * entries, deleting any which do not meet the criteria
 
259
         * described in the ts structure.
 
260
         */
 
261
        done = 0;
 
262
 
 
263
        while ( !done && retrocl_trimming == 1 ) {
 
264
            int         did_delete;
 
265
            Slapi_Attr  *attr;
 
266
 
 
267
            did_delete = 0;
 
268
            first_in_log = retrocl_get_first_changenumber();
 
269
            if ( 0UL == first_in_log ) {
 
270
                slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, 
 
271
                                 "trim_changelog: no changelog records "
 
272
                                 "to trim\n" );
 
273
                /* Bail out - we can't do any useful work */
 
274
                break;
 
275
            }
 
276
 
 
277
            last_in_log = retrocl_get_last_changenumber();
 
278
            if ( last_in_log == first_in_log ) {
 
279
                /* Always leave at least one entry in the change log */
 
280
                break;
 
281
            }
 
282
            if ( me > 0L ) {
 
283
                e = get_changerecord( first_in_log, &ldrc );
 
284
                if ( NULL != e ) {
 
285
                    Slapi_Value *sval = NULL;
 
286
                    const struct berval *val = NULL;
 
287
                    rc = slapi_entry_attr_find( e, attr_changetime, &attr );
 
288
                    /* Bug 624442: Logic checking for lack of timestamp was
 
289
                       reversed. */
 
290
                    if ( 0 != rc  || slapi_attr_first_value( attr,&sval ) == -1 ||
 
291
                            (val = slapi_value_get_berval ( sval )) == NULL ||
 
292
                                NULL == val->bv_val ) {
 
293
                        /* What to do if there's no timestamp? Just delete it. */
 
294
                      retrocl_set_first_changenumber( first_in_log + 1 );
 
295
                      ldrc = delete_changerecord( first_in_log );
 
296
                      num_deleted++;
 
297
                      did_delete = 1;
 
298
                    } else {
 
299
                        time_t change_time = parse_localTime( val->bv_val );
 
300
                        if ( change_time + me < now ) {
 
301
                            retrocl_set_first_changenumber( first_in_log + 1 );
 
302
                            ldrc = delete_changerecord( first_in_log );
 
303
                            num_deleted++;
 
304
                            did_delete = 1;
 
305
                        }
 
306
                    /* slapi_entry_free( e ); */ /* XXXggood should we be freeing this? */
 
307
                    }
 
308
                }
 
309
            }
 
310
            if ( !did_delete ) {
 
311
                done = 1;
 
312
            }
 
313
        }
 
314
    } else {
 
315
       LDAPDebug(LDAP_DEBUG_PLUGIN, "not yet time to trim: %ld < (%d+%d)\n",
 
316
                 now,lt,(CHANGELOGDB_TRIM_INTERVAL/1000));
 
317
    }
 
318
    PR_Lock( ts.ts_s_trim_mutex );
 
319
    ts.ts_s_trimming = 0;
 
320
    ts.ts_s_last_trim = now;
 
321
    PR_Unlock( ts.ts_s_trim_mutex );
 
322
    if ( num_deleted > 0 ) {
 
323
        slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, 
 
324
                         "trim_changelog: removed %d change records\n",
 
325
                         num_deleted );
 
326
    }
 
327
    return rc;
 
328
}
 
329
 
 
330
static int retrocl_active_threads;
 
331
 
 
332
/*
 
333
 * Function: changelog_trim_thread_fn
 
334
 *
 
335
 * Returns: nothing
 
336
 * 
 
337
 * Arguments: none
 
338
 *
 
339
 * Description: the thread creation callback.  retrocl_active_threads is 
 
340
 * provided for debugging purposes.
 
341
 *
 
342
 */
 
343
 
 
344
static void
 
345
changelog_trim_thread_fn( void *arg )
 
346
{
 
347
    PR_AtomicIncrement(&retrocl_active_threads);
 
348
    trim_changelog();
 
349
    PR_AtomicDecrement(&retrocl_active_threads);
 
350
}
 
351
 
 
352
 
 
353
 
 
354
/*
 
355
 * Function: retrocl_housekeeping
 
356
 * Arguments: cur_time - the current time
 
357
 * Returns: nothing
 
358
 * Description: Determines if it is time to trim the changelog database,
 
359
 *              and if so, determines if the changelog database needs to
 
360
 *              be trimmed.  If so, a thread is started which will trim
 
361
 *              the database.  
 
362
 */
 
363
 
 
364
void retrocl_housekeeping ( time_t cur_time, void *noarg )
 
365
{
 
366
    int                 ldrc;
 
367
 
 
368
    if (retrocl_be_changelog == NULL) {
 
369
        LDAPDebug0Args(LDAP_DEBUG_TRACE,"not housekeeping if no cl be\n");
 
370
        return;
 
371
    }
 
372
 
 
373
    if ( !ts.ts_s_initialized ) {
 
374
        slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "changelog_housekeeping called before "
 
375
                "trimming constraints set\n" );
 
376
        return;
 
377
    }
 
378
 
 
379
    PR_Lock( ts.ts_s_trim_mutex );
 
380
    if ( !ts.ts_s_trimming ) {
 
381
        int     must_trim = 0;
 
382
        /* See if we need to trim */
 
383
        /* Has enough time elapsed since our last check? */
 
384
        if ( cur_time - ts.ts_s_last_trim >= (ts.ts_c_max_age) ) {
 
385
                /* Is the first entry too old? */
 
386
                time_t first_time;
 
387
                /*
 
388
                 * good we could avoid going to the database to retrieve
 
389
                 * this time information if we cached the last value we'd read.
 
390
                 * But a client might have deleted it over protocol.
 
391
                 */
 
392
                first_time = retrocl_getchangetime( SLAPI_SEQ_FIRST, &ldrc );
 
393
                LDAPDebug(LDAP_DEBUG_PLUGIN,
 
394
                          "cltrim: ldrc=%d, first_time=%ld, cur_time=%ld\n",
 
395
                          ldrc,first_time,cur_time);
 
396
                if ( LDAP_SUCCESS == ldrc && first_time > (time_t) 0L &&
 
397
                     first_time + ts.ts_c_max_age < cur_time ) {
 
398
                    must_trim = 1;
 
399
                }
 
400
        }
 
401
        if ( must_trim ) {
 
402
            LDAPDebug0Args(LDAP_DEBUG_TRACE,"changelog about to create thread\n");
 
403
            /* Start a thread to trim the changelog */
 
404
            ts.ts_s_trimming = 1;
 
405
            if ( PR_CreateThread( PR_USER_THREAD,
 
406
                    changelog_trim_thread_fn, NULL,
 
407
                    PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD,
 
408
                    RETROCL_DLL_DEFAULT_THREAD_STACKSIZE ) == NULL ) {
 
409
                slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "unable to create changelog trimming thread\n" );
 
410
            }
 
411
        } else {
 
412
            LDAPDebug0Args(LDAP_DEBUG_PLUGIN,
 
413
                      "changelog does not need to be trimmed\n");
 
414
        }
 
415
    }
 
416
    PR_Unlock( ts.ts_s_trim_mutex );
 
417
}
 
418
 
 
419
 
 
420
/*
 
421
 * Function: age_str2time
 
422
 *
 
423
 * Returns: time_t
 
424
 * 
 
425
 * Arguments: string representation of age (digits and unit s,m,h,d or w)
 
426
 *
 
427
 * Description:
 
428
 * convert time from string like 1h (1 hour) to corresponding time in seconds
 
429
 *
 
430
 */
 
431
 
 
432
static time_t
 
433
age_str2time (const char *age)
 
434
{
 
435
    char *maxage;
 
436
    char unit;
 
437
    time_t ageval;
 
438
    
 
439
    if (age == NULL || age[0] == '\0' || strcmp (age, "0") == 0) {
 
440
        return 0; 
 
441
    }
 
442
    
 
443
    maxage = slapi_ch_strdup ( age );
 
444
    if (!maxage) {
 
445
        slapi_log_error( SLAPI_LOG_PLUGIN, "retrocl",
 
446
                       "age_str2time: Out of memory\n" );
 
447
        ageval = -1;
 
448
        goto done;
 
449
    }
 
450
 
 
451
    unit = maxage[ strlen( maxage ) - 1 ];
 
452
    maxage[ strlen( maxage ) - 1 ] = '\0';
 
453
    ageval = strntoul( maxage, strlen( maxage ), 10 );
 
454
    switch ( unit ) {
 
455
    case 's':
 
456
      break;
 
457
    case 'm':
 
458
      ageval *= 60;
 
459
      break;
 
460
    case 'h':
 
461
      ageval *= ( 60 * 60 );
 
462
      break;
 
463
    case 'd':
 
464
      ageval *= ( 24 * 60 * 60 );
 
465
      break;
 
466
    case 'w':
 
467
      ageval *= ( 7 * 24 * 60 * 60 );
 
468
      break;
 
469
    default:
 
470
      slapi_log_error( SLAPI_LOG_PLUGIN, "retrocl",
 
471
                       "age_str2time: unknown unit \"%c\" "
 
472
                       "for maxiumum changelog age\n", unit );
 
473
      ageval = -1;
 
474
    }
 
475
done:
 
476
    if ( maxage) {
 
477
        slapi_ch_free ( (void **) &maxage );
 
478
    }
 
479
    return ageval;
 
480
}
 
481
 
 
482
/*
 
483
 * Function: retrocl_init_trimming
 
484
 *
 
485
 * Returns: none, exits on fatal error
 
486
 * 
 
487
 * Arguments: none
 
488
 *
 
489
 * Description: called during startup
 
490
 *
 
491
 */
 
492
 
 
493
void retrocl_init_trimming (void)
 
494
{
 
495
    const char *cl_maxage;
 
496
    time_t ageval;
 
497
    
 
498
    cl_maxage = retrocl_get_config_str(CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE);
 
499
    
 
500
    if (cl_maxage == NULL) {
 
501
      LDAPDebug0Args(LDAP_DEBUG_TRACE,"No maxage, not trimming retro changelog.\n");
 
502
      return;
 
503
    }
 
504
    ageval = age_str2time (cl_maxage);
 
505
    slapi_ch_free ((void **)&cl_maxage);
 
506
    
 
507
    ts.ts_c_max_age = ageval;
 
508
    ts.ts_s_last_trim = (time_t) 0L;
 
509
    ts.ts_s_trimming = 0;
 
510
    if (( ts.ts_s_trim_mutex = PR_NewLock()) == NULL ) {
 
511
        slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "set_changelog_trim_constraints: "
 
512
                "cannot create new lock.\n" );
 
513
        exit( 1 );
 
514
    }
 
515
    ts.ts_s_initialized = 1;
 
516
    retrocl_trimming = 1;
 
517
    
 
518
    retrocl_trim_ctx = slapi_eq_repeat(retrocl_housekeeping,
 
519
                                       NULL, (time_t)0,
 
520
                                       /* in milliseconds */
 
521
                                       CHANGELOGDB_TRIM_INTERVAL);
 
522
 
 
523
}
 
524
 
 
525
/*
 
526
 * Function: retrocl_stop_trimming
 
527
 *
 
528
 * Returns: none
 
529
 * 
 
530
 * Arguments: none
 
531
 *
 
532
 * Description: called when server is shutting down to ensure trimming stops
 
533
 * eventually.
 
534
 *
 
535
 */
 
536
 
 
537
void retrocl_stop_trimming(void)
 
538
{
 
539
    retrocl_trimming = 0;
 
540
    if (retrocl_trim_ctx) {
 
541
      slapi_eq_cancel(retrocl_trim_ctx);
 
542
      retrocl_trim_ctx = NULL;
 
543
    }
 
544
}
 
545