## The thradd.c Code

The following code is a listing of the thradd.c program:

/*

* The program operates in two modes; it adds or removes entries of

* object type organizational person to/from a directory. The

* information about the entries is read from a file.

*

* The program requires that a tree exists in the directory.

* Therefore, each time the program runs, the following tree of 3

* entries is added to or removed from the directory, according

* to the operation mode.

*

* O C=it

* | (objectClass=Country)

* |

* O O=sni

* | (objectClass=Organization)

* |

* O OU=ap

* (objectClass=OrganizationalUnit)

*

* Information about the organizational persons to be added or

* removed is read from the input file. It may contain any number

* of lines, where each line must have the following syntax:

*

* <common name> <surname> <phone number>

* Each item must be a string without a space.

*

* Lines containing less than 3 strings are rejected by the

* program. The program does not check to see if the strings conform

* to the appropriate attribute syntax; that is a wrong attribute

* syntax will lead to a ds_add_entry error, or to a

* ds_remove_entry error.

*

* Usage: thradd [-d] [-f<file_name>]

* -d If the option -d is set, the entries in the

* file and the tree described above are removed,

* otherwise they are added.

* -f<file_name> The option -f specifies the name of the input

* file. If left out, the default "thradd.dat"

* is used.

*/

/* Step 1 */

/*

* Header file for thread programming:

*/

#include <pthread.h>

#include <stdio.h>

#include <xom.h>

#include <xds.h>

#include <xdsbdcp.h>

#include <xdsgds.h>

#include <xdscds.h>

#include "thradd.h" /* static data structures. */

/* Step 2 */

/*

* typedef for parameter block of function add_or_remove

* (this is necessary because the start function of a thread

* takes only 1 parameter). The following 3 parameters are

* passed to add_or_remove:

*

* Input - Session object from the ds_bind call

* Input - Buffer with the entry information

* Input - "adding" or "removing" mode ?

*/

typedef struct {

OM_private_object session;

char line[MAX_LINE_LEN+1];

int do_remove;

} pb_add_or_remove;

/*

* static constants:

*

* Default name for input file containing entry information.

*/

static char fn_default[] = "thradd.dat";

/*

* function declarations:

*/

char *own_fgets(char *s, int n, FILE *f);

void add_or_remove(pb_add_or_remove *pb);

int

main(

int argc,

char *argv[]

)

{

OM_workspace workspace; /* workspace for objects */

OM_private_object bound_session; /* Holds the Session */

/* returned by ds_bind() */

FILE *fp; /* pointer for input file*/

int do_remove = FALSE; /* "adding" or "removing"*/

int error = FALSE; /* error in options ? */

int is_eof = FALSE; /* EOF input file reached*/

int thread_count; /* no. of created threads*/

char *file_name; /* ptr to input file_name*/

/* Step 3 */

pthread_t threads[MAX_THREAD_NO]; /* thread table */

pb_add_or_remove param_block[MAX_THREAD_NO]; /* 1 param block*/

/* for start routine per thread */

int dummy;

int c;

int i;

extern char *optarg;/* external variable used by getopt */

extern int optind; /* external variable used by getopt */

/*

* scan options -d and -f

*/

file_name = fn_default;

while ((c=getopt(argc, argv, "df:")) != EOF)

{

switch (c)

{

case 'd':

do_remove = TRUE;

break;

case 'f':

file_name = optarg;

break;

default:

error = TRUE;

break;

}

}

if (error)

{

printf("usage: %s [-d] [-f<file_name>]\n", argv[0]);

return(FAILURE);

}

if (( fp = fopen(file_name, "r")) == (FILE *) NULL)

{

printf("fopen() error, file name: %s\n", file_name);

return(FAILURE);

}

/*

* Initialize a directory workspace for use by XOM.

*/

if ((workspace = ds_initialize()) == (OM_workspace)0)

printf("ds_initialize() error\n");

/*

* Negotiate the use of the BDCP and GDS packages.

*/

if (ds_version(features, workspace) != DS_SUCCESS)

printf("ds_version() error\n");

/*

* Add the fixed tree of entries, if in adding mode

*/

if (!do_remove)

if (add_entries(workspace))

printf("add_entries() error\n");

/*

* Bind to the default GDS server.

* The returned session object is stored in the private

* object variable bound_session and is used for further

* XDS function calls.

*/

if (ds_bind(DS_DEFAULT_SESSION, workspace, &bound_session)

!= DS_SUCCESS)

printf("ds_bind() error\n");

/* Step 4 */

/*

* Add or remove entries described in input file. This is done

* in parallel, creating up to MAX_THREAD_NO threads at a time.

*/

while (!is_eof)

{

for (thread_count=0; thread_count<MAX_THREAD_NO;

thread_count++)

{

/* Step 5 */

/*

* Prepare parameter block:

*/

is_eof = (own_fgets(param_block[thread_count].line,

MAX_LINE_LEN, fp) == NULL);

if (is_eof)

break;

param_block[thread_count].session = bound_session;

param_block[thread_count].do_remove = do_remove;

/* Step 6 */

/*

* Create thread with start routine add_or_remove:

*/

if (pthread_create(&threads[thread_count],

pthread_attr_default,

(pthread_startroutine_t) add_or_remove,

(pthread_addr_t) &param_block[thread_count])

!= SUCCESS)

printf("pthread_create() error\n");

} /* end for */

/*

* Wait for termination of the created threads and release

* resources:

*/

for (i=0; i<thread_count; i++)

{

/* Step 7 */

/*

* Wait for termination of thread

* (If thread has terminated already, the function

* returns immediately):

*/

if (pthread_join(threads[i], (pthread_addr_t) &dummy)

!= SUCCESS)

printf("pthread_join() error\n");

/* Step 8 */

/*

* Release resources used by the thread:

*/

if (pthread_detach(&threads[i]) != SUCCESS)

printf("pthread_detach() error\n");

} /* end for */

} /* end while */

/*

* Close the connection to the GDS server.

*/

if (ds_unbind(bound_session) != DS_SUCCESS)

printf("ds_unbind() error\n");

if (om_delete(bound_session) != OM_SUCCESS)

printf("om_delete() error\n");

/*

* Remove the tree from the directory, if removing mode

*/

if (do_remove)

if (remove_entries(workspace))

printf("remove_entries() error\n");

/*

* Close the directory workspace.

*/

if (ds_shutdown(workspace) != DS_SUCCESS)

printf("ds_shutdown() error\n");

fclose(fp);

return(SUCCESS);

} /* end main() */

