/* @(#)95    1.18  src/examples/type_mgr/server_object_db.c, examples.src, os2dce21.dss, 960602a.1 1/12/96 14:31:28 */
/*
 * COMPONENT_NAME:  examples.src
 *
 * FUNCTIONS:
 *
 * ORIGINS: 27
 *
 * (C) COPYRIGHT International Business Machines Corp. 1992, 1994
 * All Rights Reserved
 * Licensed Materials - Property of IBM
 *
 * US Government Users Restricted Rights - Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 */

/*********************************************************************
 *  File      :  server_object_db.c
 *********************************************************************
 *                                                                   *
 *  Functions :  utilities to manage the server's in-memory object   *
 *          table and file based object database.
 *                                                                   *
 *  Comments  :  These routines are called by the server at startup  *
 *         and by the tm_admin manager.                        *
 *                                                                   *
 *********************************************************************/
#ifdef IBMOS2
#include <os2def.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <malloc.h>
#include <dce/pthread.h>
#include <dce/dce_error.h>
#include <dce/rpc.h>
#include "cust_if.h"
#include "type_mgr.h"
#include "server.h"
#include "server_object_db.h"

/*
 *  Function prototypes for private routines
 */

static unsigned32
object_db_check_init( void );

static unsigned32
object_db_init_if_needed( void );

static unsigned32
object_table_entry_add( char       *object_name,
                char     *type_name );

static unsigned32
object_table_entry_delete( char *object_name );

static unsigned32
object_db_file_entry_add( char     *object_name,
              char     *type_name );

static unsigned32
object_db_file_entry_delete( char *object_name );

static unsigned32
object_db_entry_inq_begin_int( object_db_handle_t *context );

static unsigned32
object_db_entry_inq_next_int( object_db_handle_t *context,
              char        **object_name_p,
              char        **type_name_p );

static unsigned32
object_db_entry_inq_done_int( object_db_handle_t *context );


/*********************************************************************
 *                    PUBLIC API
 *********************************************************************
 *   These functions are prototyped in server object_db.h
 *********************************************************************
 *
 *********************************************************************/


/*********************************************************************
 *   Function    :  PUBLIC object_db_init
 *********************************************************************
 *
 *   Note     :  See svrodb.h for comment header.
 *
 *********************************************************************/

unsigned32
object_db_init( void )
{

    return( object_db_init_if_needed() );

} /* object_db_init */


/*********************************************************************
 *   Function    :  PUBLIC object_db_entry_add
 *********************************************************************
 *
 *   Note     :  See svrodb.h for comment header.
 *
 *********************************************************************/

unsigned32
object_db_entry_add( char    *object_name,
             char      *type_name )
{
    unsigned32 status;

    status = object_db_check_init();

    if( status != SUCCESS )
    return( CODING_ERROR );

    /*
     *  Add entry to persistent database.
     */

    status = object_db_file_entry_add( object_name,
                       type_name );

    if( status != SUCCESS )
    return( FAILURE );

    /*
     *  Add entry to in-memory object table.
     */

    status = object_table_entry_add( object_name,
                     type_name );

    if( status != SUCCESS )
    {
    object_db_file_entry_delete( object_name );
    return( FAILURE );
    }

    return( SUCCESS );

}  /* object_db_entry_add */


/*********************************************************************
 *   Function    :  PUBLIC object_db_entry_delete
 *********************************************************************
 *
 *   Note     :  See svrodb.h for comment header.
 *
 *********************************************************************/

unsigned32
object_db_entry_delete( char *object_name )
{
    unsigned32 status;

    status = object_db_check_init();

    if( status != SUCCESS )
    return( CODING_ERROR );

    status = object_table_entry_delete( object_name );

    if( status == NO_MATCH )
    return( status );

    if( status != SUCCESS )
    return( FAILURE );

    status = object_db_file_entry_delete( object_name );

    if( status == NO_MATCH || status == SUCCESS )
    return( status );

    return( FAILURE );

} /* object_db_entry_delete */


/*********************************************************************
 *   Function    :  PUBLIC object_db_entry_inq_begin
 *********************************************************************
 *
 *   Note     :  See svrodb.h for comment header.
 *
 *********************************************************************/

