Hi All,

i need to discuss the following problem regarding both LDAP SDK
and MP enabled NLM development. I found that my test NLM stops working
properly after several hundreds of successful cycles. Sometimes there
are hundreds of cycles, sometimes there are thousands. On failure i see
the following message on the server console:

1-22-2007 5:22:34 pm: SERVER-5.70-0 [nmID=130004]
Illegal request detected by routine kMutexUnlock

It is not possible to unload my NLM after this message. Server console
simply hangs on unload attempt. The same NLM works fine when
it is compiled/linked without XDC data. Test hardware is HP LT6000R
equipped with 6 (six) P-III Xeon 700MHz and 8Gb of RAM. My NetWare,
LDAP, eDirectory versions are as follows:

Novell Netware, V6.5 Support Pack 5 - CPR Release
Support Pack Revision 05
(C) Copyright 1983- 2005 Novell Inc. All Rights Reserved. Patent Pending.
Server Version 5.70.05 April 11, 2006
Novell eDirectory Version 8.8 SP1 SMP
NDS Version 20114.28 September 14, 2006

SBS65:m libc
LIBC.NLM
Loaded from [C:\NWSERVER\] on Jan 22, 2007 5:09:33 pm
(Address Space = OS)
Standard C Runtime Library for NLMs [optimized, 7]
Version 9.00.05 January 3, 2007
Copyright (c) 1999-2007 by Novell, Inc. All rights reserved.
SBS65:m nldap
NLDAP.NLM
Loaded from [SYS:SYSTEM\] on Jan 22, 2007 5:11:25 pm
(Address Space = OS)
LDAP Agent for Novell eDirectory 8.8 SP1
Version 20114.59 August 30, 2006
Copyright 1997-2006 Novell, Inc. All rights reserved.
SBS65:m lldapsdk
LLDAPSDK.NLM
Loaded from [SYS:SYSTEM\] on Jan 22, 2007 5:10:20 pm
(Address Space = OS)
LDAP SDK Library (LibC version)
Version 3.04 September 11, 2006
(C) Copyright 1999-2005, Novell, Inc. All rights reserved
SBS65:m ds
DS.NLM
Loaded from [SYS:SYSTEM\] on Jan 22, 2007 5:10:20 pm
(Address Space = OS)
Novell eDirectory Version 8.8 SP1 SMP
Version 20114.28 September 14, 2006
Copyright 1993-2006 Novell, Inc. All rights reserved. Patents Pending.

And finally NLM source code:

#define N_PLAT_NLM
#include <stddef.h>
#include <screen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <ldap.h>
#include <nks/synch.h>


unsigned long shutDown = 0;
unsigned long threadCnt = 0;
unsigned long TotalThreadCnt = 0;

int ldapPort;
char *ldapHost, *loginDN, *password, *containerName;
char usage[] =
"\nUsage: ldpamt <host name> <port number> <login dn> <password>
<container name>\n"
"\nExample: ldapmt 127.0.0.1 389 cn=admin,o=Acme secret ou=Sales,o=Acme\n";

int BeginThread( void (*func)( LDAP * ),
void * stackP,
unsigned int stackSize,
void * arg );

void Search ( LDAP *ld );

void unloadSignal( int sig );



//////////////////////////////////////////////////////////////////////////////
void unloadSignal( int sig )
{
shutDown = 1;

if (sig == SIGTERM)
{
while (threadCnt) NXThreadYield();
}
}


//////////////////////////////////////////////////////////////////////////////
int main( int argc, char **argv)
{
int version = LDAP_VERSION3;
LDAP *ld = NULL, *ld1;
int rc;
int i;

#if defined(N_PLAT_NLM) && defined(__NOVELL_LIBC__)
setscreenmode(SCR_NO_MODE); /* Don't clear screen on
exit */
#endif

atomic_inc(&threadCnt);

if (argc != 6)
{
printf("%s", usage);
return(1);
}

ldapHost = argv[1];
ldapPort = atoi(argv[2]);
loginDN = argv[3];
password = argv[4];
containerName = argv[5];

signal( SIGTERM, unloadSignal );

version = LDAP_VERSION3;
ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, &version);