/* Step 9 */

/*

* Handle (add or remove) a directory entry

*/

void

add_or_remove(

pb_add_or_remove *pb /* parameter information */

)

{

/*

* further local variables:

*/

char common_name[MAX_AT_LEN+1];

char phone_num[MAX_AT_LEN+1];

char surname[MAX_AT_LEN+1];

OM_sint invoke_id;

/* Step 10 */

/*

* local variables for descriptors for objects read from file

*/

OM_descriptor ava_genop[] = {

OM_OID_DESC(OM_CLASS, DS_C_AVA),

OM_OID_DESC(DS_ATTRIBUTE_TYPE, DS_A_COMMON_NAME),

OM_NULL_DESCRIPTOR, /* place holder */

OM_NULL_DESCRIPTOR

};

OM_descriptor rdn_genop[] = {

OM_OID_DESC(OM_CLASS, DS_C_DS_RDN),

OM_NULL_DESCRIPTOR, /* place holder */

OM_NULL_DESCRIPTOR

};

OM_descriptor dn_genop[] = {

OM_OID_DESC(OM_CLASS,DS_C_DS_DN),

{DS_RDNS,OM_S_OBJECT,{0,rdn_it}},

{DS_RDNS,OM_S_OBJECT,{0,rdn_sni}},

{DS_RDNS,OM_S_OBJECT,{0,rdn_ap}},

OM_NULL_DESCRIPTOR, /* place holder */

OM_NULL_DESCRIPTOR

};

OM_descriptor att_phone_num[] = {

OM_OID_DESC(OM_CLASS, DS_C_ATTRIBUTE),

OM_OID_DESC(DS_ATTRIBUTE_TYPE, DS_A_PHONE_NBR),

OM_NULL_DESCRIPTOR, /* place holder */

OM_NULL_DESCRIPTOR

};

OM_descriptor att_surname[] = {

OM_OID_DESC(OM_CLASS, DS_C_ATTRIBUTE),

OM_OID_DESC(DS_ATTRIBUTE_TYPE, DS_A_SURNAME),

OM_NULL_DESCRIPTOR, /* place holder */

OM_NULL_DESCRIPTOR

};

OM_descriptor alist_OP[] = {

OM_OID_DESC(OM_CLASS, DS_C_ATTRIBUTE_LIST),

{DS_ATTRIBUTES, OM_S_OBJECT, {0, obj_class_OP} },

OM_NULL_DESCRIPTOR, /* place holder */

OM_NULL_DESCRIPTOR, /* place holder */

OM_NULL_DESCRIPTOR

};

rdn_genop[1].type = DS_AVAS;

rdn_genop[1].syntax = OM_S_OBJECT;

rdn_genop[1].value.object.padding = 0;

rdn_genop[1].value.object.object = ava_genop;

dn_genop[4].type = DS_RDNS;

dn_genop[4].syntax = OM_S_OBJECT;

dn_genop[4].value.object.padding = 0;

dn_genop[4].value.object.object = rdn_genop;

alist_OP[2].type = DS_ATTRIBUTES;

alist_OP[2].syntax = OM_S_OBJECT;

alist_OP[2].value.object.padding = 0;

alist_OP[2].value.object.object = att_surname;

alist_OP[3].type = DS_ATTRIBUTES;

alist_OP[3].syntax = OM_S_OBJECT;

alist_OP[3].value.object.padding = 0;

alist_OP[3].value.object.object = att_phone_num;

if (sscanf(pb->line, "%s %s %s", common_name,

surname, phone_num) != 3)

{

printf("invalid input line: >%s<\n", pb->line);

return;

}

/*

* Fill descriptor for common name

*/

ava_genop[2].type = DS_ATTRIBUTE_VALUES;

ava_genop[2].syntax = OM_S_PRINTABLE_STRING;

ava_genop[2].value.string.length =

(OM_string_length)strlen(common_name);

ava_genop[2].value.string.elements = common_name;

if (!pb->do_remove) /* add */

{

/*

* Fill descriptors for surname and phone number

*/

att_surname[2].type = DS_ATTRIBUTE_VALUES;

att_surname[2].syntax = OM_S_TELETEX_STRING;

att_surname[2].value.string.length =

(OM_string_length)strlen(surname);

att_surname[2].value.string.elements = surname;

att_phone_num[2].type = DS_ATTRIBUTE_VALUES;

att_phone_num[2].syntax = OM_S_PRINTABLE_STRING;

att_phone_num[2].value.string.length =

(OM_string_length)strlen(phone_num);

att_phone_num[2].value.string.elements = phone_num;

/*

* add entry

*/

if (ds_add_entry(pb->session, DS_DEFAULT_CONTEXT, dn_genop,

alist_OP, &invoke_id) != DS_SUCCESS)

printf("ds_add_entry() error: %s %s %s\n",

common_name, surname, phone_num);

else

printf("entry added: %s %s %s\n",

common_name, surname, phone_num);

}

else /* remove */

{

/*

* remove entry

*/

if (ds_remove_entry(pb->session, DS_DEFAULT_CONTEXT,

dn_genop, &invoke_id) != DS_SUCCESS)

printf("ds_remove_entry() error: %s\n", common_name);

else

printf("entry removed: %s\n", common_name);

} /* end if */

} /* end add_or_remove() */