unsigned32
object_db_entry_inq_begin( object_db_handle_t *context )
{

    return( object_db_entry_inq_begin_int( context ));

} /* object_db_entry_inq_begin */


/*********************************************************************
 *   Function    :  PUBLIC object_db_entry_inq_next
 *********************************************************************
 *
 *   Note     :  See svrodb.h for comment header.
 *
 *********************************************************************/

unsigned32
object_db_entry_inq_next( object_db_handle_t *context,
              char        **object_name_p,
              char        **type_name_p )
{

    return( object_db_entry_inq_next_int( context,
                      object_name_p,
                      type_name_p ));

} /* object_db_entry_inq_done_int */


/*********************************************************************
 *   Function    :  PUBLIC object_db_entry_inq_done
 *********************************************************************
 *
 *   Note     :  See svrodb.h for comment header.
 *
 *********************************************************************/

unsigned32
object_db_entry_inq_done( object_db_handle_t *context )
{

    return( object_db_entry_inq_done_int( context ));

} /* object_db_entry_inq_done_int */


/*********************************************************************
 *   Function    :  PUBLIC object_db_entry_query_type
 *********************************************************************
 *
 *   Note     :  See svrodb.h for comment header.
 *
 *********************************************************************/

unsigned32
object_db_entry_query_type( char *object_name,
                char **type_name_p )

{
    object_db_handle_t  h;
    char        *o_name;
    char        *t_name;
    unsigned32        status;

    status = object_db_check_init();

    if( status != SUCCESS )
      return( CODING_ERROR );

    status = object_db_entry_inq_begin( h );

    if( status != SUCCESS )
    {
      return( FAILURE );
    }

    while( TRUE )
    {
      status = object_db_entry_inq_next( h,
                         &o_name,
                         &t_name );
      switch( status )
      {
      case NO_MORE_ENTRIES :
          status = NO_MATCH;
          break;
      case SUCCESS :
          if( strcmp( o_name, object_name ) == 0 )
          {
            if( type_name_p != NULL )
              *type_name_p = t_name;
            status = SUCCESS;
            free( o_name );
            break;
          }
          free( o_name );
          free( t_name );
          continue;
      default:
          status = FAILURE;
          break;
      }
    }

    /*
     * Everything after the close of the while loop is on the common exit path.
     */

    object_db_entry_inq_done(  h );
    return( status );

} /* object_db_entry_query_type */


/*********************************************************************
 *                    PRIVATE API
 *********************************************************************
 *   WARNING: These functions and data structures are for use inside
 *            this module only!
 *********************************************************************
 *
 *********************************************************************/

/*
 *  Internal macros and data structures used to ensure one-time initialzation.
 */

static boolean32  g_object_db_init_state = OBJECT_DB_UNINITIALIZED;
static pthread_once_t object_db_once_block = pthread_once_init;

/*
 *  Structure that holds information about an object entry.  This
 *  includes the objects name, the UUID that represents it, and the
 *  index (into the type table) of the type the object is associated with.
 *
 *  The ref_count is incremented when a context handle points at the
 *  entry and is decremented when a context handle that points at the
 *  entry is reassigned.
 *
 *  When the entry_delete routine is called, the specified table entry is
 *  simply marked for deletion.  A helper thread will actually remove
 *  the entry from the table.
 *
 *  This implementation of the object table uses a doubly linked list with
 *  head and tail pointers.
 */

typedef struct object_db_table_entry {
    char              *object_name;
    char        *type_name;
    unsigned16        ref_count;
    unsigned32        status;
    struct object_db_table_entry    *next, *prev;
} object_db_table_entry_t, *object_db_table_entry_p_t;

#define VALID    (0)
#define DELETED    (1)

static struct{
    object_db_table_entry_p_t  head, tail;
} object_db_table;


/*
 *  Mutex used to lock around accesses to in-memory object table.
 */

static pthread_mutex_t object_db_table_mutex;

/*
 *  Mutex used to lock around accesses to persistent object-database file.
 */

static pthread_mutex_t object_db_file_mutex;

/*
 *  Filename of persistent object database.
 */

static char *g_object_db_filename;

/*
 * Function prototypes.
 */

static void
object_db_init_once( void );