if (( ld = ldap_init( ldapHost, ldapPort )) == NULL)
{
printf("LDAP session initialization failed\n");
return 0;
}


rc = ldap_simple_bind_s( ld, loginDN, password );
if (rc != LDAP_SUCCESS )
{
printf("ldap_simple_bind_s: %s\n", ldap_err2string( rc ));
ldap_unbind_s ( ld );
return 0;
}

printf("LDAP session initialized and binded.\n");

while (!shutDown)
{
for (i = 0; i < 8; i++)
{
ld1 = ldap_dup(ld);
TotalThreadCnt++;
BeginThread(Search, NULL, 65535, ld1);
}
while (threadCnt > 1) NXThreadDelay(100);

printf("%d threads are done\n", TotalThreadCnt);
}

if (ld) ldap_unbind_s(ld);

atomic_dec(&threadCnt);
return 0;
}


//////////////////////////////////////////////////////////////////////////////
void Search ( LDAP *ld )
{
int rc,entryCount;

LDAPMessage *searchResult = NULL,
*entry = NULL;

char *dn = NULL,
*attribute = NULL,
**values = NULL;

BerElement *ber = NULL;

atomic_inc(&threadCnt);

// printf("Search started \n");

rc = ldap_search_ext_s(
ld, /* LDAP session handle */
containerName, /* container to search */
LDAP_SCOPE_ONELEVEL, /* search scope */
"(objectclass=*)", /* search filter */
NULL, /* return all attributes */
0, /* return attributes and
values */
NULL,
NULL,
NULL,
LDAP_NO_LIMIT,
&searchResult );

if ( rc != LDAP_SUCCESS || searchResult == NULL )
{
printf("ldap_search_ext_s: %s\n", ldap_err2string( rc ));
goto cleanup;
}


entryCount = ldap_count_entries( ld, searchResult );
if (entryCount < 1) goto precleanup;

/* Go through the search results by checking entries */
for ( entry = ldap_first_entry( ld, searchResult );
entry != NULL;
entry = ldap_next_entry( ld, entry ) )
{
if (( dn = ldap_get_dn( ld, entry )) != NULL )
{
//printf("\n dn: %s\n", dn );
ldap_memfree( dn );
}

for ( attribute = ldap_first_attribute( ld, entry, &ber );
attribute != NULL;
attribute = ldap_next_attribute( ld, entry, ber ) )
{
/* Get values and print. Assumes all values are strings. */
if (( values = ldap_get_values( ld, entry, attribute)) !=
NULL )
{
//for ( i = 0; values[i] != NULL; i++ )
// printf(" %s: %s\n", attribute, values[i] );
ldap_value_free( values );
}
ldap_memfree( attribute );
}

if (ber) ber_free(ber, 0); ber = NULL;

}

precleanup:

// printf("Search completed successfully. Entries returned: %d\n",
entryCount);

cleanup:

if (searchResult) ldap_msgfree( searchResult );

if (ld) ldap_destroy(ld);

atomic_dec(&threadCnt);

return ;
}


//////////////////////////////////////////////////////////////////////////////
int BeginThread( void (*func)( LDAP * ),
void * stackP,
unsigned int stackSize,
void * arg )
{
int err;
NXContext_t context; // will get freed when thread destroyed
NXThreadId_t threadID;

stackP = stackP; // Suppress compiler warning

context = NXContextAlloc( (void (*)(void *))func,
arg,
NX_PRIO_MED,
stackSize,
NX_CTX_NORMAL,
&err );
if( !err )
{
err = NXThreadCreate( context,
NX_THR_BIND_CONTEXT | NX_THR_DETACHED,
&threadID );
if( err )
{
NXContextFree( context );
return -1;
}
}
else
return -1;

return threadID;
} // BeginThread

I'm afraid that both ldap_search_() and ldap_destroy() funsctions are
not MP enabled. So it seems that it is not possible to develop true
multiprocessor NLM with current version of LDAP SDK.

Please let me know if i'm mistaken,
Andrey Karyagin

ABG Card Technology
Software Development Department