Author: prowley
Update of /cvs/dirsec/ldapserver/ldap/servers/plugins/dna
In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv24292/ldap/servers/plugins/dna
Added Files:
Makefile addentries.sh config.sh del_test_entries.dns
delentries.sh dna.c dna.ldif editentries.sh oneentry.sh
posix.ldif posix_one.ldif posix_test.ldif seeconfig.sh
seeentries.sh subtest.ldif
Log Message:
Distributed numeric assignment plugin - initial check in
--- NEW FILE Makefile ---
#
# BEGIN COPYRIGHT BLOCK
# This Program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; version 2 of the License.
#
# This Program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA.
#
# In addition, as a special exception, Red Hat, Inc. gives You the additional
# right to link the code of this Program with code not covered under the GNU
# General Public License ("Non-GPL Code") and to distribute linked combinations
# including the two, subject to the limitations in this paragraph. Non-GPL Code
# permitted under this exception must only link to the code of this Program
# through those well defined interfaces identified in the file named EXCEPTION
# found in the source code files (the "Approved Interfaces"). The files of
# Non-GPL Code may instantiate templates or use macros or inline functions from
# the Approved Interfaces without causing the resulting work to be covered by
# the GNU General Public License. Only Red Hat, Inc. may make changes or
# additions to the list of Approved Interfaces. You must obey the GNU General
# Public License in all respects for all of the Program code and other code used
# in conjunction with the Program except the Non-GPL Code covered by this
# exception. If you modify this file, you may extend this exception to your
# version of the file, but you are not obligated to do so. If you do not wish to
# provide this exception without modification, you must delete this exception
# statement from your version and license this file solely under the GPL without
# exception.
#
#
# Copyright (C) 2007 Red Hat, Inc.
# All rights reserved.
# END COPYRIGHT BLOCK
#
LDAP_SRC = ../../..
BUILD_ROOT = ../../../..
NOSTDCLEAN=true # don't let nsconfig.mk define target clean
NOSTDSTRIP=true # don't let nsconfig.mk define target strip
OBJDEST = $(OBJDIR)/lib/libdna
LIBDIR = $(LDAP_PLUGIN_RELDIR)
include $(BUILD_ROOT)/nsdefs.mk
include $(BUILD_ROOT)/nsconfig.mk
include $(LDAP_SRC)/nsldap.mk
ifeq ($(ARCH), WINNT)
DEF_FILE:=./dna.def
endif
DNA_OBJS = dna.o
OBJS = $(addprefix $(OBJDEST)/, $(DNA_OBJS))
DNA_DLL = libdna-plugin
INCLUDES += -I../http -I../../slapd -I../../../include
CFLAGS+=$(SLCFLAGS) -DSLAPD_LOGGING
ifeq ($(ARCH), WINNT)
EXTRA_LIBS_DEP += $(LIBSLAPD) $(NSPR_DEP) $(LDAPSDK_DEP)
EXTRA_LIBS_DEP += $(LDAP_COMMON_LIBS_DEP)
EXTRA_LIBS += $(NSPRLINK) $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL)
EXTRA_LIBS += $(LDAP_COMMON_LIBS)
DNA_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
endif
ifeq ($(ARCH), AIX)
EXTRA_LIBS_DEP += $(LIBSLAPD) $(NSPR_DEP) $(LDAPSDK_DEP)
EXTRA_LIBS_DEP += $(LDAP_COMMON_LIBS_DEP)
EXTRA_LIBS += $(LIBSLAPDLINK) $(NSPRLINK) $(LDAP_SDK_LIBLDAP_DLL)
EXTRA_LIBS += $(LDAP_COMMON_LIBS)
LD=ld
endif
ifeq ($(ARCH), HPUX)
EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
EXTRA_LIBS_DEP += $(LDAP_COMMON_LIBS_DEP)
EXTRA_LIBS += $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
EXTRA_LIBS += $(LDAP_COMMON_LIBS)
endif
DNA= $(addprefix $(LIBDIR)/, $(DNA_DLL).$(DLL_SUFFIX))
clientSDK:
all: $(OBJDEST) $(LIBDIR) $(DNA)
ifeq ($(ARCH), WINNT)
$(DNA): $(OBJS) $(DNA_DLL_OBJ) $(DEF_FILE)
$(LINK_DLL) $(DNA_DLL_OBJ) $(EXTRA_LIBS) /DEF:$(DEF_FILE)
else
$(DNA): $(OBJS) $(DNA_DLL_OBJ)
$(LINK_DLL) $(DNA_DLL_OBJ) $(EXTRA_LIBS)
endif
veryclean: clean
clean:
$(RM) $(OBJS)
ifeq ($(ARCH), WINNT)
$(RM) $(DNA_DLL_OBJ)
endif
$(RM) $(DNA)
$(OBJDEST):
$(MKDIR) $(OBJDEST)
$(LIBDIR):
$(MKDIR) $(LIBDIR)
--- NEW FILE addentries.sh ---
#!/bin/sh
ldapadd -x -D "cn=Directory Manager" -w secret123 -h localhost -p 3389 -f
posix_test.ldif -c
--- NEW FILE config.sh ---
#!/bin/sh
ldapadd -x -D "cn=Directory Manager" -w secret123 -h localhost -p 3389 -f
dna.ldif -c
ldapadd -x -D "cn=Directory Manager" -w secret123 -h localhost -p 3389 -f
posix.ldif -c
ldapadd -x -D "cn=Directory Manager" -w secret123 -h localhost -p 3389 -f
subtest.ldif -c
--- NEW FILE del_test_entries.dns ---
uid=add_has_magic_number, dc=example, dc=com
uid=add_second_has_magic_number, dc=example, dc=com
uid=no_uid_number, dc=example, dc=com
uid=add_has_uid_number_550, dc=example, dc=com
uid=add_is_sub, ou=sub, dc=example, dc=com
ou=sub, dc=example, dc=com
--- NEW FILE delentries.sh ---
#!/bin/sh
ldapdelete -x -D "cn=Directory Manager" -w secret123 -h localhost -p 3389 -f
del_test_entries.dns -c
--- NEW FILE dna.c ---
/** BEGIN COPYRIGHT BLOCK
* This Program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; version 2 of the License.
*
* This Program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA.
*
* In addition, as a special exception, Red Hat, Inc. gives You the additional
* right to link the code of this Program with code not covered under the GNU
* General Public License ("Non-GPL Code") and to distribute linked
combinations
* including the two, subject to the limitations in this paragraph. Non-GPL Code
* permitted under this exception must only link to the code of this Program
* through those well defined interfaces identified in the file named EXCEPTION
* found in the source code files (the "Approved Interfaces"). The files of
* Non-GPL Code may instantiate templates or use macros or inline functions from
* the Approved Interfaces without causing the resulting work to be covered by
* the GNU General Public License. Only Red Hat, Inc. may make changes or
* additions to the list of Approved Interfaces. You must obey the GNU General
* Public License in all respects for all of the Program code and other code used
* in conjunction with the Program except the Non-GPL Code covered by this
* exception. If you modify this file, you may extend this exception to your
* version of the file, but you are not obligated to do so. If you do not wish to
* provide this exception without modification, you must delete this exception
* statement from your version and license this file solely under the GPL without
* exception.
*
*
* Copyright (C) 2007 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
/**
* Distributed Numeric Assignment plug-in
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "portable.h"
#include "nspr.h"
#include "slapi-private.h"
#include "dirlite_strings.h"
#include "dirver.h"
#include "prclist.h"
#include "ldif.h"
/* get file mode flags for unix */
#ifndef _WIN32
#include <sys/stat.h>
#endif
#define DNA_PLUGIN_SUBSYSTEM "dna-plugin"
#define DNA_PLUGIN_VERSION 0x00010000
#define DNA_DN "cn=Distributed Numeric Assignment
Plugin,cn=plugins,cn=config" /* temporary */
#define DNA_SUCCESS 0
#define DNA_FAILURE -1
/**
* DNA config types
*/
#define DNA_TYPE "dnaType"
#define DNA_PREFIX "dnaPrefix"
#define DNA_NEXTVAL "dnaNextValue"
#define DNA_INTERVAL "dnaInterval"
#define DNA_GENERATE "dnaMagicRegen"
#define DNA_FILTER "dnaFilter"
#define DNA_SCOPE "dnaScope"
#define FEATURE_DESC "Distributed Numeric Assignment"
#define PLUGIN_DESC "Distributed Numeric Assignment plugin"
static Slapi_PluginDesc pdesc = { FEATURE_DESC,
PLUGIN_MAGIC_VENDOR_STR,
PRODUCTTEXT,
PLUGIN_DESC };
/**
* linked list of config entries
*/
struct _defs {
PRCList list;
char *dn;
char *type;
char *prefix;
unsigned long nextval;
unsigned long interval;
struct slapi_filter *filter;
char *generate;
char *scope;
} dna_anchor;
typedef struct _defs configEntry;
static PRCList *config;
static PRRWLock *g_dna_cache_lock;
static void *_PluginID = NULL;
static char *_PluginDN = NULL;
/*
* new value lock
*/
static Slapi_Mutex *g_new_value_lock;
/**
*
* DNA plug-in management functions
*
*/
int dna_init(Slapi_PBlock *pb);
static int dna_start(Slapi_PBlock *pb);
static int dna_close(Slapi_PBlock *pb);
static int dna_postop_init(Slapi_PBlock *pb);
/**
*
* Local operation functions
*
*/
static int loadPluginConfig();
static int parseConfigEntry(Slapi_Entry *e);
static void deleteConfig();
static void freeConfigEntry(configEntry **entry);
/**
*
* helpers
*
*/
static char *dna_get_dn(Slapi_PBlock *pb);
static int dna_dn_is_config(char *dn);
static int dna_get_next_value(configEntry *config_entry, char **next_value_ret);
/**
*
* the ops (where the real work is done)
*
*/
static int dna_config_check_post_op(Slapi_PBlock *pb);
static int dna_pre_op( Slapi_PBlock *pb, int modtype );
static int dna_mod_pre_op( Slapi_PBlock *pb );
static int dna_add_pre_op( Slapi_PBlock *pb );
/**
* debug functions - global, for the debugger
*/
void dnaDumpConfig();
void dnaDumpConfigEntry(configEntry *);
/**
* set the debug level
*/
#ifdef _WIN32
int *module_ldap_debug = 0;
void plugin_init_debug_level(int *level_ptr)
{
module_ldap_debug = level_ptr;
}
#endif
/**
*
* Deal with cache locking
*
*/
void dna_read_lock()
{
PR_RWLock_Rlock(g_dna_cache_lock);
}
void dna_write_lock()
{
PR_RWLock_Wlock(g_dna_cache_lock);
}
void dna_unlock()
{
PR_RWLock_Unlock(g_dna_cache_lock);
}
/**
*
* Get the dna plug-in version
*
*/
int dna_version()
{
return DNA_PLUGIN_VERSION;
}
/**
* Plugin identity mgmt
*/
void setPluginID(void * pluginID)
{
_PluginID=pluginID;
}
void * getPluginID()
{
return _PluginID;
}
void setPluginDN(char *pluginDN)
{
_PluginDN = pluginDN;
}
char * getPluginDN()
{
return _PluginDN;
}
/*
dna_init
-------------
adds our callbacks to the list
*/
int dna_init( Slapi_PBlock *pb )
{
int status = DNA_SUCCESS;
char * plugin_identity=NULL;
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "--> dna_init\n");
/**
* Store the plugin identity for later use.
* Used for internal operations
*/
slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
PR_ASSERT (plugin_identity);
setPluginID(plugin_identity);
if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
SLAPI_PLUGIN_VERSION_01 ) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
(void *) dna_start ) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
(void *) dna_close ) != 0 ||
slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
(void *)&pdesc ) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN,
(void *) dna_mod_pre_op ) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN,
(void *) dna_add_pre_op ) != 0 ||
/* the config change checking post op */
slapi_register_plugin(
"postoperation", /* op type */
1, /* Enabled */
"dna_init", /* this function desc */
dna_postop_init, /* init func for post op */
PLUGIN_DESC, /* plugin desc */
NULL, /* ? */
plugin_identity /* access control */
)
)
{
slapi_log_error( SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
"dna_init: failed to register plugin\n" );
status = DNA_FAILURE;
}
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "<-- dna_init\n");
return status;
}
static int dna_postop_init(Slapi_PBlock *pb)
{
int status = DNA_SUCCESS;
if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
SLAPI_PLUGIN_VERSION_01 ) != 0 ||
slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
(void *)&pdesc ) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN,
(void *) dna_config_check_post_op ) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN,
(void *) dna_config_check_post_op ) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN,
(void *) dna_config_check_post_op ) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN,
(void *) dna_config_check_post_op ) != 0
)
{
slapi_log_error( SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
"dna_postop_init: failed to register plugin\n" );
status = DNA_FAILURE;
}
return status;
}
/*
dna_start
--------------
Kicks off the config cache.
It is called after dna_init.
*/
static int dna_start( Slapi_PBlock *pb )
{
char * plugindn = NULL;
char * httpRootDir = NULL;
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "-->
dna_start\n");
config = &dna_anchor.list;
g_dna_cache_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "dna");
g_new_value_lock = slapi_new_mutex();
if(!g_dna_cache_lock || !g_new_value_lock)
{
slapi_log_error( SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
"dna_start: lock creation failed\n" );
return DNA_FAILURE;
}
/**
* Get the plug-in target dn from the system
* and store it for future use. This should avoid
* hardcoding of DN's in the code.
*/
slapi_pblock_get(pb, SLAPI_TARGET_DN, &plugindn);
if (plugindn == NULL || strlen(plugindn) == 0)
{
slapi_log_error( SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM ,
"dna_start: had to use hard coded config dn\n");
plugindn = DNA_DN;
}
else
{
slapi_log_error( SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM ,
"dna_start: config at %s\n", plugindn);
}
setPluginDN(plugindn);
/**
* Load the config for our plug-in
*/
PR_INIT_CLIST(config);
if (loadPluginConfig() != DNA_SUCCESS)
{
slapi_log_error( SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
"dna_start: unable to load plug-in configuration\n" );
return DNA_FAILURE;
}
slapi_log_error( SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM , "dna: ready for
service\n");
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "<--
dna_start\n");
return DNA_SUCCESS;
}
/*
dna_close
--------------
closes down the cache
*/
static int dna_close( Slapi_PBlock *pb )
{
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "-->
dna_close\n");
deleteConfig();
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "<--
dna_close\n");
return DNA_SUCCESS;
}
/*
* config looks like this
* - cn=myplugin
* --- ou=posix
* ------ cn=accounts
* ------ cn=groups
* --- cn=samba
* --- cn=etc
* ------ cn=etc etc
*/
static int loadPluginConfig()
{
int status = DNA_SUCCESS;
int result;
int i;
Slapi_PBlock *search_pb;
Slapi_Entry **entries = NULL;
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "-->
loadPluginConfig\n");
dna_write_lock();
deleteConfig();
search_pb = slapi_pblock_new();
slapi_search_internal_set_pb(search_pb, DNA_DN, LDAP_SCOPE_SUBTREE,
"objectclass=*", NULL, 0, NULL, NULL, getPluginID(), 0);
slapi_search_internal_pb(search_pb);
slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
if (status != DNA_SUCCESS)
{
status = DNA_SUCCESS;
goto cleanup;
}
slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
if (NULL == entries || entries[0] == NULL)
{
status = DNA_SUCCESS;
goto cleanup;
}
for (i = 0; (entries[i] != NULL); i++)
{
status = parseConfigEntry(entries[i]);
}
cleanup:
slapi_free_search_results_internal(search_pb);
slapi_pblock_destroy(search_pb);
dna_unlock();
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "<--
loadPluginConfig\n");
return status;
}
static int parseConfigEntry(Slapi_Entry *e)
{
char *key = NULL;
char *value = NULL;
configEntry *entry = NULL;
configEntry *config_entry = NULL;
Slapi_Attr *attr = NULL;
PRCList *list = NULL;
int entry_added = 0;
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "-->
parseConfigEntry\n");
entry = (configEntry*) slapi_ch_calloc(1, sizeof(configEntry));
if(0 == entry)
goto bail;
value = slapi_entry_get_ndn(e);
if(value) {
entry->dn = strdup(value);
}
slapi_log_error( SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM , "----------> dn
[%s] \n",entry->dn,0,0);
value = slapi_entry_attr_get_charptr(e, DNA_TYPE);
if(value) {
entry->type = value;
}
else
goto bail;
slapi_log_error( SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM , "----------> dnaType
[%s] \n",entry->type,0,0);
value = slapi_entry_attr_get_charptr(e, DNA_NEXTVAL);
if (value) {
entry->nextval = strtoul(value,0,0);
slapi_ch_free_string(&value);
value = 0;
}
else
goto bail;
slapi_log_error( SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM , "---------->
dnaNextValue [%d] \n",entry->nextval,0,0);
value = slapi_entry_attr_get_charptr(e, DNA_PREFIX);
if (value) {
entry->prefix = value;
}
slapi_log_error( SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM , "----------> dnaPrefix
[%s] \n",entry->prefix,0,0);
value = slapi_entry_attr_get_charptr(e, DNA_INTERVAL);
if (value) {
entry->interval = strtoul(value,0,0);
slapi_ch_free_string(&value);
value = 0;
}
else
goto bail;
slapi_log_error( SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM , "---------->
dnaInterval [%s] \n",value,0,0);
value = slapi_entry_attr_get_charptr(e, DNA_GENERATE);
if (value) {
entry->generate = value;
}
slapi_log_error( SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM , "---------->
dnaMagicRegen [%s] \n",entry->generate,0,0);
value = slapi_entry_attr_get_charptr(e, DNA_FILTER);
if (value) {
entry->filter = slapi_str2filter(value);
}
else
goto bail;
slapi_log_error( SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM , "----------> dnaFilter
[%s] \n",value,0,0);
slapi_ch_free_string(&value);
value = 0;
value = slapi_entry_attr_get_charptr(e, DNA_SCOPE);
if (value) {
char *canonical_dn = slapi_dn_normalize(value);
entry->scope = canonical_dn;
}
slapi_log_error( SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM , "---------->
dnaScope [%s] \n",entry->scope,0,0);
/**
* Finally add the entry to the list
* we group by type then by filter
* and finally sort by dn length with longer dn's
* first - this allows the scope checking
* code to be simple and quick and
* cunningly linear
*/
if(!PR_CLIST_IS_EMPTY(config))
{
list = PR_LIST_HEAD(config);
while(list != config)
{
config_entry = (configEntry*)list;
if(slapi_attr_type_cmp(config_entry->type, entry->type,1))
goto next;
if(slapi_filter_compare(config_entry->filter, entry->filter))
goto next;
if(slapi_dn_issuffix(entry->scope,config_entry->scope))
{
PR_INSERT_BEFORE(&(entry->list), list);
slapi_log_error( SLAPI_LOG_CONFIG,
DNA_PLUGIN_SUBSYSTEM ,
"store [%s] before [%s] \n",entry->scope,config_entry->scope,0);
entry_added = 1;
break;
}
next:
list = PR_NEXT_LINK (list);
if(config == list)
{
/* add to tail */
PR_INSERT_BEFORE(&(entry->list), list);
slapi_log_error( SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM , "store [%s] at
tail\n",entry->scope,0,0);
entry_added = 1;
break;
}
}
}
else
{
/* first entry */
PR_INSERT_LINK(&(entry->list), config);
slapi_log_error( SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM , "store [%s]
at head \n",entry->scope,0,0);
entry_added = 1;
}
bail:
if(0 == entry_added)
{
slapi_log_error( SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM ,
"config entry [%s] skipped\n",entry->dn,0,0);
freeConfigEntry(&entry);
}
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "<--
parseConfigEntry\n");
return DNA_SUCCESS;
}
static void freeConfigEntry(configEntry **entry)
{
configEntry *e = *entry;
if(e->dn)
{
slapi_log_error( SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM ,
"freeing config entry [%s]\n",e->dn,0,0);
slapi_ch_free_string(&e->dn);
}
if(e->type)
slapi_ch_free_string(&e->type);
if(e->prefix)
slapi_ch_free_string(&e->prefix);
if(e->filter)
slapi_filter_free(e->filter,1);
if(e->generate)
slapi_ch_free_string(&e->generate);
if(e->scope)
slapi_ch_free_string(&e->scope);
slapi_ch_free((void**)entry);
}
static void deleteConfigEntry(PRCList *entry)
{
PR_REMOVE_LINK(entry);
freeConfigEntry((configEntry**)&entry);
}
static void deleteConfig()
{
PRCList *list;
while(!PR_CLIST_IS_EMPTY(config))
{
list = PR_LIST_HEAD(config);
deleteConfigEntry(list);
}
return;
}
/****************************************************
Helpers
****************************************************/
static char *dna_get_dn(Slapi_PBlock *pb)
{
char *dn = 0;
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "-->
dna_get_dn\n");
if(slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn ))
{
slapi_log_error( SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM, "dna_get_dn:
failed to get dn of changed entry");
goto bail;
}
/* slapi_dn_normalize( dn );
*/
bail:
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "<--
dna_get_dn\n");
return dn;
}
/* config check
matching config dn or a descendent reloads config
*/
static int dna_dn_is_config(char *dn)
{
int ret = 0;
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "-->
dna_is_config\n");
if(slapi_dn_issuffix(dn, getPluginDN()))
{
ret=1;
}
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "<--
dna_is_config\n");
return ret;
}
/****************************************************
Functions that actually do things other
than config and startup
****************************************************/
/*
* Perform ldap operationally atomic increment
* Return the next value to be assigned
* Method:
* 1. retrieve entry
* 2. remove current value, add new value in one operation
* 3. if failed, and less than 3 times, goto 1
*/
static int dna_get_next_value(configEntry *config_entry, char **next_value_ret)
{
int ret = LDAP_SUCCESS;
Slapi_DN *dn = 0;
char *attrlist[3];
Slapi_Entry *e = 0;
int attempts = 0;
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "-->
dna_get_next_value\n");
/* get pre-requisites to search */
dn = slapi_sdn_new_dn_byref(config_entry->dn);
attrlist[0] = DNA_NEXTVAL;
attrlist[1] = DNA_INTERVAL;
attrlist[2] = 0;
/* the operation is constructed such that race conditions
* to increment the value are detected and avoided - one wins,
* one loses - however, there is no need for the server to compete
* with itself so we lock here
*/
slapi_lock_mutex(g_new_value_lock);
while(attempts < 3 && LDAP_SUCCESS == ret)
{
attempts++;
/* do update */
if(e)
{
slapi_entry_free(e);
e = 0;
}
ret = slapi_search_internal_get_entry( dn, attrlist, &e,getPluginID());
if(LDAP_SUCCESS == ret)
{
char *old_value;
old_value = slapi_entry_attr_get_charptr(e, DNA_NEXTVAL);
if(old_value)
{
LDAPMod mod_add;
LDAPMod mod_delete;
LDAPMod *mods[3];
Slapi_PBlock *pb = slapi_pblock_new();
char *delete_val[2];
char *add_val[2];
char new_value[16];
char *interval = 0;
mods[0] = &mod_delete;
mods[1] = &mod_add;
mods[2] = 0;
if(0 == pb)
goto bail;
interval = slapi_entry_attr_get_charptr(e, DNA_INTERVAL);
if(0 == interval)
{
slapi_pblock_destroy(pb);
slapi_ch_free_string(&old_value);
goto bail;
}
/* perform increment */
sprintf(new_value, "%lu",
strtoul(interval,0,0) +
strtoul(old_value,0,0));
delete_val[0] = old_value;
delete_val[1] = 0;
mod_delete.mod_op = LDAP_MOD_DELETE;
mod_delete.mod_type = DNA_NEXTVAL;
mod_delete.mod_values = delete_val;
add_val[0] = new_value;
add_val[1] = 0;
mod_add.mod_op = LDAP_MOD_ADD;
mod_add.mod_type = DNA_NEXTVAL;
mod_add.mod_values = add_val;
mods[0] = &mod_delete;
mods[1] = &mod_add;
mods[2] = 0;
slapi_modify_internal_set_pb(
pb, config_entry->dn,
mods, 0, 0,
getPluginID(), 0);
slapi_modify_internal_pb(pb);
slapi_pblock_get(pb,
SLAPI_PLUGIN_INTOP_RESULT,
&ret);
slapi_pblock_destroy(pb);
slapi_ch_free_string(&interval);
if(LDAP_SUCCESS == ret)
{
*next_value_ret = old_value;
break;
}
else
{
slapi_ch_free_string(&old_value);
if(LDAP_NO_SUCH_ATTRIBUTE != ret)
{
/* not the result of a race
to change the value
*/
break;
}
else
/* we lost the race to mod
try again
*/
ret = LDAP_SUCCESS;
}
}
else
break;
}
else
break;
}
bail:
slapi_unlock_mutex(g_new_value_lock);
if(dn)
slapi_sdn_free(&dn);
if(e)
slapi_entry_free(e);
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "<--
dna_get_next_value\n");
return ret;
}
/* for mods and adds:
where dn's are supplied, the closest in scope
is used as long as the type and filter
are identical - otherwise all matches count
*/
static int dna_pre_op(Slapi_PBlock *pb, int modtype)
{
char *dn = 0;
PRCList *list = 0;
configEntry *config_entry = 0;
struct slapi_entry *e = 0;
char *last_type = 0;
char *value = 0;
int generate = 0;
Slapi_Mods *smods = 0;
Slapi_Mod *smod = 0;
LDAPMod **mods;
int free_entry = 0;
int ret = 0;
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "-->
dna_pre_op\n");
if(0 == (dn = dna_get_dn(pb)))
goto bail;
if(dna_dn_is_config(dn))
goto bail;
if(LDAP_CHANGETYPE_ADD == modtype)
{
slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &e);
}
else
{
/* xxxPAR: Ideally SLAPI_MODIFY_EXISTING_ENTRY should be
* available but it turns out that is only true if you are
* a dbm backend pre-op plugin - lucky dbm backend pre-op
* plugins.
* I think that is wrong since the entry is useful for filter
* tests and schema checks and this plugin shouldn't be limited
* to a single backend type, but I don't want that fight right
* now so we go get the entry here
*
slapi_pblock_get( pb, SLAPI_MODIFY_EXISTING_ENTRY, &e);
*/
Slapi_DN *tmp_dn = slapi_sdn_new_dn_byref(dn);
if(tmp_dn)
{
slapi_search_internal_get_entry(
tmp_dn, 0, &e,getPluginID());
slapi_sdn_free(&tmp_dn);
free_entry = 1;
}
/* grab the mods - we'll put them back later with
* our modifications appended
*/
slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods);
smods = slapi_mods_new();
slapi_mods_init_passin(smods, mods);
}
if(0 == e)
goto bailmod;
dna_read_lock();
if(!PR_CLIST_IS_EMPTY(config))
{
list = PR_LIST_HEAD(config);
while(list != config && LDAP_SUCCESS == ret)
{
config_entry = (configEntry*)list;
/* did we already service this type? */
if(last_type)
{
if(!slapi_attr_type_cmp(config_entry->type,
last_type,1))
goto next;
}
/* is the entry in scope? */
if(config_entry->scope)
{
if(!slapi_dn_issuffix(dn, config_entry->scope))
goto next;
}
/* does the entry match the filter? */
if(config_entry->filter)
{
if(LDAP_SUCCESS != slapi_vattr_filter_test(pb,
e,
config_entry->filter,0))
goto next;
}
if(LDAP_CHANGETYPE_ADD == modtype)
{
/* does attribute contain the magic value
or is the type not there?
*/
value = slapi_entry_attr_get_charptr(
e, config_entry->type);
if((value &&
!slapi_utf8casecmp(
config_entry->generate,
value)) ||
0 == value)
{
generate = 1;
}
}
else
{
/* check mods for magic value */
Slapi_Mod *next_mod = slapi_mod_new();
smod = slapi_mods_get_first_smod(
smods,
next_mod);
while(smod)
{
char *type = (char *)
slapi_mod_get_type(smod);
if(slapi_attr_types_equivalent(
type,
config_entry->type))
{
struct berval *bv =
slapi_mod_get_first_value(
smod);
int len = strlen(
config_entry->
generate);
if(len == bv->bv_len)
{
if(!slapi_utf8ncasecmp(
bv->bv_val,
config_entry->
generate,
len))
generate = 1;
break;
}
}
slapi_mod_done(next_mod);
smod = slapi_mods_get_next_smod(
smods,
next_mod);
}
slapi_mod_free(&next_mod);
}
if(generate)
{
char *new_value;
int len;
/* create the value to add */
if(ret = dna_get_next_value(config_entry,&value))
break;
len = strlen(value) + 1;
if(config_entry->prefix)
{
len += strlen(config_entry->prefix);
}
new_value = slapi_ch_malloc(len);
if(config_entry->prefix)
{
strcpy(new_value,
config_entry->prefix);
strcat(new_value, value);
}
else
strcpy(new_value, value);
/* do the mod */
if(LDAP_CHANGETYPE_ADD == modtype)
{
/* add - add to entry */
slapi_entry_attr_set_charptr(
e,
config_entry->type,
new_value);
}
else
{
/* mod - add to mods */
slapi_mods_add_string(
smods,
LDAP_MOD_REPLACE,
config_entry->type,
new_value);
}
/* free up */
slapi_ch_free_string(&value);
slapi_ch_free_string(&new_value);
/* make sure we don't generate for this
* type again
*/
if(LDAP_SUCCESS == ret)
{
last_type = config_entry->type;
}
generate = 0;
}
next:
list = PR_NEXT_LINK (list);
}
}
dna_unlock();
bailmod:
if(LDAP_CHANGETYPE_MODIFY == modtype)
{
/* these are the mods you made, really,
* I didn't change them, honest, just had a quick look
*/
mods = slapi_mods_get_ldapmods_passout(smods);
slapi_pblock_set( pb, SLAPI_MODIFY_MODS, mods);
slapi_mods_free(&smods);
}
bail:
if(free_entry && e)
slapi_entry_free(e);
if(ret)
slapi_log_error( SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM , "dna_pre_op: operation
failure [%d]\n", ret);
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "<--
dna_pre_op\n");
return ret;
}
static int dna_add_pre_op( Slapi_PBlock *pb )
{
return dna_pre_op(pb, LDAP_CHANGETYPE_ADD);
}
static int dna_mod_pre_op( Slapi_PBlock *pb )
{
return dna_pre_op(pb, LDAP_CHANGETYPE_MODIFY);
}
static int dna_config_check_post_op(Slapi_PBlock *pb)
{
char *dn;
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "-->
dna_config_check_post_op\n");
if(dn = dna_get_dn(pb))
{
if(dna_dn_is_config(dn))
loadPluginConfig();
}
bail:
slapi_log_error( SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM , "<--
dna_config_check_post_op\n");
return 0;
}
/****************************************************
End of
Functions that actually do things other
than config and startup
****************************************************/
/**
* debug functions to print config
*/
void dnaDumpConfig()
{
PRCList *list;
dna_read_lock();
if(!PR_CLIST_IS_EMPTY(config))
{
list = PR_LIST_HEAD(config);
while(list != config)
{
dnaDumpConfigEntry((configEntry*)list);
list = PR_NEXT_LINK (list);
}
}
dna_unlock();
}
void dnaDumpConfigEntry(configEntry *entry)
{
printf("<- type --------------> %s\n", entry->type);
printf("<---- prefix ---------> %s\n", entry->prefix);
printf("<---- next value -----> %lu\n", entry->nextval);
printf("<---- interval -------> %lu\n", entry->interval);
printf("<---- filter ---------> %s\n", entry->filter);
printf("<---- generate flag --> %s\n", entry->generate);
}
--- NEW FILE dna.ldif ---
# --- BEGIN COPYRIGHT BLOCK ---
# This Program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; version 2 of the License.
#
# This Program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA.
#
# In addition, as a special exception, Red Hat, Inc. gives You the additional
# right to link the code of this Program with code not covered under the GNU
# General Public License ("Non-GPL Code") and to distribute linked combinations
# including the two, subject to the limitations in this paragraph. Non-GPL Code
# permitted under this exception must only link to the code of this Program
# through those well defined interfaces identified in the file named EXCEPTION
# found in the source code files (the "Approved Interfaces"). The files of
# Non-GPL Code may instantiate templates or use macros or inline functions from
# the Approved Interfaces without causing the resulting work to be covered by
# the GNU General Public License. Only Red Hat, Inc. may make changes or
# additions to the list of Approved Interfaces. You must obey the GNU General
# Public License in all respects for all of the Program code and other code used
# in conjunction with the Program except the Non-GPL Code covered by this
# exception. If you modify this file, you may extend this exception to your
# version of the file, but you are not obligated to do so. If you do not wish to
# provide this exception without modification, you must delete this exception
# statement from your version and license this file solely under the GPL without
# exception.
#
#
# Copyright (C) 2007 Red Hat, Inc.
# All rights reserved.
# --- END COPYRIGHT BLOCK ---
# plugin configuration entry
dn: cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
objectclass: top
objectclass: nsSlapdPlugin
objectclass: extensibleObject
objectclass: nsContainer
cn: Distributed Numeric Assignment Plugin
nsslapd-plugininitfunc: dna_init
nsslapd-plugintype: preoperation
nsslapd-pluginenabled: on
nsslapd-plugindescription: Distributed Numeric Assignment plugin
nsslapd-pluginvendor: Fedora Project
nsslapd-pluginVersion: 1.1
nsslapd-pluginId: distributed-numeric-assignment
nsslapd-pluginPath: /home/prowley/srv/lib/fedora-ds/plugins/libdna-plugin.so
--- NEW FILE editentries.sh ---
#!/bin/sh
ldapvi -D "cn=Directory Manager" -w secret123 -h localhost:3389 -b
"dc=example, dc=com" "(objectclass=posixaccount)"
--- NEW FILE oneentry.sh ---
#!/bin/sh
ldapadd -x -D "cn=Directory Manager" -w secret123 -h localhost -p 3389 -f
posix_one.ldif -c
--- NEW FILE posix.ldif ---
# --- BEGIN COPYRIGHT BLOCK ---
# This Program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; version 2 of the License.
#
# This Program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA.
#
# In addition, as a special exception, Red Hat, Inc. gives You the additional
# right to link the code of this Program with code not covered under the GNU
# General Public License ("Non-GPL Code") and to distribute linked combinations
# including the two, subject to the limitations in this paragraph. Non-GPL Code
# permitted under this exception must only link to the code of this Program
# through those well defined interfaces identified in the file named EXCEPTION
# found in the source code files (the "Approved Interfaces"). The files of
# Non-GPL Code may instantiate templates or use macros or inline functions from
# the Approved Interfaces without causing the resulting work to be covered by
# the GNU General Public License. Only Red Hat, Inc. may make changes or
# additions to the list of Approved Interfaces. You must obey the GNU General
# Public License in all respects for all of the Program code and other code used
# in conjunction with the Program except the Non-GPL Code covered by this
# exception. If you modify this file, you may extend this exception to your
# version of the file, but you are not obligated to do so. If you do not wish to
# provide this exception without modification, you must delete this exception
# statement from your version and license this file solely under the GPL without
# exception.
#
#
# Copyright (C) 2007 Red Hat, Inc.
# All rights reserved.
# --- END COPYRIGHT BLOCK ---
# add plugin configuration for posix users
dn: cn=Posix,cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
objectclass: top
objectclass: nsContainer
objectclass: extensibleObject
cn: Posix
dn: cn=Accounts,cn=Posix,cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
objectclass: top
objectclass: extensibleObject
cn: Accounts
dnaType: uidNumber
dnaNextValue: 500
dnaInterval: 4
dnaMagicRegen: 499
dnaFilter: (objectclass=posixAccount)
# add plugin configuration for posix groups
dn: cn=Groups,cn=Posix,cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
objectclass: top
objectclass: extensibleObject
cn: Groups
dnaType: gidNumber
dnaNextValue: 500
dnaInterval: 4
dnaMagicRegen: 499
dnaFilter: (objectclass=posixGroup)
--- NEW FILE posix_one.ldif ---
dn: uid=add_has_magic_number, dc=example, dc=com
objectclass: top
objectclass: organizationalperson
objectclass: posixaccount
cn: 1
sn: test
uid: add_has_uid_number
uidNumber: 499
gidNumber: 550
homeDirectory: /
--- NEW FILE posix_test.ldif ---
dn: uid=add_has_magic_number, dc=example, dc=com
objectclass: top
objectclass: organizationalperson
objectclass: posixaccount
cn: 1
sn: test
uid: add_has_uid_number
uidNumber: 499
gidNumber: 550
homeDirectory: /
dn: uid=add_second_has_magic_number, dc=example, dc=com
objectclass: top
objectclass: organizationalperson
objectclass: posixaccount
cn: 2
sn: test
uid: add_second_has_magic_number
uidNumber: 499
gidNumber: 550
homeDirectory: /
dn: uid=no_uid_number, dc=example, dc=com
objectclass: top
objectclass: organizationalperson
objectclass: posixaccount
cn: 3
sn: test
uid: no_uid_number
gidNumber: 550
homeDirectory: /
dn: uid=add_has_uid_number_550, dc=example, dc=com
objectclass: top
objectclass: organizationalperson
objectclass: posixaccount
cn: 4
sn: test
uid: add_has_uid_number
uidNumber: 550
gidNumber: 550
homeDirectory: /
dn: ou=sub, dc=example, dc=com
objectclass: top
objectclass: organizationalunit
ou: sub
dn: uid=add_is_sub, ou=sub, dc=example, dc=com
objectclass: top
objectclass: organizationalperson
objectclass: posixaccount
cn: 4
sn: test
uidNumber: 499
gidNumber: 550
homeDirectory: /
--- NEW FILE seeconfig.sh ---
#!/bin/sh
ldapsearch -x -D "cn=Directory Manager" -w secret123 -h localhost -p 3389 -b
"cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config"
"(objectclass=*)"
--- NEW FILE seeentries.sh ---
#!/bin/sh
ldapsearch -x -D "cn=Directory Manager" -w secret123 -h localhost -p 3389 -b
"dc=example, dc=com" "(objectclass=posixaccount)"
--- NEW FILE subtest.ldif ---
# --- BEGIN COPYRIGHT BLOCK ---
# This Program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; version 2 of the License.
#
# This Program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA.
#
# In addition, as a special exception, Red Hat, Inc. gives You the additional
# right to link the code of this Program with code not covered under the GNU
# General Public License ("Non-GPL Code") and to distribute linked combinations
# including the two, subject to the limitations in this paragraph. Non-GPL Code
# permitted under this exception must only link to the code of this Program
# through those well defined interfaces identified in the file named EXCEPTION
# found in the source code files (the "Approved Interfaces"). The files of
# Non-GPL Code may instantiate templates or use macros or inline functions from
# the Approved Interfaces without causing the resulting work to be covered by
# the GNU General Public License. Only Red Hat, Inc. may make changes or
# additions to the list of Approved Interfaces. You must obey the GNU General
# Public License in all respects for all of the Program code and other code used
# in conjunction with the Program except the Non-GPL Code covered by this
# exception. If you modify this file, you may extend this exception to your
# version of the file, but you are not obligated to do so. If you do not wish to
# provide this exception without modification, you must delete this exception
# statement from your version and license this file solely under the GPL without
# exception.
#
#
# Copyright (C) 2007 Red Hat, Inc.
# All rights reserved.
# --- END COPYRIGHT BLOCK ---
# add Samba SIDs
dn: cn=Example top level,cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
objectclass: top
objectclass: extensibleObject
dnaType: title
dnaPrefix: example-
dnaNextValue: 600
dnaInterval: 4
dnaMagicRegen: assign
dnaFilter: (objectclass=organizationalperson)
dnaScope: dc=example, dc=com
dn: cn=Example sub level,cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
objectclass: top
objectclass: extensibleObject
dnaType: title
dnaPrefix: sub-example-
dnaNextValue: 600
dnaInterval: 4
dnaMagicRegen: assign
dnaFilter: (objectclass=organizationalperson)
dnaScope: ou=sub, dc=example, dc=com