static unsigned32
object_db_table_load_from_file( void );


/**********************************************************************
 *  Function    :  PRIVATE object_db_check_init
 **********************************************************************
 *
 *  Description    :  Returns the state of the object database initialization
 *
 *  Returns    :  SUCCESS / FAILURE
 *
 *  Comments    :  Should be called at entrace of all public object_db*
 *           API calls.
 *
 ***********************************************************************/
static unsigned32
object_db_check_init( void )
{
     switch( g_object_db_init_state )
     {
     case OBJECT_DB_CREATED  :
     case OBJECT_DB_RESTARTED:
     return( SUCCESS );
     default:
     return( FAILURE );
     }
}


/**********************************************************************
 *  Function    :  PRIVATE object_db_init_if_needed
 **********************************************************************
 *
 *  Description    :  If initialization has not been completed, calls
 *            pthread_once to perform initialization.
 *
 *  Returns    :  OBJECT_DB_CREATED /  OBJECT_DB_RESTARTED /
 *           OBJECT_DB_INIT_UNDERWAY / FAILURE
 *
 *  Comments    :  called by public API object_db_init().  The routine
 *           is reentrant in the sense that while initialization
 *           is underway, other calls will be blocked by the
 *           pthread_once mechanism.
 *
 ***********************************************************************/

static unsigned32
object_db_init_if_needed( void )
{
    unsigned32  status;

    /*
     *  pthread_once has no return value, hence we use a global variable
     *  to inquire as to the state of things.
     */

    status =  g_object_db_init_state;

    switch( status )
    {
    case OBJECT_DB_CREATED         :
    case OBJECT_DB_RESTARTED    :
    case FAILURE              :
    /*
     *  Although it would be safe to call pthread_once in these cases,
     *  there is no reason to waste the time.  In fact, if the calling
     *  routine is clever, it would check some global variable to
     *  decide if the init_if_needed routine needed to be called at all.
     *  We buried our such global variable in a check_init routine
     *  because of its having two "success" values, so not much would
     *  be gained in this program.
     */
    return( status );
    case OBJECT_DB_INIT_UNDERWAY:
    /*
     *  Typically, we want any thread finding initialization underway
     *  to simply block in pthread_once until initialization is complete.
     *
     *  Special handling of this case is necessary if it is possible for
     *  a thread to recursively call this routine (recursive calls to
     *  pthread_once will deadlock).  This is unlikely in the current
     *  version of this program, since only the public API object_db_init
     *  calls this routine and there are no cycles in the initialization
     *  call graph.  So, in this version, if initialization is underway,
     *  the newly arriving thread can simply block in pthread_once.
     *
     *  If recursive calling of pthread_once is a possibility, one
     *  alternative is to have the thread performing the initialization
     *  (i.e., the thread that makes it into the once routine) call
     *  pthread_self to acquire its thread id, then set a global
     *  variable to this value.  Prior to calling pthread_once, each
     * tthread calls pthread_self and compares this id with the
     *  initializing thread's id using pthread_equal.  If the id's
     *  are different, then it is okay to go ahead and block on
     *  pthread_once.  If the id's match, about all that can be done
     *  is to return from this function to avoid a deadlock.
     *
     *  If you are still paying attention, you might note that the first
     *  thread to call pthread_equal will be comparing its to an
     *  uninitialized global initializing thread variable. Take a look
     *  at the pthread_equal macro (<dce/pthread.h>) and see why this
     *  should be okay. We could not figure out any reasonable way to
     *  initialize the variable, i.e., what is an invalid thread id?
     */

    break;

    default:    /* case UNINITIALIZED: */
    break;
    }

    /*
     * The first thread into pthread_once will perform the initialization.
     * All subsequent calling threads block until this thread is finished
     * executing.
     */

    pthread_once( &object_db_once_block,
          (pthread_initroutine_t) object_db_init_once );

    status = g_object_db_init_state;

    return( status );

} /* object_db_init_if_needed */