/*

* Add the tree of entries described above.

*/

int

add_entries(

OM_workspace workspace /* In -- XDS workspace */

)

{

OM_private_object bound_session; /* Holds Session object */

/* returned by ds_bind() */

OM_sint invoke_id;

int error = FALSE;

/* Bind (without credentials) to the default GDS server */

if (ds_bind(DS_DEFAULT_SESSION, workspace, &bound_session)

!= DS_SUCCESS)

error = TRUE;

/* Add entries to the GDS server */

if (ds_add_entry(bound_session, DS_DEFAULT_CONTEXT, dn_it,

alist_C, &invoke_id) != DS_SUCCESS)

error = TRUE;

if (ds_add_entry(bound_session, DS_DEFAULT_CONTEXT, dn_sni,

alist_O, &invoke_id) != DS_SUCCESS)

error = TRUE;

if (ds_add_entry(bound_session, DS_DEFAULT_CONTEXT, dn_ap,

alist_OU, &invoke_id) != DS_SUCCESS)

error = TRUE;

/* Close the connection to the GDS server */

if (ds_unbind(bound_session) != DS_SUCCESS)

error = TRUE;

if (om_delete(bound_session) != OM_SUCCESS)

error = TRUE;

return (error);

}

/*

* Remove the tree of entries described above.

*/

int

remove_entries(

OM_workspace workspace /* In -- XDS workspace */

)

{

OM_private_object bound_session; /* Holds Session object */

/* returned by ds_bind() */

OM_sint invoke_id;

int error = FALSE;

/* Bind to the default GDS server */

if (ds_bind(DS_DEFAULT_SESSION, workspace, &bound_session)

!= DS_SUCCESS)

error = TRUE;

/* Remove entries from the GDS server */

if (ds_remove_entry(bound_session, DS_DEFAULT_CONTEXT,

dn_ap, &invoke_id) != DS_SUCCESS)

error = TRUE;

if (ds_remove_entry(bound_session, DS_DEFAULT_CONTEXT,

dn_sni, &invoke_id) != DS_SUCCESS)

error = TRUE;

if (ds_remove_entry(bound_session, DS_DEFAULT_CONTEXT,

dn_it, &invoke_id) != DS_SUCCESS)

error = TRUE;

/* Close the connection to the GDS server */

if (ds_unbind(bound_session) != DS_SUCCESS)

error = TRUE;

if (om_delete(bound_session) != OM_SUCCESS)

error = TRUE;

return (error);

}

/*

* read one line with fgets and overwrite new line by

* a null character

*/

char *

own_fgets(

char *s, /* OUT -- string read */

int n, /* IN --- maximum number of chars to be read */

FILE *f /* IN --- input file */

)

{

char *result;

int i = 0;

result = fgets(s, n, f);

if (result != NULL)

{

i = strlen(s);

if (s[i-1] == '\n')

s[i-1] = '\0';

}

return (result);

}

##