summaryrefslogtreecommitdiffstats
path: root/ldap/servers/plugins/retrocl/retrocl_trim.c
diff options
context:
space:
mode:
authorcvsadm <cvsadm>2005-01-21 00:44:34 +0000
committercvsadm <cvsadm>2005-01-21 00:44:34 +0000
commitb2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch)
treecf58939393a9032182c4fbc4441164a9456e82f8 /ldap/servers/plugins/retrocl/retrocl_trim.c
downloadds-ldapserver7x.tar.gz
ds-ldapserver7x.tar.xz
ds-ldapserver7x.zip
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'ldap/servers/plugins/retrocl/retrocl_trim.c')
-rw-r--r--ldap/servers/plugins/retrocl/retrocl_trim.c505
1 files changed, 505 insertions, 0 deletions
diff --git a/ldap/servers/plugins/retrocl/retrocl_trim.c b/ldap/servers/plugins/retrocl/retrocl_trim.c
new file mode 100644
index 00000000..5c413097
--- /dev/null
+++ b/ldap/servers/plugins/retrocl/retrocl_trim.c
@@ -0,0 +1,505 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+
+#include "retrocl.h"
+
+typedef struct _trim_status {
+ time_t ts_c_max_age; /* Constraint - max age of a changelog entry */
+ time_t ts_s_last_trim; /* Status - last time we trimmed */
+ int ts_s_initialized; /* Status - non-zero if initialized */
+ int ts_s_trimming; /* non-zero if trimming in progress */
+ PRLock *ts_s_trim_mutex; /* protects ts_s_trimming */
+} trim_status;
+static trim_status ts = {0L, 0L, 0, 0, NULL};
+
+/*
+ * All standard changeLogEntry attributes (initialized in get_cleattrs)
+ */
+static const char *cleattrs[ 10 ] = { NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL };
+
+static int retrocl_trimming = 0;
+static Slapi_Eq_Context retrocl_trim_ctx = NULL;
+
+/*
+ * Function: get_cleattrs
+ *
+ * Returns: an array of pointers to attribute names.
+ *
+ * Arguments: None.
+ *
+ * Description: Initializes, if necessary, and returns an array of char *s
+ * with attribute names used for retrieving changeLogEntry
+ * entries from the directory.
+ */
+static const char **get_cleattrs(void)
+{
+ if ( cleattrs[ 0 ] == NULL ) {
+ cleattrs[ 0 ] = attr_objectclass;
+ cleattrs[ 1 ] = attr_changenumber;
+ cleattrs[ 2 ] = attr_targetdn;
+ cleattrs[ 3 ] = attr_changetype;
+ cleattrs[ 4 ] = attr_newrdn;
+ cleattrs[ 5 ] = attr_deleteoldrdn;
+ cleattrs[ 6 ] = attr_changes;
+ cleattrs[ 7 ] = attr_newsuperior;
+ cleattrs[ 8 ] = attr_changetime;
+ cleattrs[ 9 ] = NULL;
+ }
+ return cleattrs;
+}
+
+/*
+ * Function: delete_changerecord
+ *
+ * Returns: LDAP_ error code
+ *
+ * Arguments: the number of the change to delete
+ *
+ * Description:
+ *
+ */
+
+static int
+delete_changerecord( changeNumber cnum )
+{
+ Slapi_PBlock *pb;
+ char *dnbuf;
+ int delrc;
+
+ dnbuf = slapi_ch_malloc( strlen( attr_changenumber ) + 20 +
+ strlen(RETROCL_CHANGELOG_DN));
+ /* Delete the record */
+ sprintf( dnbuf, "%s=%ld, %s", attr_changenumber, cnum,
+ RETROCL_CHANGELOG_DN);
+ pb = slapi_pblock_new ();
+ slapi_delete_internal_set_pb ( pb, dnbuf, NULL /*controls*/, NULL /* uniqueid */,
+ g_plg_identity[PLUGIN_RETROCL], 0 /* actions */ );
+ slapi_delete_internal_pb (pb);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &delrc );
+ slapi_pblock_destroy( pb );
+
+ if ( delrc != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "delete_changerecord: could not delete "
+ "change record %d\n", cnum );
+ } else {
+ slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "delete_changerecord: deleted changelog entry \"%s\"\n", dnbuf);
+ }
+ slapi_ch_free((void **) &dnbuf );
+ return delrc;
+}
+
+/*
+ * Function: handle_getchangerecord_result
+ * Arguments: op - pointer to Operation struct for this operation
+ * err - error code returned from search
+ * Returns: nothing
+ * Description: result handler for get_changerecord(). Sets the crt_err
+ * field of the cnum_result_t struct to the error returned
+ * from the backend.
+ */
+static void
+handle_getchangerecord_result( int err, void *callback_data )
+{
+ cnum_result_t *crt = callback_data;
+
+ if ( crt == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
+ "handle_getchangerecord_result: callback_data NULL\n" );
+ } else {
+ crt->crt_err = err;
+ }
+}
+
+/*
+ * Function: handle_getchangerecord_search
+ * Arguments: op - pointer to Operation struct for this operation
+ * e - entry returned by backend
+ * Returns: 0 in all cases
+ * Description: Search result operation handler for get_changerecord().
+ * Sets fields in the cnum_result_t struct pointed to by
+ * op->o_handler_data.
+ */
+static int
+handle_getchangerecord_search( Slapi_Entry *e, void *callback_data)
+{
+ cnum_result_t *crt = callback_data;
+
+ if ( crt == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
+ "handle_getchangerecord_search: op->o_handler_data NULL\n" );
+ } else if ( crt->crt_nentries > 0 ) {
+ /* only return the first entry, I guess */
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
+ "handle_getchangerecord_search: multiple entries returned\n" );
+ } else {
+ crt->crt_nentries++;
+ crt->crt_entry = e;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Function: get_changerecord
+ * Arguments: cnum - number of change record to retrieve
+ * Returns: Pointer to an entry structure. The caller must free the entry.
+ * If "err" is non-NULL, an error code is returned in the memory
+ * location it points to.
+ * Description: Retrieve the change record entry whose number is "cnum".
+ */
+static Slapi_Entry *get_changerecord( changeNumber cnum, int *err )
+{
+ cnum_result_t crt, *crtp = &crt;
+ char fstr[ 16 + CNUMSTR_LEN + 2 ];
+ Slapi_PBlock *pb;
+
+ if ( cnum == 0UL ) {
+ if ( err != NULL ) {
+ *err = LDAP_PARAM_ERROR;
+ }
+ return NULL;
+ }
+ crtp->crt_nentries = crtp->crt_err = 0; crtp->crt_entry = NULL;
+ sprintf( fstr, "%s=%ld", attr_changenumber, cnum );
+
+ pb = slapi_pblock_new ();
+ slapi_search_internal_set_pb (pb, RETROCL_CHANGELOG_DN,
+ LDAP_SCOPE_SUBTREE, fstr,
+ (char **)get_cleattrs(), /* cast const */
+ 0 /* attrsonly */,
+ NULL /* controls */, NULL /* uniqueid */,
+ g_plg_identity[PLUGIN_RETROCL],
+ 0 /* actions */);
+
+ slapi_search_internal_callback_pb (pb, crtp,
+ handle_getchangerecord_result,
+ handle_getchangerecord_search, NULL );
+ if ( err != NULL ) {
+ *err = crtp->crt_err;
+ }
+
+ slapi_pblock_destroy (pb);
+
+ return( crtp->crt_entry );
+}
+
+/*
+ * Function: trim_changelog
+ *
+ * Arguments: none
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Description: Trims the changelog, according to the constraints
+ * described by the ts structure.
+ */
+static int trim_changelog(void)
+{
+ int rc = 0, ldrc, done;
+ time_t now;
+ changeNumber first_in_log = 0, last_in_log = 0;
+ Slapi_Entry *e = NULL;
+ int num_deleted = 0;
+ int me,lt;
+
+
+ now = current_time();
+
+ PR_Lock( ts.ts_s_trim_mutex );
+ me = ts.ts_c_max_age;
+ lt = ts.ts_s_last_trim;
+ PR_Unlock( ts.ts_s_trim_mutex );
+
+ if ( now - lt >= (CHANGELOGDB_TRIM_INTERVAL / 1000) ) {
+
+ /*
+ * Trim the changelog. Read sequentially through all the
+ * entries, deleting any which do not meet the criteria
+ * described in the ts structure.
+ */
+ done = 0;
+
+ while ( !done && retrocl_trimming == 1 ) {
+ int did_delete;
+ Slapi_Attr *attr;
+
+ did_delete = 0;
+ first_in_log = retrocl_get_first_changenumber();
+ if ( 0UL == first_in_log ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "trim_changelog: no changelog records "
+ "to trim\n" );
+ /* Bail out - we can't do any useful work */
+ break;
+ }
+
+ last_in_log = retrocl_get_last_changenumber();
+ if ( last_in_log == first_in_log ) {
+ /* Always leave at least one entry in the change log */
+ break;
+ }
+ if ( me > 0L ) {
+ e = get_changerecord( first_in_log, &ldrc );
+ if ( NULL != e ) {
+ Slapi_Value *sval = NULL;
+ const struct berval *val = NULL;
+ rc = slapi_entry_attr_find( e, attr_changetime, &attr );
+ /* Bug 624442: Logic checking for lack of timestamp was
+ reversed. */
+ if ( 0 != rc || slapi_attr_first_value( attr,&sval ) == -1 ||
+ (val = slapi_value_get_berval ( sval )) == NULL ||
+ NULL == val->bv_val ) {
+ /* What to do if there's no timestamp? Just delete it. */
+ retrocl_set_first_changenumber( first_in_log + 1 );
+ ldrc = delete_changerecord( first_in_log );
+ num_deleted++;
+ did_delete = 1;
+ } else {
+ time_t change_time = parse_localTime( val->bv_val );
+ if ( change_time + me < now ) {
+ retrocl_set_first_changenumber( first_in_log + 1 );
+ ldrc = delete_changerecord( first_in_log );
+ num_deleted++;
+ did_delete = 1;
+ }
+ /* slapi_entry_free( e ); */ /* XXXggood should we be freeing this? */
+ }
+ }
+ }
+ if ( !did_delete ) {
+ done = 1;
+ }
+ }
+ } else {
+ LDAPDebug(LDAP_DEBUG_PLUGIN, "not yet time to trim: %d < (%d+%d)\n",
+ now,lt,(CHANGELOGDB_TRIM_INTERVAL/1000));
+ }
+ PR_Lock( ts.ts_s_trim_mutex );
+ ts.ts_s_trimming = 0;
+ ts.ts_s_last_trim = now;
+ PR_Unlock( ts.ts_s_trim_mutex );
+ if ( num_deleted > 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "trim_changelog: removed %d change records\n",
+ num_deleted );
+ }
+ return rc;
+}
+
+static int retrocl_active_threads;
+
+/*
+ * Function: changelog_trim_thread_fn
+ *
+ * Returns: nothing
+ *
+ * Arguments: none
+ *
+ * Description: the thread creation callback. retrocl_active_threads is
+ * provided for debugging purposes.
+ *
+ */
+
+static void
+changelog_trim_thread_fn( void *arg )
+{
+ PR_AtomicIncrement(&retrocl_active_threads);
+ trim_changelog();
+ PR_AtomicDecrement(&retrocl_active_threads);
+}
+
+
+
+/*
+ * Function: retrocl_housekeeping
+ * Arguments: cur_time - the current time
+ * Returns: nothing
+ * Description: Determines if it is time to trim the changelog database,
+ * and if so, determines if the changelog database needs to
+ * be trimmed. If so, a thread is started which will trim
+ * the database.
+ */
+
+void retrocl_housekeeping ( time_t cur_time, void *noarg )
+{
+ static time_t thread_start_time;
+ int ldrc;
+
+ if (retrocl_be_changelog == NULL) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"not housekeeping if no cl be\n",0,0,0);
+ return;
+ }
+
+ if ( !ts.ts_s_initialized ) {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "changelog_housekeeping called before "
+ "trimming constraints set\n" );
+ return;
+ }
+
+ PR_Lock( ts.ts_s_trim_mutex );
+ if ( !ts.ts_s_trimming ) {
+ int must_trim = 0;
+ /* See if we need to trim */
+ /* Has enough time elapsed since our last check? */
+ if ( cur_time - ts.ts_s_last_trim >= (ts.ts_c_max_age) ) {
+ /* Is the first entry too old? */
+ time_t first_time;
+ /*
+ * good we could avoid going to the database to retrieve
+ * this time information if we cached the last value we'd read.
+ * But a client might have deleted it over protocol.
+ */
+ first_time = retrocl_getchangetime( SLAPI_SEQ_FIRST, &ldrc );
+ LDAPDebug(LDAP_DEBUG_PLUGIN,
+ "cltrim: ldrc=%d, first_time=%d, cur_time=%d\n",
+ ldrc,first_time,cur_time);
+ if ( LDAP_SUCCESS == ldrc && first_time > (time_t) 0L &&
+ first_time + ts.ts_c_max_age < cur_time ) {
+ must_trim = 1;
+ }
+ }
+ if ( must_trim ) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"changelog about to create thread\n",0,0,0);
+ /* Start a thread to trim the changelog */
+ thread_start_time = cur_time;
+ ts.ts_s_trimming = 1;
+ if ( PR_CreateThread( PR_USER_THREAD,
+ changelog_trim_thread_fn, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD,
+ RETROCL_DLL_DEFAULT_THREAD_STACKSIZE ) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "unable to create changelog trimming thread\n" );
+ }
+ } else {
+ LDAPDebug(LDAP_DEBUG_PLUGIN,
+ "changelog does not need to be trimmed\n",0,0,0);
+ }
+ }
+ PR_Unlock( ts.ts_s_trim_mutex );
+}
+
+
+/*
+ * Function: age_str2time
+ *
+ * Returns: time_t
+ *
+ * Arguments: string representation of age (digits and unit s,m,h,d or w)
+ *
+ * Description:
+ * convert time from string like 1h (1 hour) to corresponding time in seconds
+ *
+ */
+
+static time_t
+age_str2time (const char *age)
+{
+ char *maxage;
+ char unit;
+ time_t ageval;
+
+ if (age == NULL || age[0] == '\0' || strcmp (age, "0") == 0) {
+ return 0;
+ }
+
+ maxage = slapi_ch_strdup ( age );
+ unit = maxage[ strlen( maxage ) - 1 ];
+ maxage[ strlen( maxage ) - 1 ] = '\0';
+ ageval = strntoul( maxage, strlen( maxage ), 10 );
+ if ( maxage) {
+ slapi_ch_free ( (void **) &maxage );
+ }
+ switch ( unit ) {
+ case 's':
+ break;
+ case 'm':
+ ageval *= 60;
+ break;
+ case 'h':
+ ageval *= ( 60 * 60 );
+ break;
+ case 'd':
+ ageval *= ( 24 * 60 * 60 );
+ break;
+ case 'w':
+ ageval *= ( 7 * 24 * 60 * 60 );
+ break;
+ default:
+ slapi_log_error( SLAPI_LOG_PLUGIN, "retrocl",
+ "age_str2time: unknown unit \"%c\" "
+ "for maxiumum changelog age\n", unit );
+ ageval = -1;
+ }
+
+ return ageval;
+}
+
+/*
+ * Function: retrocl_init_trimming
+ *
+ * Returns: none, exits on fatal error
+ *
+ * Arguments: none
+ *
+ * Description: called during startup
+ *
+ */
+
+void retrocl_init_trimming (void)
+{
+ const char *cl_maxage;
+ time_t ageval;
+
+ cl_maxage = retrocl_get_config_str(CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE);
+
+ if (cl_maxage == NULL) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"No maxage, not trimming retro changelog.\n",0,0,0);
+ return;
+ }
+ ageval = age_str2time (cl_maxage);
+ slapi_ch_free ((void **)&cl_maxage);
+
+ ts.ts_c_max_age = ageval;
+ ts.ts_s_last_trim = (time_t) 0L;
+ ts.ts_s_trimming = 0;
+ if (( ts.ts_s_trim_mutex = PR_NewLock()) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "set_changelog_trim_constraints: "
+ "cannot create new lock.\n" );
+ exit( 1 );
+ }
+ ts.ts_s_initialized = 1;
+ retrocl_trimming = 1;
+
+ retrocl_trim_ctx = slapi_eq_repeat(retrocl_housekeeping,
+ NULL,(time_t)0,
+ CHANGELOGDB_TRIM_INTERVAL);
+
+}
+
+/*
+ * Function: retrocl_stop_trimming
+ *
+ * Returns: none
+ *
+ * Arguments: none
+ *
+ * Description: called when server is shutting down to ensure trimming stops
+ * eventually.
+ *
+ */
+
+void retrocl_stop_trimming(void)
+{
+ retrocl_trimming = 0;
+ if (retrocl_trim_ctx) {
+ slapi_eq_cancel(retrocl_trim_ctx);
+ retrocl_trim_ctx = NULL;
+ }
+}
+