/****************************************************************************
 *  Function    :  PRIVATE object_db_init_once
 ****************************************************************************
 *
 *  Description    :  performs one time initialization of the object
 *           table data structures.
 *
 *  Returns    :  none
 *
 *  Side Effects:  g_object_db_init_state
 *            OBJECT_DB_INIT_UNDERWAY /
 *            OBJECT_DB_CREATED /
 *            OBJECT_DB_RESTARTED /
 *            FAILURE
 *
 *
 *  Comments    :  Performs actual initialization of object database
 *           structures.  Sets global variables to indicate
 *           initialization in underway.
 *
 *           At the end of the routine sets global variable
 *           to indicate initialization complete.
 ***************************************************************************/

static void
object_db_init_once( void )
{
    unsigned32  status = 0;

    g_object_db_init_state = OBJECT_DB_INIT_UNDERWAY;

    pthread_mutex_init( &object_db_table_mutex,
                pthread_mutexattr_default );

    pthread_mutex_init( &object_db_file_mutex,
                pthread_mutexattr_default );

    /* need to check and report bad status from above routine */

    object_db_table.head = object_db_table.tail = NULL;

    /*
     *  Test to ensure the directory path to the persistent
     *  database file is defined in the environment (type_mgr.h).
     */

#ifndef SERVER_OBJECT_DB_PATH
#error Variable SERVER_OBJECT_DB_PATH not defined.
#endif

    /*
     *  Test to ensure the prefix for the  persistent
     *  database file is defined in the environment (type_mgr.h).
     */

#ifndef SERVER_OBJECT_DB_PREFIX
#error Variable SERVER_OBJECT_DB_PREFIX not defined.
#endif

    /*
     *  Compose persistent object database filename.
     *  (assumes SERVER_OBJECT_DB_PATH is / terminated)
     */

    g_object_db_filename =
    (char *)malloc( strlen( SERVER_OBJECT_DB_PATH )
                + strlen( SERVER_OBJECT_DB_PREFIX )
                + strlen(g_server_ns_entry) + 1);

    if( g_object_db_filename == NULL )
    {
    status = FAILURE;
    goto exit;
    }

    strcpy( g_object_db_filename, SERVER_OBJECT_DB_PATH );
    strcat( g_object_db_filename, SERVER_OBJECT_DB_PREFIX );
    strcat( g_object_db_filename, g_server_ns_entry );
#ifdef IBMOS2
    {
      char * tpch = strchr(g_object_db_filename,'@');
      if ( tpch )
      {
        *tpch = '\0';
        strcat(g_object_db_filename,".db");
      }
    }
#endif

    /*
     *  Must be called AFTER g_object_db_filename is composed.
     */

    status = object_db_table_load_from_file();

 exit:

    /*
     *  This must be the last action of this init routine.
     */

    g_object_db_init_state = status;

} /* object_db_init_once */


/*********************************************************************
 *   Function    :  PRIVATE object_db_table_load_from_file
 *********************************************************************
 *
 *   Description :  Read the object data file (ie the local object
 *                  database), and load the object entry names into
 *                  in-memory table
 *
 *   Returns     :  OBJECT_DB_CREATED / OBJECT_DB_RESTARTED / FAILURE
 *
 *   Comments    :  This routine should only be called from the one-time
 *            initialization routine.  There is no testing of
 *            database status or mutexs so the assumption is
 *            the only thread executing in the database is the
 *            thread executing this routine as part of the
 *            database initialization.
 *
 *********************************************************************/

