This article is an introduction how to start writing an NLM modules. It is dedicated to beginning programmers.
To create an executable module on server side (NLM) is needed the compiler of Watcom firm .
In this article is described how to use Watcom compiler, which is accessible as an Open Sorce software at present. Minimum necessery software is given here.
As a result we get NLM8.NLM module which we can run on Netware serwer.
1.0 Preparing of an environment to work.
In a minimum version to create executable programs on Netware server is needed DOS system.
Assuming all software will be installed on disc C: one should add the following commands/settings to AUTOEXEC.BAT file :
PATH C:\WATCOM\BINW
SET INCLUDE=C:\WATCOM\H
SET WATCOM=C:\WATCOM
Downloaded Watcom compiler software one should copy to disc C: keeping the same structure as original.
The attached file contains only customized NWSDK folder ( due to its real original size of a few dozen MB). To proper work one should download up-to-date NDK library version from http://developer.novell.com/ndk/nwsdkc.htm
2.0 An example of the model NLM module.
Source code of NLM8.C module is available in WATCOM\NLM8 directory/folder.
Below is shown a pattern of multi-thread NLM module. Apart the main thread of programm there are open two additional threads.
//==========================================
// PATTERN OF MULTI-THREAD NLM MODULE
//==========================================
//Includes
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <nwconio.h>
#include <nwthread.h>
#include <nwenvrn.h>
#include <nwerrno.h>
#include <ntypes.h>
//Globals
int NLM_MainThreadGroupID;
//This is the thread group ID for the main() thread
int NLM_ThreadCount = 0;
//Keeps count of number of threads running
int NLM_Exiting = FALSE;
//Flag that threads poll to know when to exit
int NLM_Thread1_ID;
//Thread ID for second thread we created in main()
int NLM_Thread2_ID;
//Thread ID for third thread we created in main()
int Thread1_ScreenHandle = NULL;
//Handle for screen created in thread 1
//Function prototypes
void main(void);
void NLM_SignalHandler(int sig);
void Thread1Func(void *);
void Thread2Func(void *);
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
// *********** MAIN ********
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
void main(void)
{
//Increment thread count
++NLM_ThreadCount;
//Get this thread's ID from OS
NLM_MainThreadGroupID = GetThreadGroupID();
//Register NLM_SignalHandler function for SIGTERM [UNLOAD] event
signal(SIGTERM, NLM_SignalHandler);
//Register NLM_SignalHandler function for SIGINT [CTRL-C] event
signal(SIGINT, NLM_SignalHandler);
ConsolePrintf("\nNLM8: Thread 0 (main() thread) started
and running!\n\r");
//////////////////////////////////
// Body of main continues here...
//////////////////////////////////
//Let's start up a new thread
NLM_Thread1_ID = BeginThread(Thread1Func, NULL, 8192, NULL);
if(NLM_Thread1_ID == EFAILURE)
{
ConsolePrintf("\n\rNLM8: ERROR: Could not start thread
1. BeginThread returned: %X\n\r", NLM_Thread1_ID);
//Don't exit immediately, set NLM_Exiting flag instead
NLM_Exiting = TRUE;
}
//Now let's start up another thread
NLM_Thread2_ID = BeginThread(Thread2Func, NULL, 8192, NULL);
if(NLM_Thread2_ID == EFAILURE)
{
ConsolePrintf("\n\rNLM8: ERROR: Could not start thread
2. BeginThread returned: %X\n\r", NLM_Thread2_ID);
//Don't exit immediately, set NLM_Exiting flag instead
NLM_Exiting = TRUE;
}
//////////////////////////////////
// Body of main ends here...
//////////////////////////////////
//Make sure the main() thread is the last to exit!
while(NLM_ThreadCount != 1 || !NLM_Exiting)
ThreadSwitchWithDelay();
ConsolePrintf("NLM8: main() thread exiting!\n\r");
//Bring thread count to zero so that the signal
//handler can now exit, if necessary
--NLM_ThreadCount;
return;
}
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
// Signal Handler
// This callback function will be called from the
// Server Console OS thread when the NLM is
// manually UNLOADed from the console,
// or if the server is going down,
// or if SIGTERM is raise()'ed for this NLM.
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
void NLM_SignalHandler(int sig)
{
int handlerThreadGroupID;
handlerThreadGroupID = GetThreadGroupID();
SetThreadGroupID(NLM_MainThreadGroupID);
switch(sig)
{
//SIGTERM - NLM is being UNLOADed,
//or otherwise terminated externally [e.g. Server going down]
case SIGTERM:
//Set flag to tell all threads it's time to leave
NLM_Exiting = TRUE;
////////////////////////////////////////
//NLM SDK functions may be called here//
//Don't do your cleanup here. //
//Do this in each thread instead. //
////////////////////////////////////////
//Wait until all threads have terminated before leaving
while(NLM_ThreadCount)
{
//This print statement is just for illustration.
ConsolePrintf("NLM8: SignalHandler:
Waiting for threads to terminate...\n\r");
ThreadSwitchWithDelay();
}
ConsolePrintf("NLM8: SignalHandler: Done!\n\r");
break;
default:
break;
}
SetThreadGroupID(handlerThreadGroupID);
return;
}
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
// This is the entry point for the second thread (created in main())
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
void Thread1Func(void *arg)
{
arg = arg; //suppress warnings
//Increment thread count
++NLM_ThreadCount;
ConsolePrintf("NLM8: Thread 1 started and running!\n\r");
//Create a screen for this thread for use by this NLM.
//Note that this automatically becomes the current [active] screen for this
//thread, because there are no other screens in this NLM.
Thread1_ScreenHandle = CreateScreen("NLM TEMPLATE
Multi-thread Screen", AUTO_DESTROY_SCREEN);
if(Thread1_ScreenHandle == EFAILURE || Thread1_ScreenHandle == NULL)
{
ConsolePrintf("\n\rNLM8: ERROR: CreateScreen returned: %X\n\r",
Thread1_ScreenHandle);
//Don't exit immediately, set NLM_Exiting flag instead
NLM_Exiting = TRUE;
}
//Thread1 run loop
while(!NLM_Exiting)
{
printf("Thread 1 is printing this text!\n");
//Press a key on this screen to terminate the NLM
if(kbhit())
{
NLM_Exiting = TRUE;
printf("Time to leave!!\n");
}
delay(500); //put this thread to sleep for 500ms.
}
//Clean up this thread's resources
if(Thread1_ScreenHandle != NULL)
DestroyScreen(Thread1_ScreenHandle);
//Reset to NULL so other threads won't try to use
// it now that it has been destroyed
Thread1_ScreenHandle = NULL;
//Decrement thread count
--NLM_ThreadCount;
ConsolePrintf("NLM8: Thread 1 shutdown...\n\r");
}
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
// This is the entry point for the third thread (created in main())
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
void Thread2Func(void *arg)
{
arg = arg; //suppress warnings
//Increment thread count
++NLM_ThreadCount;
ConsolePrintf("NLM8: Thread 2 started and running!\n\r");
//Set this thread's active screen to the screen we created in thread 1
if(Thread1_ScreenHandle != NULL)
SetCurrentScreen(Thread1_ScreenHandle);
else
NLM_Exiting = TRUE;
//Thread2 run loop
while(!NLM_Exiting)
{
if(Thread1_ScreenHandle != NULL) //make sure the screen is still valid
printf("This text comes from Thread 2!\n");
delay(500); //put this thread to sleep for 500ms.
}
//Decrement thread count
--NLM_ThreadCount;
ConsolePrintf("NLM8: Thread 2 shutdown...\n\r");
}
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
3.0 Compilation and linking.
Module compilation can be done by execution of komp.bat file which contains a command:
set pgm=nlm8
wcc386 -D2 -ms -w4 -5s -bt=NetWare -Ic:\nwsdk nlm8.c
Compilation and linking of module can be done by execution of link.bat file which contains following commands:
set pgm=nlm8
wcc386 -D2 -mf -w4 -5s -5r -bm -fp5 -fpi87 -eq -bt=NetWare -Ic:\nwsdk nlm8.c
wlink @nlm8.lnk
Linking parameters are set in a file NLM8.LNK:
SYSTEM NOVELL 'MULTI-THREAD NLM MODULE'
debug all
file nlm8
name nlm8.nlm
option map
option stack=16384
option copyright 'Copyright by DJack Software 04/2003. All rights reserved.'
option version = 1.1
import @c:\nwsdk\imports\nwsnut.imp
import @c:\nwsdk\imports\clib.imp
import @c:\nwsdk\imports\dsapi.imp
import @c:\nwsdk\imports\calnlm32.imp
import @c:\nwsdk\imports\clxnlm32.imp
import @c:\nwsdk\imports\netnlm32.imp
module nwsnut.nlm
module clib.nlm
module calnlm32.nlm
module clxnlm32.nlm
module netnlm32.nlm
As a result we get NLM8.NLM module which we can run on Netware serwer.
More information is provided in documentation of WATCOM compiler.
|