Novell is now a part of Micro Focus

Extending the NDS Schema: A Beginner's Approach

Articles and Tips: article

KEVIN BURNETT
Software Engineer
Developer Support

CHRISTOPHER JENKINS
Software Engineer
Developer Support

01 Jun 1997


A practical, hands-on treatment of the NDS Schema. Topics covered include Attribute Types, Schema Operations, and Class Functions. Complete sample programs are included.

Introduction

Novell Directory Services (NDS) provides an extensible, flexible database which can be customized to suit a particular developer's application or needs. NDS functions as a repository of information for the network making it possible to track network resources in a very convenient way. NDS also enhances the availability and security of both network resources and business-critical resources. NDS is scalable to match your company's needs and requirements. It is this scalability that adds significant value to NDS. The entity that allows this scalability is the NDS schema. The structure of the NDS directory tree is regulated by the NDS schema.

The NDS schema defines via rules how the NDS directory tree is structured. The NDS schema determines what objects are defined, what attributes can be associated with objects, what properties objects inherit, and what positions objects occupy in the NDS directory tree. Novell provides a base schema, which is a collection pre-defined object classes and attribute definitions.


Note: The base schema is not removable from NDS.

The base schema can be enhanced. For example, you could add a cellular phone number to the user class. This would allow your application to track this number. Access to this number could be limited to only those users who had a real need to know it. Independent software vendors (ISVs) and customers can integrate new services into the NDS tree by extending the NDS schema and adding new objects. This objects can be virtually any functionality that you would have a need for. For example, an ISV could add fax server functionality to the network by adding a fax server object to the NDS tree.

Nuts and Bolts

The following text will explain the process of extending the NDS schema by using examples to create a new object class definition and a new attribute type definition. (If you are new to NDS programming, it is recommended that you read the "Introducing the Schema" chapter in the NDS Developer's Concepts book, which is a part of Novell's SDK Release 10 and later.)

This article will present an overview of how to extend the NDS schema followed by two sample NetWare Loadable Module (NLM) programs illustrating these concepts.

Attribute Types

Before discussing extending the NDS schema, a close look at attribute type is in order. Attribute types are based on attribute syntaxes. There are two components that combine together to determine what an attribute is; the attribute syntax and the attribute constraints.

Attribute Constraints

Attribute constraints are flags that can be defined for an attribute that give certain characteristics. Constraints can also be described as restrictions affecting the attribute value. The following are some examples of attribute constraints:


Attribute Constraint
Description

DS_SINGLEVALUE_ATTR

When this flag is set, the attribute can contain only one value. When the flag is cleared, the attribute can contain multiple values.

DS_SYNC_IMMEDIATE

When NDS makes a modification to an attribute with this constraint, other replicas containing the object are synchronized immediately rather than at the next synchronization interval.

DS_PUBLIC_READ

When this flag is set, anyone can read the attribute as long as they are attached to a server, even if they are not authenticated.


Note: For a complete list of attribute constraints, see NWDSDEFS.H in the SDKCD11\NWSDK\INCLUDEdirectory structure.

Attribute Syntax

An attribute syntax defines a data type for the attribute. The standard attribute syntax definitions are contained in the NDS Schema Specification manual, which is a part of Novell's SDK Release 10 and later. NDS does not allow extension of these syntaxes. You most commonly use syntaxes when you are performing a search. They provide the criteria for the search. The following are several examples selected from the many attribute syntaxes provided by NDS:


Attribute Syntax
Description

SYN_CI_STRING

The case Ignore String syntax is used in attributes whose values are strings and the case (upper or lower) is not significant.

SYN_INTEGER

The Integer syntax is used for attributes whose values are signed integers.

SYN_TEL_NUMBER

The Telephone Number syntax is used for attributes whose values are telephone numbers.

Attributes and Classes

When first defining an attribute, the attribute is not associated with any object class. For an attribute definition to really mean anything it must be associated with a class. In other words, you could create a set of attributes such as Hands, Arms, and Fingers. By themselves, they aren't useful. When you define an object class such as a Body, you tell it which attributes it is going to have. The attributes now take on a meaning and give depth to the object class.

Schema Operations

The following operations can be performed on the NDS schema:

  • You can create, read and delete attribute type definitions.

  • You can create, read and delete object class definitions.

  • You can list containable classed.

  • You can modify class definitions.

  • You can read attribute syntax definitions.

  • You can read attribute syntax Ids.

The only thing you might want to do, but can't, is create or modify an attribute syntax. The attribute syntaxes are the most fundamental building blocks of the NDS Directory and can't be altered.

The following sections in this article explain how to do the operations used to perform the concepts in the preceding list.


Note: You must have Admin or equivalent rights to extend the NDS schema. And,if you are working on a workstation client, you must initializethe Unicode tables.

Attribute Functions

Attribute type definitions can be created, read or deleted. To perform these operations, you must be familiar with the following NDS API functions:


API
Description

NWDSDefineAttr

Adds a new attribute definition tothe Directory Schema.

NWDSReadAttrDef

Retrieves information about DirectorySchema attribute definitions.

NWDSRemoveAttrDef

Deletes an attribute definition fromthe Directory schema.

Creating An Attribute

Creating an attribute is a two-step process. First, you declare a structure of type NWATTR_INFO and fill it in. For example, suppose you wanted to create an attribute called "Second Home Directory." You would declare a structure and fill it in, as follows:

NWDSCCODE ccode;

NWATTR_INFO newAttr; /* Allocate the strucutre. */



/* Populate the strucutre. */



newAttr.attrFlags = DS_SINGLE_VALUED_ATTR | DS_SIZED_ATTR; /* Constraints set to single value. */ 

newAttr.attrSyntaxID = SYN_CI_STRING; /* Syntax set to string */

newAttr.attrLower = 1; /* Set size of the string. */

newAttr.attrUpper = 255;



newAttr.asn1ID.length = 0; /* This example does not use the asn1ID field. */

newAttr.asn1ID.data[0] = 0; /* This example does not use the asn1ID field. */

Let's look at the five fields that make up the NWATTR_INFO structure in more detail:


Field
Description

attrFlags