static unsigned32
object_db_table_load_from_file()
{
    FILE         *fp;
    char         *p, in_buf[BUFSIZ];
    unsigned32   status;


    p = in_buf;

    fp = fopen( g_object_db_filename ,"r");

    if ( fp == NULL )
    {
        /*
         * There is no object data file.  This means either the server is
         * being started for the first time, or (bad thing) the
     * object_data_file has been deleted.
         */

        fclose ( fp );

    if(( fp = fopen( g_object_db_filename ,"w")) == NULL)
    {
        status = FAILURE;
        goto exit;
    }

    sprintf( in_buf,
          "### WARNING! TYPE MANAGER %s OBJECT DATABASE"\
         "- DO NOT EDIT! ###\n",
          g_server_ns_entry );

    if( fputs( in_buf, fp ) == EOF )
    {
        status = FAILURE;
        goto exit;
    }

    status = OBJECT_DB_CREATED;
    goto exit;
    }

    /*
     *  Strip off the warning line at the top of the file.
     */

    if( fgets( in_buf, BUFSIZ, fp ) == NULL )
    {
    status = FAILURE;
    goto exit;
    }

    /*
     *  Verify the file belongs to the database.
     */

    if( strstr( in_buf, g_server_ns_entry ) == NULL )
    {
    status = FAILURE;
    goto exit;
    }

    /*
     *  Read object entries from the file, create object table entries.
     */

    while( fgets( in_buf, BUFSIZ, fp ) != NULL)
    {
    char    object_name_buf[NAME_SIZE];
    char    type_name_buf[NAME_SIZE];

         /* scan the object name and type name from the input buf */

    if( sscanf( in_buf, "%s %s", object_name_buf, type_name_buf ) != 2 )
    {
        status = FAILURE;
        goto exit;
    }

    /*
     *  add the object to the server's table
     */

         status = object_table_entry_add( object_name_buf,
                      type_name_buf );

    /*
     *  Should not be any problem with duplicate entries at this point.
     */

         if ( status != SUCCESS )
         {
             status = FAILURE;
         goto exit;
     }
    }
    status = OBJECT_DB_RESTARTED;

 exit:

    if( fp != NULL )
    fclose(fp);

    return( status );

} /* object_db_table_load_from_file */

/*********************************************************************
 *   Function    :  PRIVATE object_table_entry_add
 *********************************************************************
 *
 *   Description :  Allocate the memory for the new object entry
 *            and add it to the server's in-memory object
 *                  table.
 *
 *   Returns     :  SUCCESS / FAILURE
 *
 *   Comments    :  Does not check for duplicate entries.
 *
 *********************************************************************/

static unsigned32
object_table_entry_add( char    *object_name,
                char      *type_name )
{
    object_db_table_entry_p_t    p;

    /*
     *  allocate, initialize, assign the object table entry
     */

    p = (object_db_table_entry_p_t )malloc( sizeof( object_db_table_entry_t ));

    if( p == NULL )
    {
    return( FAILURE );
    }

    if(( p->object_name = ( char *)malloc( strlen( object_name ))) == NULL )
    {
    free( p );
    return( FAILURE );
    }

    if(( p->type_name = ( char *)malloc( strlen( type_name ))) == NULL )
    {
    free( p );
    return( FAILURE );
    }

    strcpy( p->object_name, object_name );
    strcpy( p->type_name, type_name );
    p->ref_count = 0;
    p->status = VALID;

    p->next = p->prev = NULL;

    pthread_mutex_lock( &object_db_table_mutex );

    /*
     * add object to in-memory list
     */

    if( object_db_table.head == NULL )
    {
    object_db_table.head = object_db_table.tail = p;
    }
    else
    {
    p->prev = object_db_table.tail;
    object_db_table.tail->next = p;
    object_db_table.tail = p;
    }

    pthread_mutex_unlock( &object_db_table_mutex );

    return ( SUCCESS );

} /* object_table_entry_add */


/*********************************************************************
 *   Function    :  PRIVATE object_table_entry_delete
 *********************************************************************
 *
 *   Description :  Remove the object entry from the server's
 *            object table.
 *
 *   Returns     :  SUCCESS / FAILURE / NO_MATCH
 *
 *   Comments    :  Deletes all instances of entries with matching
 *            object name.
 *                                                                   *
 *********************************************************************/

static unsigned32
object_table_entry_delete( char *object_name )
{
    object_db_table_entry_p_t  h;
    unsigned32      status;
    char       *o_name;

    /*
     *  don't lock mutex here, else will deadlock in search routine
     *  (one argument for using a recursive mutex for the object table)
     */

    status = object_db_entry_inq_begin( (object_db_handle_t *)&h );

    if( status != SUCCESS )
    {
    return( FAILURE );
    }

    while((status = object_db_entry_inq_next( (object_db_handle_t *)&h,
                                     &o_name,
                                     (char **)NULL )) != NO_MORE_ENTRIES)
    {
        if( strcmp( o_name, object_name ) == 0 )
        {
            /*
             *  Just mark the list element for deletion, the helper
             *  thread will take  care of it (once the ref count == 0).
             *  Might as well do all of the housekeeping in one place.
             *
             *  In a sense we are cheating here by taking advantage of
             *  our knowledge that the context handle points to the
             *  object_db_table_entry_t that we want to delete.
             *
             *  By the way, the helper thread may not be implemented yet.
             *  the extra deleted object list entries hanging around
             *  shouldn't hurt anything (Other routines should ignore).
             */

            pthread_mutex_lock( &object_db_table_mutex );
            h->status = DELETED;
            pthread_mutex_unlock( &object_db_table_mutex );
            free((char *)o_name);
            break;
        }
        free((char *)o_name);
    } /* end while */

    object_db_entry_inq_done( (object_db_handle_t *)&h );

    if( status == NO_MORE_ENTRIES )
        status = NO_MATCH;
    else
        status = SUCCESS;

    return( status );

} /* object_table_entry_delete */


/*********************************************************************
 *   Function    :  PRIVATE object_db_file_entry_add
 *********************************************************************
 *
 *   Description :  Add an object to the server's local database.
 *
 *   Returns     :  SUCCESS / FAILURE
 *
 *   Comments    :  Does not check for duplicate entries.
 *
 *            To simplify cleanup and mutex unlocking, all paths
 *            must exit via the exit label.
 *
 *********************************************************************/

static unsigned32
object_db_file_entry_add( char     *object_name,
              char     *type_name )
{
    FILE         *fp;
    unsigned32   status;

    pthread_mutex_lock( &object_db_file_mutex );

    fp = fopen( g_object_db_filename ,"a");
    if ( fp == NULL )
    {
        fclose ( fp );
    status =  FAILURE;
    goto exit;
    }
    fprintf(fp, "%s %s\n", object_name, type_name );
    fclose(fp);
    status = SUCCESS;

 exit:

    pthread_mutex_unlock( &object_db_file_mutex );

    return( status );

} /* object_db_file_entry_add */


/*********************************************************************
 *   Function    :  PRIVATE object_db_file_entry_delete
 *********************************************************************
 *
 *   Description :  Remove the specified object from the server's
 *            local database.
 *
 *   Returns     :  SUCCESS / FAILURE / NO_MATCH
 *
 *   Comments    :  Deletes all instances of entries with matching
 *            object name.
 *
 *            To simplify cleanup and mutex unlocking, all paths
 *            must exit via the exit label.
 *
 *********************************************************************/

static unsigned32
object_db_file_entry_delete( char *object_name )
{
    FILE         *fp, *ftmp;
    char         buf[BUFSIZ];
    unsigned8    found = 0;
    char         *tmp_db = "/tmp/tmp_db";
    unsigned32    status;

    pthread_mutex_lock( &object_db_file_mutex );

    /* open the object data base file */

    if ((fp = fopen( g_object_db_filename ,"r")) == NULL)
    {
        status = FAILURE;
    goto exit;
    }

    /* get the name of the temporary file and open it for writing */

    if ((ftmp = fopen(tmp_db,"w+")) == NULL)
    {
    status = FAILURE;
    goto exit;
    }

    /* copy each non-matching entry to the temporary file */

    while(fgets( buf,BUFSIZ, fp ) != NULL)
    {
    if (strstr( buf,object_name ) == NULL)
    {
        if( fputs( buf, ftmp ) == EOF )
        {
        status = FAILURE;
        goto exit;
        }
    }
    else
        found++;
    }

    fclose(fp);
    fclose(ftmp);

    if ( found )
    {
        /* rename the tmp file to be the new object database file */
        remove(g_object_db_filename);
        rename(tmp_db, g_object_db_filename);
    }
    else
    {
    status = NO_MATCH;
    goto exit;
    }

    /*
     *  Assume success if reached here.
     */

    status = SUCCESS;

 exit:

    pthread_mutex_unlock( &object_db_file_mutex );
    return( status );

} /* object_db_file_entry_delete */


/*********************************************************************
 *   Function    :  PRIVATE object_db_entry_inq_begin_int
 *********************************************************************
 *
 *   Description :  Internal routine to implement
 *            object_db_entry_inq_begin().
 *
 *   Comments     :  See svrodb.h for comment header.
 *
 *********************************************************************/