Defines the constraints on the attribute.Multiple constraints can be set by ORing them together.

attrSyntaxID

Defines the syntax ID that the attributeis based upon.

attrLower

Defines a lower limit for the valueof the attribute. If the attribute defines a string, this field placesa lower limit on the length of the string.

attrUpper

Defines an upper limit for the valueof the attribute. If the attribute defines a string, this field placesan upper limit on the length of the string.

Ans1ID

When developers register their schemaextensions with Novell, they are given an ans1ID, which is the developer'sidentification. This field (structure) is provided for developer's to passin their asn1ID information.

The second step in creating a new attribute definition is to define the attribute using NWDSDefineAttr. The following shows how to do this:

ccode = NWDSDefineAttr(context,""Second Home Directory", "newAttr);

NWDSDefineAttr requires three arguments: the directory context handle, the name of the new attribute in a string and the address of the data structure of the attribute.

Reading An Attribute Definition

Reading an attribute definition by using NWDSReadAttrDef is shown in the following example:

ccode = NWDSReadAttrDef(context, DS_ATTR_DEFS, FALSE, inBuf, &iterationHandle, outBuf);

NWDSReadAttrDefrequires five arguments:

  • the Directory context handle,

  • the type of information required,

  • the all-attributes flag cleared,

  • a pointer to the request buffer that contains the name of the attribute,

  • the address of the iteration handle and a pointer to the output buffer that receives the attribute definition.

Deleting an Attribute Definition

An attribute definition can be deleted by calling NWDSRemoveAttrDef. For example, if you wanted to delete the attribute "Second Home Directory," you would create the following code:

ccode = NWDSRemoveAttrDef(context, "Second Home Directory");

The NWDSRemoveAttrDef only requires two arguments: the Directory context handle and the name of the attribute to be deleted.

Note: You can't remove an attribute thatis part of a Base class, nor can you remove an attribute that you haveadded to a Base class definition.

Class Functions

The five basic APIs available in the cross-platform libraries are as follows:


API
Description

NWDSDefineClass

You can create a new class with this function.

NWDSModifyClassDef

You can modify an existing class with this function

NWDSReadClassDef

You can read a class definition with this function.

NWDSRemoveClassDef

You can delete a class definition with this function.

NWDSListContainableClasses

You can list all of the containable class definitions with this function.


Note: For a complete description of these functions, refer to Novell'sNetWare SDK Release 11's On-Line documentation or Help file.

Creating a Class Definition

The following steps need to be completed to create a new object class definition:

  1. Declare the needed variables.

  2. Allocate and initialize a request buffer.

  3. Fill out the class information structure.

  4. Place the attribute information into the request buffer.

  5. Create the new object definition.

The following explains, by example, how to do this:

Suppose you want to create a new class called "MyClass" class. The first step would be to declare the needed variables

NWDSCCODE ccode;

NWCLASS_INFO myClassInfo; /* class info structure */

pBuf_T  myClassBuf; /* request buffer */

A buffer of type NWDS_BUFFER is needed as a request buffer and a structure of type NWCLASS_INFOis needed to store the class information.

The next step is to allocate and initialize the request buffer:

ccode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &myClassBuf);&
ccode = NWDSInitBuf(context, DSV_DEFINE_CLASS, myClassBuf);

After the buffer has been initialized, the next step is to fill out the class information structure:

myClassInfo.classFlags = DS_EFFECTIVE_CLASS; /* Set flags to effective class */

myClassInfo.asn1ID.length = 0;    /* Not used in this example */

myClassInfo.asn1ID.data[0] = 0;    /* Not used in this example */

The class information structure only has two fields:


Field
Description

classFlags

Defines the class type. If the effective class flage was cleared, the class would be noneffective. Another useful flag is DS_CONTAINER_CLASS.

asn1ID

When developer's register thier schema with Novell, they are given an asn1ID, which is the developer's identification. This field, which is actually a structure, is provided for developers to pass in their asn1ID information.

After filling out the class information structure, the next step is to start placing attribute information into the request buffer. There are five categories of class items that you must place into the class buffer. You call NWDSBeginClassItem to start a new category. NWDSBeginClassItem must be called for each category, even if you are not going to add items to it. To add items to a category, you call NWDSPutClassItem for each item added. The following lists the five categories of class items in the order they must be called:

  1. Super class names.

  2. Containment class names

  3. Naming attribute names

  4. Mandatory attribute names

  5. Optional attribute names

The following code snipet demonstrates how to do this:

ccode = NWDSBeginClassItem(context, myClassBuf);

/* super class names example */

ccode = NWDSPutClassItem(context, myClassBuf, "TOP"); /* traditional */



ccode = NWDSBeginClassItem(context, myClassBuf);

/* containment class names examples */

ccode = NWDSPutClassItem(context, myClassBuf, C_ORGANIZATIONAL_UNIT);

ccode = NWDSPutClassItem(context, myClassBuf, "Organization");



ccode = NWDSBeginClassItem(context, myClassBuf);

/* naming attribute names example */

ccode = NWDSPutClassItem(context, myClassBuf, "CN");



ccode = NWDSBeginClassItem(context, myClassBuf);

/* mandatory attribute names examples */

ccode = NWDSPutClassItem(context, myClassBuf, "CN");

ccode = NWDSPutClassItem(context, myClassBuf, "Second Home Directory");



ccode = NWDSBeginClassItem(context, myClassBuf);

/* optional attribute names example */

ccode = NWDSPutClassItem(context, myClassBuf, "Optional Attribute");

Mandatory attributes must be added during the creation of a class definition. It is not possible to go back later and use NWDSModifyClassDef to add a mandatory attribute. It is also suggested that you should be very careful about making an attribute mandatory.

The last step is to create the new object class definition:

ccode = NWDSDefineClass(context, "MyClass", &myClassInfo, myClassBuf);

NWDSDefineClass requires four parameters: the Directory context handle, the name of the new class, the address of the class information structure and a pointer to the request buffer.

Modifying a Class Definition

NWDSModifyClassDef is used to add optional attributes to a class definition. Note that the only modifications that can be done to an existing class is the addition of optional attributes as illustrated in the following example:

ccode = NWDSModifyClassDef(context, "MyClass", buffer);