static unsigned32
object_db_entry_inq_begin_int( object_db_handle_t *context )
{
    unsigned32 status;

    status = object_db_check_init();

    if( status != SUCCESS )
    return( CODING_ERROR );

    if( context == NULL )
    return( CODING_ERROR );

    *context = NULL;

    return( SUCCESS );
}  /* object_db_entry_inq_begin_int */


/*********************************************************************
 *   Function    :  PRIVATE object_db_entry_inq_next_int
 *********************************************************************
 *
 *   Description :  Internal routine to implement
 *            object_db_entry_inq_next().
 *
 *   Comments     :  See svrodb.h for comment header.
 *
 *********************************************************************/

static unsigned32
object_db_entry_inq_next_int( object_db_handle_t *context,
              char        **object_name_p,
              char        **type_name_p )
{
    object_db_table_entry_p_t          h;
    unsigned32        status;
    boolean32       done = FALSE;

    status = object_db_check_init();

    if( status != SUCCESS )
    return( CODING_ERROR );

    if( context == NULL )
    return( CODING_ERROR );

    if ( *context == NULL )
    {
        h = (object_db_handle_t )object_db_table.head;
    }
    else
    {
        h = (object_db_table_entry_p_t)*context;
        h = h->next;
    }

    /*
     *  Loop until either the end of the table is reached or a valid
     *  entry (one not marked DELETED) is found.
     */

    while( !done )
    {
        /*
         *  Since the mutex is locked at the start of the loop, it must
         *  be unlocked at the end of the loop, AND AT EVERY LOOP
         *  CONTINUATION AND BREAK!
         *
         *  The purpose of locking and unlocking the mutex each iteration
         *  is to keep other threads from starving.
         */

        pthread_mutex_lock( &object_db_table_mutex );

        if( h == NULL )
        {
            pthread_mutex_unlock( &object_db_table_mutex );
            status = NO_MORE_ENTRIES;
            done = TRUE;
            continue;
        }

        h->ref_count++;

        if ( h->status != DELETED )
        {
            if( object_name_p != NULL )
            {
                *object_name_p = (char *)malloc(strlen(h->object_name+1));
                if( *object_name_p == NULL )
                {
                    pthread_mutex_unlock( &object_db_table_mutex );
                    free( *object_name_p );
                    h->ref_count--;
                    status = FAILURE;
                    done = TRUE;
                    continue;
                }
                strcpy( *object_name_p, h->object_name);
            }

            if( type_name_p != NULL )
            {
                *type_name_p = (char *)malloc(strlen(h->type_name+1));
                if( *type_name_p == NULL )
                {
                    pthread_mutex_unlock( &object_db_table_mutex );
                    free( *object_name_p );
                    free( *type_name_p );
                    h->ref_count--;
                    status = FAILURE;
                    done = TRUE;
                    continue;
                }
                strcpy( *type_name_p, h->type_name);
            }
            h->ref_count--;
            done = TRUE;
        }
        else
        {
            h->ref_count--;
            h = h->next;
        }

        pthread_mutex_unlock( &object_db_table_mutex );

    } /* end while */

    /*
     *  everything after the loop exit will be on the common exit path.
     */

    *context = (object_db_handle_t)h;
    return( status );

} /* object_db_entry_inq_next_int */


/*********************************************************************
 *   Function    :  PRIVATE object_db_entry_inq_done_int
 *********************************************************************
 *
 *   Description :  Internal routine to implement
 *            object_db_entry_inq_done().
 *
 *   Comments     :  See svrodb.h for comment header.
 *
 *********************************************************************/

static unsigned32
object_db_entry_inq_done_int( object_db_handle_t *context )
{
    object_db_table_entry_p_t    h;
    unsigned32      status;

    status = object_db_check_init();

    if( status != SUCCESS )
        return( CODING_ERROR );

    if( context == NULL )
    return( CODING_ERROR );

    h = ( object_db_table_entry_p_t ) *context;

    if( h != NULL )
    {
    pthread_mutex_lock( &object_db_table_mutex );
    h->ref_count--;
    pthread_mutex_unlock( &object_db_table_mutex );
    *context = NULL;
    }
    return( SUCCESS );

}  /* object_db_entry_inq_done_int */

/* server_object_db.c */