This function requires three parameters: the Directory context handle, the name of the class to be modified and a pointer to the request buffer that contains the optional attribute information to be added.

If you would like to determine the names of all the classes and their definitions, use NWDSReadClassDef as illustrated in the following example:

ccode = NWDSReadClassDef(context, DS_CLASS_DEF_NAMES, TRUE, NULL,  

           &iterationHandle, resultBuffer);&

This function requires six parameters: the Directory context handle, the information type specifier (can be names only, or names and definitions), the all-clases flag set, NULL instead of a pointer to an input buffer, the address of the iteration handle and a pointer to the output buffer.

Read each class entry from the output buffer using NWDSGetClassDefCount and NWDSGetClassDef, followed by five consecutive calls to NWDSGetClassItemCount and NWDSGetClassItem.


Note: A call to NWDSReadClassDef returns only the attributes that were defined for that particular class. It does not return the attributes that were inherited, but does return the name of the super class. To find all of theattributes available for a class, call NWDSReadClassDeffor each super class until you reach TOP.

Deleting a Class Definition

A class definition can be deleted using the API NWDSRemoveClassDef. This is shown in the following example:

ccode - NWDSRemoveClassDef(context, "MyClass");

NWDSRemoveClassDef requires two parameters: the Directory context handle and the name of the class to be deleted.


Note: You can't remove Base class definitions, nor can you remove any class untilall of the objects associated with that class have been removed from the Directory tree.

Listing Containable Classes

NWDSListContainableClasses will list all of the containable classes as illustrated in the following example:

ccode = NWDSListContainableClasses(context, parentObject, &iterationHandle, &
      resultBuffer);

The required parameters for this function are: the Directory context handle, a pointer to the name of the parent container, the address of the iteration handle and the location of the result buffer.

The information returned in the result buffer can be removed using NWDSGetClassitemCount and NWDSGetClassItem.

Novell's NDS Schema Registry

Since there will be several developer's that will be extending the NDS schema, there is a need for a mechanism to control duplicate names from occurring. With out some sort of safeguard, potential collisions could occur in an NDS Directory tree. Novell provides an NDS Schema registry whereby developer's can register a prefix that will be guaranteed to be unique to their project and/or company. The prefix is limited to eight characters, but only needs to be registered once per company. This prefix can be used for naming attribute syntaxes and class definitions. For more information about registering an NDS Schema Prefix with Novell, see our Web site at: http://netware.novell.com/nds/schema.htm.

Conclusion

The following two sample programs will illustrate what has been discussed in this article. The first program will add a new class definition to the NDS schema, create a new attribute definition, create a new object using these definitions and populate the new attribute with a new value. The program will then pause and allow you to switch to the second program, via the NetWare server console, which will display the value in the new attribute. Lastly you can switch back to the original program which will delete the new object, attribute definition and class definition.


Note: These two programs are available as Technical Information Documents (TIDs)on Novell's Developer Support Web site located at: http://devsup.novell.com/sample.htm, in either the Directory Services or Server groups under the Sample Code column. The filename is: XNDSSCHE.EXE.

Credits

The following people, groups and resources made this article and sample code possible. Novell Developer Support Group, Novell Developer Relations Group, Novell Java Class Libraries Group, NDS Developer's Concepts manual - from Novell's SDK Release 11 and Novell's SDK Release 11 in general.

Sample Program #1, SCHEMA.C.

/**************************************************************************

** Copyright (c) 1997 Novell, Inc.  All Rights Reserved.

**

** THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND

** TREATIES.  USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE

** LICENSE AGREEMENT ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK)

** THAT CONTAINS THIS WORK. 

** 

** Pursuant to the SDK License Agreement, Novell hereby grants to

** Developer a royalty-free, non-exclusive license to include the

** sample code SCHEMA.C and derivative binaries in its product.

** Novell grants to Developer worldwide distribution rights to market,

** distribute or sell the sample code SCHEMA.C and derivative

** binaries as a component of Developer's product(s).  Novell shall

** have no obligations to Developer or Developer's customers with

** respect to this code.

** 

** DISCLAIMER:

** 

**  Novell, Inc. makes no representations or warranties with respect

** to the contents or use of this code, and specifically disclaims any

** express or implied warranties of merchantability or fitness for any

** particular purpose.  Further, Novell, Inc. reserves the right to revise

** this publication and to make changes to its content, at any time,

** without obligation to notify any person or entity of such revisions or

** changes.

**

**  Further, Novell, Inc. makes no representations or warranties with

** respect to any software, and specifically disclaims any express or

** implied warranties of merchantability or fitness for any particular

** purpose.  Further, Novell, Inc. reserves the right to make changes to

** any and all parts of the software, at any time, without obligation to

** notify any person or entity of such changes.

**

***************************************************************************

**

** File: SCHEMA.C

**

** This program will define a new attribute called Second Home Directory.

** Next a new class definition will be created in the NetWare Directory

** Services Schema called MyClass, utilizing the new attribute.  Finally

** an object, myObject, will be created and the new object's Second Home

** Directory will be populated.

**

** The second portion of this program will remove the new object,

** myObject.  Next it will delete the new class definition, MyClass.

** Lastly it will remove the new attribute definition for Second Home

** Directory. 

** 

**

**       

** Programmers:

**

** Ini   Who                  Firm

** ---------------------------------------------------------------------

** KDB   Kevin D. Burnett     Novell Developer Support

**

**

** History:

**

** When        Who      What

** ---------------------------------------------------------------------

** 3-07-1997   KDB      First code.

** 3-08-1997   KDB      Modifications and comments.

** 3-12-1997   KDB      Re-design and modifications.

**************************************************************************/



/**************************************************************************

** Explanation of the program.

**

** Expanding the Directory Schema.

** You can expand the Directory Services Schema by creating new class,

** attribute, and object definitions. These changes will be replicated

** on all name servers within the Directory tree.

**

** This program is broken into three parts:

** 1. Defining a new attribute.

** 2. Defining a new class.

** 3. Creating a new object to use the new class and attribute definitions. 

**

** NOTE: If you expand the Base Schema, it is your responsibility to

** provide utilities that your customers can use to access the new object

** classes and attribute types. The NetWare Utilities do not provide an

** interface for accessing extensions to the Base Schema.

**

**************************************************************************/



/**************************************************************************

** Headers and function prototypes.

*/



   /*------------------------------------------------

   ** Defines

   */

   #define N_PLAT_NLM /* X-plat libraries need to know the platform. */

   #define TRUE 1

   #define FALSE 0

   

   /*------------------------------------------------

   ** ANSI Include headers.

   */

   #include <stdio.h> <


   /*------------------------------------------------

   ** Novell specific Include headers.

   */

   

   #include <nwdsapi.h<<
   #include <nwdsdc.h<<
   #include <nwdsdefs.h<<
   #include <nwapidef.h<<
   #include <nwdsnmtp.h<<
   

/**************************************************************************

** Watcom's IDE requires this function.

*/

void __WATCOM_Prelude(void)

{

   return;

}



/**************************************************************************

** main

*/

void main (void)

{

   NWDSCCODE            ccode; 

   NWDSContextHandle    context;

   NWATTR_INFO          newAttr;       // attribute structure

   NWCLASS_INFO         myClassInfo;   // class info structure 

   pBuf_T               myClassBuf;

   pBuf_T               myObjectBuf;

   nint32               myClassBufFlag = FALSE;

   nint32               myObjectBufFlag = FALSE;

   nstr8                userName[NW_MAX_USER_NAME_LEN];

   nstr8                userPassword[50];

      

   /*------------------------------------------------

   ** Create a NDS context.

   */

   context = NWDSCreateContext();

   if(context == ERR_CONTEXT_CREATION)

   {

      printf("\nNWDSCreateContext failed.");

      goto errorExitGeneral;

   }

   

   /*------------------------------------------------

   ** As an NLM, we need to be authenticated to NDS.

   ** Get user name and password.

   */

   printf("\nMust authenticate to NDS");

   printf("\nEnter User name: ");

   gets(userName);

   printf("Enter User Password: ");

   gets(userPassword);



   /*------------------------------------------------

   ** Perform the Login/authentication.

   */

   ccode = NWDSLogin(context, 0, userName, userPassword, 0);

   if(ccode)

   {

      printf("\nNWDSLogin returned %d", ccode);

      goto errorExitContext;

   }

   else

   {

      printf("\nAuthentication successful.");

      printf("\nStart process of defining a new class/attribute.");

   }



/**************************************************************************

** Create the new attribute.

**

** Following are steps for creating a New Attribute Definition.

**

** Before creating a new attribute, you should examine the attributes provided

** by the Base Schema to determine if any of them will meet your needs.

** If not, you can create a new attribute type.

**

** This example program demonstrates the creation of a new attribute class

** named "Second Home Directory" that can be used to hold the name of an

** alternate home directory.

**

** An attribute named "Home Directory" is listed in the "Attribute Type

** Definitions chapter, in the NetWare SDK Release 11's "NetWare Directory

** Services Schema Specification" Manual.  Most of the definition for the

** Home Directory attribute works for defining the Second Home Directory

** attribute. 

**

** The "Second Home Directory" attribute is defined as follows:

**

** Syntax is "case insensitive string" or "Path."

** The constraints are DS_SINGLE_VALUED_ATTR + DS_SIZED_ATTR.

** The size of the value pointed to can be from 1 to 255 characters.

**

** While you can use most of the Home Directory definition to define the

** "Second Home Directory" attribute, you cannot set the DS_NONREMOVABLE_ATTR

** constraint. (You cannot define nonremovable attributes; only the Base

** Schema attributes are defined as nonremovable.)

**

** To create the Second Home Directory attribute, complete the following steps:

**

** 1.Declare a structure of type NWATTR_INFO and fill it in.

**

** NWDSCCODE ccode; 

** NWATTR_INFO newAttr;

**  

**   This code assumes you have created your context and have 

**   logged in as an object that has permission to extend the 

**   Schema. You must also have initialized the Unicode 

**   tables if you are on a workstation client. 

** 

** newAttr.attrFlags=DS_SINGLE_VALUED_ATTR | DS_SIZED_ATTR; 

** newAttr.attrSyntaxID=SYN_CI_STRING; 

** newAttr.attrLower=1;

** newAttr.attrUpper=255;

**  

** This example does not use the asn1ID field.

**

** newAttr.asn1ID.length=0; 

** newAttr.asn1ID.data[0]=0;

**

** 2.Create the new attribute definition.

**

** ccode= NWDSDefineAttr(context,"Second Home Directory",

**                       &newAttr); &
** if(ccode < 0)  

**   printf("Attribute could not be added. 

**           NWDSDefineAttr returned %d\n",err);

**

**************************************************************************/



   /*------------------------------------------------

   ** Set up the NWATTR_INFO structure.

   */

   newAttr.attrFlags = DS_SINGLE_VALUED_ATTR | DS_SIZED_ATTR; 

   newAttr.attrSyntaxID = SYN_CI_STRING;

   newAttr.attrLower = 1; 

   newAttr.attrUpper = 255; 

   newAttr.asn1ID.length = 0; /* This example does not use the asn1ID field. */

   newAttr.asn1ID.data[0] = 0; /* This example does not use the asn1ID field. */



   /*------------------------------------------------

   ** Add the new attribute definition to the schema.

   */

   ccode= NWDSDefineAttr(context,"Second Home Directory", &newAttr);&
   if(ccode == ERR_ATTRIBUTE_ALREADY_EXISTS)

   {

      printf("\nNWDSDefineAttr, Second Home Directory, already exists.");

      goto defineClass;

   }

   else if(ccode != 0)

   { 

      printf("\nNWDSDefineAttr, Second Home Directory, returned %d", ccode);

      goto errorExitLogin;

   }

   else

      printf("\nNWDSDefineAttr 'Second Home Directory' successful.");



/**************************************************************************

** Define the new class.

**

** Creating a Class Definition

**

** To create a new object class, you must follow a five-step process:

**

** 1. Declare the needed variables.

** 2. Allocate and initialize a request buffer.

** 3. Fill out the class information structure.

** 4. Place the attribute information into the request buffer.

** 5. Create the new object definition.

**

** Suppose you wanted to create a new class called "MyClass" class.

** Your first step would be to declare the needed variables:

**

** NWDSCCODE      ccode;

** NWCLASS_INFO   MyClass;       // class info structure

** NWDS_BUFFER    *myClassBuf;   // request buffer

**

** A buffer of type NWDS_BUFFER is needed as a request buffer and a structure of

** type NWCLASS_INFO is needed to store the class information.

**

** The next step is to allocate and initialize the request buffer:

**

** ccode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &myClassBuf);&
** ccode = NWDSInitBuf(context, DSV_DEFINE_CLASS, myClassBuf);

**

** Once the buffer has been initialized, your next step is to fill out the

** class information structure:

**

** myClassInfo.classFlags = DS_EFFECTIVE_CLASS;   // Set flags to effective class

** myClassInfo.ans1ID.length = 0;                 // Not used in this example

** myClassInfo.ans1ID.data[0] = 0;                // Not used in this example

**

** The class information structure has only two fields:

**

** classFlags

** Defines the class type. If the effective class flag was cleared,

** the class would be noneffective. Another useful flag is DS_CONTAINER_CLASS.

**

** ans1ID

** When developers register their schema with Novell, they are given an asn1ID,

** which is the developer's identification. This field (structure) is provided

** for developers to pass in their asn1ID information.

**

** With the structure filled out, your next step is to start placing the attribute

** information into the request buffer. There are five categories of class items

** that you must place into the class buffer. You call NWDSBeginClassItem to start

** a new category. In fact, this function must be called for each category, even if

** you are not going to add items to it. To add items to a category, you call

** NWDSPutClassItem for each item added. Here are the five categories of class items

** in the order they are called:

**

** 1.Super class names

** 2.Containment class names

** 3.Naming attribute names

** 4.Mandatory attribute names

** 5.Optional attribute names

**

** Super class names:

** ccode = NWDSBeginClassItem(context, myClassBuf);

** ccode = NWDSPutClassItem(context, myClassBuf, "TOP");

**

** Containment class:

** ccode = NWDSBeginClassItem(context, myClassBuf);

** ccode = NWDSPutClassItem(context, myClassBuf, "TOP");

** ccode = NWDSPutClassItem(context, myClassBuf, "Organization");

**

** Naming attributes:

** ccode = NWDSBeginClassItem(context, myClassBuf);

** ccode = NWDSPutClassItem(context, myClassBuf, "CN");

**

** Mandatory attributes:

** ccode = NWDSBeginClassItem(context, myClassBuf);

** ccode = NWDSPutClassItem(context, myClassBuf, "CN");

** ccode = NWDSPutClassItem(context, myClassBuf, "Second Home Directory");

**

** Optional attributes:

** ccode = NWDSBeginClassItem(context, toastBuffer);

**

** Mandatory attributes must be added during the creation of a class

** definition.  You cannot go back later and use NWDSModifyClassDef

** to add a mandatory attribute.  Nevertheless, you should be very

** conservative about making an attribute mandatory.

**

** The final step is to create the new object class definition:

**

** ccode = NWDSDefineClass(context, "MyClass", &myClassInfo, myClassBuf);&
** if (ccode != 0)

**   printf("Error while creating class: %d\n", ccode);

**

** NWDSDefineClass requires four arguments: the Directory context handle,

** the name of the new class, the address of the class information structure,

** and a pointer to the request buffer.

**************************************************************************/

defineClass:



   /*------------------------------------------------

   ** Define the class that we are going to extend

   ** the schema to.

   ** First allocate a request buffer.

   */

   ccode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &myClassBuf);&
   if(ccode != 0)

   {

      printf("\nNWDSAllocBuf, myClassBuf, returned %d", ccode);

      goto errorExitmyObjectBuf;

   }

   else

      printf("\nNWDSAllocBuf, myClassBuf, successful.");



   /*------------------------------------------------

   ** Next initialize the request buffer.

   */

   ccode = NWDSInitBuf(context, DSV_DEFINE_CLASS, myClassBuf);

   if(ccode != 0)

   {

      printf("\nNWDSInitBuf, class, returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSInitBuf, class, successful.");



   /*------------------------------------------------

   ** Fill out the class information structure.

   */

   myClassInfo.classFlags = DS_EFFECTIVE_CLASS;   // Need to be able to create object. 



   myClassInfo.asn1ID.length = 0;                 // Not used in this example

   myClassInfo.asn1ID.data[0] = 0;                // Not used in this example



   /*------------------------------------------------

   ** Prepare to put information into myClassBuffer.

   ** This needs to be called five times for each of

   ** the five possibilities:

   **

   ** 1. Super Class Definitions.

   */

   ccode = NWDSBeginClassItem(context, myClassBuf);

   if(ccode != 0)

   {

      printf("\nNWDSBeginClassItem returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSBeginClassItem, 1, successful.");



   ccode = NWDSPutClassItem(context, myClassBuf, "TOP");

   if(ccode)

   {

      printf("\nNWDSPutClassItem returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSPutClassitem: TOP, successful");



   /*------------------------------------------------

   ** 2. Containment class names.

   */

   ccode = NWDSBeginClassItem(context, myClassBuf);

   if(ccode != 0)

   {

      printf("\n NWDSBeginClassItem returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSBeginClass, 2, successful");



   ccode = NWDSPutClassItem(context, myClassBuf, C_ORGANIZATIONAL_UNIT);

   if(ccode)

   {

      printf("\nNWDSPutClassItem returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSPutClassItem: Organazational Unit, successful.");

      

   ccode = NWDSPutClassItem(context, myClassBuf, "Organization");

   if(ccode)

   {

      printf("\nNWDSPutClassItem returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSPutClassItem: Organization, successful.");

   

   /*------------------------------------------------

   ** 3. Naming attribute names.

   */

   ccode = NWDSBeginClassItem(context, myClassBuf);

   if(ccode != 0)

   {

      printf("\nNWDSBeginClassItem returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSBeginClass, 3, successful.");



   ccode = NWDSPutClassItem(context, myClassBuf, "CN");

   if(ccode)

   {

      printf("\nNWDSPutClassItem returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSPutClassItem: CN, successful.");



   /*------------------------------------------------

   ** 4. Mandatory attribute names.

   */

   ccode = NWDSBeginClassItem(context, myClassBuf);

   if(ccode != 0)

   {

      printf("\nNWDSBeginClassItem returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSBeginClassItem, 4, successful.");



   ccode = NWDSPutClassItem(context, myClassBuf, "CN"); /* create object with name */

   if(ccode != 0)

   {

      printf("\nNWDSPutClassItem returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSPutClassItem: CN, successful.");

   

   ccode = NWDSPutClassItem(context, myClassBuf, "Second Home Directory");

   if(ccode)

   {

      printf("\nNWDSPutClassItem returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSPutClassItem: Second Home Directory, successful.");

   

   /*------------------------------------------------

   ** 5. Optional attribute names.

   ** We won't use any in this example program.

   */

   ccode = NWDSBeginClassItem(context, myClassBuf);

   if(ccode != 0)

   {

      printf("\nNWDSBeginClassItem returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSBeginClassitem, 5, successful.");



   /*------------------------------------------------

   ** Define the new class.

   */

   ccode = NWDSDefineClass(context, "MyClass", &myClassInfo, myClassBuf);&
   if(ccode == ERR_CLASS_ALREADY_EXISTS)

   {

      printf("\nNWDSDefineClass, MyClass, already exists.");

      goto createObject;

   }

   else if(ccode != 0)

   {

      printf("\nNWDSDefineClass returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSDefineClass: MyClass, successful.");



   /*------------------------------------------------

   ** Free the buffer used for the class creation

   ** process.

   */

   ccode = NWDSFreeBuf(myClassBuf);

   myClassBufFlag = TRUE;

   if(ccode)

   {

      printf("\nNWDSFreeBuf, myClassBuffer, returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSFreeBuf, myClassBuffer, successful.");



/**************************************************************************

** Create an object using the new class definition.

**

** To create a new object, you must use the following process:

**

** 1. Allocate a buffer to create new object.

** 2. Initialize buffer to be a ADD_OBJECT buffer.

** 3. Let NDS know what kind of object is being created.

** 4. Name the new object.

** 5. If adding an attribute, specify the name.

** 6. You may want to populate the new attribute.

** 7. Create the new object.

**

** Allocate the buffer to create the new object.

** ccode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &myObjectBuf);&
**

** Initialize buffer to be a ADD_OBJECT buffer.

** ccode = NWDSInitBuf(context, DSV_ADD_ENTRY, myObjectBuf);

**

** Let NDS know what kind of object is being created.

** ccode = NWDSPutAttrName(context, myObjectBuf, A_OBJECT_CLASS);

**

** Name the new object.

** ccode = NWDSPutAttrVal(context, myObjectBuf, SYN_DIST_NAME, "MyClass");

**

** If adding an attribute, specify the name.

** ccode = NWDSPutAttrName(context, myObjectBuf, "Second Home Directory");

**

** You may want to populate the new attribute.

** ccode = NWDSPutAttrVal(context, myObjectBuf, SYN_CI_STRING, "SERVER\\VOL:DIR\\DIR");

**

** Create the new object.

** ccode = NWDSAddObject(context, "myObject", NULL, 0, myObjectBuf);

**

** NWDSAddObject requires five arguments: the Directory context handle,

** the name of the object to be added, the iteration handle (this is

** reserved, pass NULL), more (a reserved parameter -- pass 0),

** and a pointer to the request buffer.

**************************************************************************/

createObject:

      

   /*------------------------------------------------

   ** Allocate buffer to create new object.

   */

   ccode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &myObjectBuf);&
   if(ccode)

   {

      printf("\nNWDSAllocBuf returned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSAllocBuf, myObjectBuf, successful.");



   /*------------------------------------------------

   ** Initialize buffer to be a ADD OBJECT buffer.

   */

   ccode = NWDSInitBuf(context, DSV_ADD_ENTRY, myObjectBuf);

   if(ccode)

   {

      printf("\nNWDSInitBuf returned %d", ccode);

      goto errorExitmyObjectBuf;

   }

   else

      printf("\nNWDSInitBuf, myObjectBuf, successful.");

   

   /*------------------------------------------------

   ** Let NDS know what kind of object we are creating.

   */

   ccode = NWDSPutAttrName(context, myObjectBuf, A_OBJECT_CLASS);

   if(ccode)

   {

      printf("\nNWDSPutAttrName, A_OBJECT_CLASS, returned %d", ccode);

      goto errorExitmyObjectBuf;

   }

   else

      printf("\nNWDSPutAttrName, A_OBJECT_CLASS, successful.");



   /*------------------------------------------------

   ** Name the new object.

   */

   ccode = NWDSPutAttrVal(context, myObjectBuf, SYN_DIST_NAME, "MyClass");

   if(ccode)

   {

      printf("\nNWDSPutAttrVal, MyClass, returned %d", ccode);

      goto errorExitmyObjectBuf;

   }

   else

      printf("\nNWDSPutAttrVal, MyClass, successful.");



   /*------------------------------------------------

   ** Specify the name of the attribute to add.

   */

   ccode = NWDSPutAttrName(context, myObjectBuf, "Second Home Directory");

   if(ccode)

   {

      printf("\nNWDSPutAttrName, Second Home Directory, returned %d", ccode);

      goto errorExitmyObjectBuf;

   }

   else

      printf("\nNWDSPutAttrName, Second Home Directory, successful.");



   /*------------------------------------------------

   ** Populate the new attribute with a value.

   */

   ccode = NWDSPutAttrVal(context, myObjectBuf, SYN_CI_STRING, "SERVER\\VOL:DIR\\DIR");

   if(ccode)

   {

      printf("\nNWDSPutAttrVal, SERVER\\VOL:DIR\\DIR, returned %d", ccode);

      goto errorExitmyObjectBuf;

   }

   else

      printf("\nNWDSPutAttrVal, SERVER\\VOL:DIR\\DIR, successful.");



   /*------------------------------------------------

   ** Create the new object using the template of

   ** "MyClass."

   */

   ccode = NWDSAddObject(context, "myObject", NULL, 0, myObjectBuf);

   if((ccode == DSERR_OBJECT_ALREADY_EXISTS) || (ccode == ERR_ENTRY_ALREADY_EXISTS))

   {

      printf("\nNWDSAddObject, myObject, already exists.");

      goto removeMessage;

   }

   else if(ccode)

   {

      printf("\nNWDSAddObject, myObject, returned %d", ccode);

      goto errorExitmyObjectBuf;

   }

   else

      printf("\nNWDSAddObject, myObject, successful.");



   /*------------------------------------------------

   ** Free myObjectBuf buffer.

   */

   ccode = NWDSFreeBuf(myObjectBuf);

   myObjectBufFlag = TRUE;

   if(ccode)

   {

      printf("\nNWDSFreeBuf, myObjectBuf, reutrned %d", ccode);

      goto errorExitmyClassBuf;

   }

   else

      printf("\nNWDSFreeBuf, muObjectBuf, successful.");



/**************************************************************************

** Pause after creating the new attribute definition, class definition

** and object.  This is so the companion program, READSCHM can discplay

** the value of the Second Home Directory attribute.

**************************************************************************/

removeMessage:

   

   /*------------------------------------------------

   ** Wait for key press before we delete the

   ** new class and "Second Home Directory" attribute.

   */

   printf("\nPress any key to delete the 'Second Home Directory' attribute.\n");

   getch();

   printf("\nStart process of deleting 'Second Home Directory' attribute.");



   /*------------------------------------------------

   ** Remove the new object.

   */

   ccode = NWDSRemoveObject(context, "myObject");

   if(ccode == ERR_NO_SUCH_ENTRY)

   {

      printf("\nNWDSRemoveObject, myObject, does not exist.");

      goto removeClass;

   }   

   else if(ccode != 0)

   {

      printf("\nNWDSRemoveObject, myObject, returned %d", ccode);

      goto errorExitmyObjectBuf;

   }

   else

      printf("\nNWDSRemoveObject, myObject, successful.");



   /*------------------------------------------------

   ** Remove the definition for "MyClass."

   */

removeClass:

   ccode = NWDSRemoveClassDef(context, "MyClass");

   if(ccode == ERR_NO_SUCH_CLASS)

   {

      printf("\nNWDSRemoveClassDef, MyClass, does not exist.");

      goto removeAttr;

   }   

   else if(ccode != 0)

   {

      printf("\nNWDSRemoveClassDef, MyClass, returned %d", ccode);

      goto errorExitmyObjectBuf;

   }

   else

      printf("\nNWDSRemoveClassDef, MyClass, successful.");



   /*------------------------------------------------

   ** Remove the new attribute definition.

   */

removeAttr:   

   ccode = NWDSRemoveAttrDef(context, "Second Home Directory");

   if(ccode == ERR_NO_SUCH_ATTRIBUTE)

   {

      printf("\nNWDSRemoveAttrDef, Secpnd Home Directory, does not exist.");

      goto cleanup;

   }   

   else if(ccode != 0)

   {

      printf("\nNWDSRemoveAttrDef, Second Home Directory, returned %d", ccode);

      goto errorExitmyObjectBuf;

   }

   else

      printf("\nNWDSRemoveAttrDef, Second Home Directory, successful.");

      

   /*------------------------------------------------

   ** Cleanup.

   */

cleanup:   

errorExitmyObjectBuf:

   if(myObjectBufFlag == FALSE)

      NWDSFreeBuf(myObjectBuf);

/* function always returns 0 */

   

errorExitmyClassBuf:

   if(myClassBufFlag == FALSE)

      NWDSFreeBuf(myClassBuf);

/* function always returns 0 */



errorExitLogin:

   ccode = NWDSLogout(context);

   if(ccode)

      printf("\nNWDSLogout returned %d", ccode);

   else

      printf("\nNWDSLogout successful.");



errorExitContext:

   ccode = NWDSFreeContext(context);

   if(ccode)

      printf("\nNWDSFreeContext returned %d", ccode);

   else

      printf("\nNWDSFreeContext successful.");



errorExitGeneral:

   return;    

}

/**************************************************************************

**************************************************************************/

Sample Program #2, READSCHM.C.

/**************************************************************************

** Copyright (c) 1997 Novell, Inc.  All Rights Reserved.

**

** THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND

** TREATIES.  USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE

** LICENSE AGREEMENT ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK)

** THAT CONTAINS THIS WORK.

** 

** Pursuant to the SDK License Agreement, Novell hereby grants to

** Developer a royalty-free, non-exclusive license to include the

** sample code READSCHM.C and derivative binaries in its product.

** Novell grants to Developer worldwide distribution rights to market,

** distribute or sell the sample code READSCHM.C and derivative

** binaries as a component of Developer's product(s).  Novell shall

** have no obligations to Developer or Developer's customers with

** respect to this code.

** 

** DISCLAIMER:

** 

**  Novell, Inc. makes no representations or warranties with respect

** to the contents or use of this code, and specifically disclaims any

** express or implied warranties of merchantability or fitness for any

** particular purpose.  Further, Novell, Inc. reserves the right to revise

** this publication and to make changes to its content, at any time,

** without obligation to notify any person or entity of such revisions or

** changes.

**

**  Further, Novell, Inc. makes no representations or warranties with

** respect to any software, and specifically disclaims any express or

** implied warranties of merchantability or fitness for any particular

** purpose.  Further, Novell, Inc. reserves the right to make changes to

** any and all parts of the software, at any time, without obligation to

** notify any person or entity of such changes.

**

***************************************************************************

**

** File: READSCHM.C

**

** This program will continually loop to read the value stored in 

** myObject's Second Home Directory.

**

**       

** Programmers:

**

** Ini   Who                  Firm

** ---------------------------------------------------------------------

** KDB   Kevin D. Burnett     Novell Developer Support

**

**

** History:

**

** When        Who      What

** ---------------------------------------------------------------------

** 3-07-1997   KDB      First code.

** 3-10-1997   KDB      Modifications and comments.

** 3-15-1997   KDB      Final comments and documentation.

**

**************************************************************************/



/**************************************************************************

** Headers and function prototypes.

*/



   /*------------------------------------------------

   ** Defines

   */

   #define N_PLAT_NLM /* X-plat libraries need to know the platform. */

   #define TRUE 1

   #define FALSE 0

   

   /*------------------------------------------------

   ** ANSI Include headers.

   */

   #include <stdio.h<<
   #include <string.h<<


   /*------------------------------------------------

   ** Novell specific Include headers.

   */

   

   #include <nwdsapi.h<<
   #include <nwdsdc.h<<
   #include <nwdsdefs.h<<
   #include <nwapidef.h<<
   #include <nwthread.h<<
   #include <nwconio.h<<
   

/**************************************************************************

** Watcom's IDE requires this function.

*/

void __WATCOM_Prelude(void)

{

   return;

}



/**************************************************************************

** main

*/

void main( void )

{

   NWDSCCODE         ccode;



   NWDSContextHandle context;

   pBuf_T            info;

   pBuf_T            attrNames;

   NWDS_ITERATION    iterHand;

   nstr8             attributeName[MAX_DN_CHARS];

   nuint32           attrCount;

   nuint32           size;

   nuint32           syntax;

   nuint32           valCount;

   nuint32           attributeVal[MAX_DN_CHARS];

   nuint32           valueFound;

   nstr8             userName[NW_MAX_USER_NAME_LEN];

   nstr8             userPassword[50];



   /*------------------------------------------------

   ** Create a NDS context.

   */

   context = NWDSCreateContext();

   if(context == ERR_CONTEXT_CREATION)

   {

      printf("\nNWDSCreateContext failed.");

      goto errorExitGeneral;

   }



   /*------------------------------------------------

   ** As an NLM, we need to be authenticated to NDS.

   ** Get user name and password.

   */

   printf("\nMust authenticate to NDS");

   printf("\nEnter User name: ");

   gets(userName);

   printf("Enter User Password: ");

   gets(userPassword);



   /*------------------------------------------------

   ** Perform the Login/authentication.

   */

   ccode = NWDSLogin(context, 0, userName, userPassword, 0);

   if(ccode != 0)

   {

      printf("\nNWDSLogin returned %d", ccode);

      goto errorExitContext;

   }



   /*------------------------------------------------

   ** Allocate needed buffers.

   */

   ccode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &info);&
   if(ccode !=0)

   {

      printf("\nNWDSAllocBuf (info) returned %d", ccode);

      goto errorExitLogin;

   }

      

   ccode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &attrNames);&
   if(ccode != 0)

   {

      printf("\nNWDSAllocBuf (attrNames) returned %d", ccode);

      goto errorExitBufinfo;

   }



   /*------------------------------------------------

   ** Set up a loop to continually check myObject's

   ** Second Home Directory value.

   */

   do

   {

      /*------------------------------------------------

      ** Initialize attribute structure.

      */

      memset(attributeVal, 0, sizeof(attributeVal));

      valueFound = 0;



      /*------------------------------------------------

      ** Initialize the buffer for a NDS read.

      */

      ccode = NWDSInitBuf(context, DSV_READ, attrNames);

      if (ccode)

      {

         printf("\nNWDSInitBuf returned %d", ccode);

         break;

      }



      /*------------------------------------------------

      ** Indicate that we want to read the

      ** Second Home Directory attribute.

      */

      ccode = NWDSPutAttrName(context, attrNames, "Second Home Directory");

      if (ccode)

      {

         printf("\nNWDSPutAttrName returned %d", ccode);

         break;

      }

      

      /*------------------------------------------------

      ** Perform the actual read.

      */

      iterHand = -1;

      ccode = NWDSRead(context, "myObject", DS_ATTRIBUTE_VALUES, FALSE, attrNames, &iterHand, info);&
      if ((ccode != ERR_NO_SUCH_ENTRY) && (ccode !=0))

      {

         printf("\nNWDSRead returned %d", ccode);

         break;

      }

      

      /*------------------------------------------------

      ** Only parse the information if the NDSRead

      ** was successful.

      */

      else if(!ccode)

      {

         

         /*------------------------------------------------

         ** Determine how may attributes were read.

         ** (Should only be one.)

         */

         ccode = NWDSGetAttrCount(context, info, &attrCount);&
         if (ccode)

         {

            printf("\nNWDSGetAttrCount returned %d", ccode);

            break;

         }

         

         /*------------------------------------------------

         ** Did we get back the attribute?

         */

         if (attrCount)

         {

            

            /*------------------------------------------------

            ** Get the attribute name.

            */

            ccode = NWDSGetAttrName(context, info, attributeName, &syntax, &valCount);&
            if (ccode)

            {

               printf("\nNWDSGetAttrName returned %d", ccode);

               break;

            }

               

            /*------------------------------------------------

            ** Did we get a value for the attribute?

            */

            if (valCount)

            {

               

               /*------------------------------------------------

               ** Calculate the attribute size.

               */

               ccode = NWDSComputeAttrValSize(context, info, syntax, &size);&
               if (ccode)

               {

                  printf("\nNWDSComputeAttrValSize returned %d", ccode);

                  break;

               }

                 

               /*------------------------------------------------

               ** Check to see that the size of the returned

               ** attribute is less than a legally defined

               ** distinguished name.  (If it were larger, the

               ** iteration handle would need to be used to

               ** read the return buffer multiple times.)

               */

               if (size < sizeof(attributeVal))

               {

                  

                  /*------------------------------------------------

                  ** Read the value to the Second Home Directory

                  ** attribute.

                  */

                  ccode = NWDSGetAttrVal(context, info, syntax, attributeVal);

                  if (ccode)

                  {

                     printf("\nNWDSGetAttrVal returned %d", ccode);

                     break;

                  }

                  if (!ccode)

                     valueFound = 1;

               }

            }

         }

       }



      /*------------------------------------------------

      ** Did we get a value?  If so, print it out.

      ** If not, print a message.

      */

      if (valueFound)

         printf("myObject has a Second Home Directory value of:

* Originally published in Novell AppNotes


Disclaimer

The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.

© Copyright Micro Focus or one of its affiliates