/* @(#)86    1.19  src/examples/type_mgr/server_setup_admin.c, examples.src, os2dce21.dss, 960602a.1 1/12/96 14:30:54 */
/*
 * 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_setup_admin.c                                *
 *********************************************************************
 *                                                                   *
 *  Functions :  server_setup_admin()                                *
 *               is_net_addr_local()                                 *
 *               server_set_auth_fn()                                *
 *                                                                   *
 *  Comments  :  Routines that control the manner in which the       *
 *               server process sets up its administrative           *
 *               interface, ie exporting information to the          *
 *               namespace and endpoint map.                         *
 *                                                                   *
 *********************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#ifdef IBMOS2
# include <os2def.h>
#endif
#include <dce/dce_error.h>
#include <dce/rpc.h>
#include "type_mgr.h"
#include "tm_admin_if.h"
#include <util.h>
#include "tm_admin.h"
#include "server_object_db.h"

/*
 *  Prototype of local routines.
 */

static boolean32
is_net_addr_local( rpc_binding_handle_t bh,
                   unsigned32           *status );

static boolean32
server_set_auth_fn( rpc_binding_handle_t client_binding,
                    unsigned32           requested_mgmt_op,
                    unsigned32           *status );


/*
 *  Global variables that are used throughout the program.  Any variables
 *  defined here should have extern definitions in server.h, so the
 *  global vars can be accessed anywhere in the server program.
 */

uuid_t                   g_server_object_uuid;


/*********************************************************************
 *   Function    :  server_setup_admin()                             *
 *********************************************************************
 *                                                                   *
 *   Description :  Initializes the server's administrative          *
 *                  interface.                                       *
 *                                                                   *
 *   Returns     :  (0) success, or appropriate error code.          *
 *                                                                   *
 *********************************************************************/

unsigned32 server_setup_admin()
{
    unsigned32           status = 0, rc;
    boolean32            entry_exit = FALSE ;
    rpc_binding_vector_t *binding_vector;
    char                 entry_name[ENTRY_LEN];
    rpc_ns_handle_t      import_context;
    uuid_vector_t        uuid_vec;
    rpc_binding_handle_t *binding_h = NULL;
    char                 group_name[ENTRY_LEN];
    char                 annotation[ENTRY_LEN];
    char                 *tmp;

    TRACE("Starting Setup of Administrative Interface\n");

    sprintf( entry_name,
             "%s%s",
             g_server_ns_directory_path,
             g_server_ns_entry );


    binding_h = ( rpc_binding_handle_t *)
                        malloc( sizeof(rpc_binding_handle_t ));

    /*
     *  check if the entry already exists in the namespace.
     */

    rpc_ns_binding_import_begin( rpc_c_ns_syntax_default,
                                 (unsigned_char_t *)entry_name,
                                 tm_admin_v1_0_s_ifspec,
                                 NULL,
                                 &import_context,
                                 &status );

    CHECK_STATUS( "rpc_ns_binding_import_begin()\n",
                  status,
                  RETURN );

    rpc_ns_binding_import_next( import_context,
                                binding_h,
                                &status );

    if ( status != rpc_s_entry_not_found &&
               status != rpc_s_no_more_bindings )
    {
        /*
         *  The fact that the entry does not exist is not a problem.
         *  We will create it.  Any other error that is reported
         *  causes the routine to return.
         */

        CHECK_STATUS( "rpc_ns_binding_import_next()\n",
                      status,
                      RETURN);

        TRACE("Server already has an entry in the namespace\n");

        /*
         * If a namespace entry for the server already exists, then
         * check for 2 things:
         *
         *   1)  is this entry name registered for a server on a
         *       different host (ie make sure the net addr is local).
         *   2)  is there already a server with this entry name
         *       registered with the endpoint map (ie is it up and
         *       running).
         *
         * If the entry name is for a server on another host, then
         * this server instance cannot run, because there is no way
         * to differentiate between the server instances in the
         * namespace.
         * If an instance of the server is already listening, this
         * server instance cannot run, since the object database is
         * not constructed to allow concurrent access from multiple
         * processes.
         */

        if (is_net_addr_local( *binding_h, &status ) == FALSE)
        {
            if (status)
            {
                PRINT("Problems examining binding handle network address\n");
                return( FAILURE );
            }
            PRINT("This server already exists on a different host.\n");
            return( FAILURE );
        }

        /*
         *  If binding can be resolved, then there is already another
         *  server with this server name listening on this interface.
         */

        rpc_ep_resolve_binding( *binding_h,
                                tm_admin_v1_0_s_ifspec,
                                &status );

        /*
         *  If the binding has no endpoint registered, that is OK.
         *  Any other error is not.
         */

        if (status != ept_s_not_registered)
        {
            /* Sorry about the comma operator! */
            PRINT("There is already a server instance running:\n");
            PRINT("\t%s\n",(tmp=strrchr(entry_name,'/'),++tmp));
            CHECK_STATUS( "rpc_ep_resolve_binding()\n",
                          status,
                          RETURN );

            /*
             *  CHECK_STATUS will catch any rpc problems, but a zero
             *  return code means the endpoint exists, which means we
             *  cannot proceed to bring up another instance.  So we
             *  set the status code to non-zero, and return.
             */

            status++;
            return(status);
        }

        rpc_ns_binding_import_done( &import_context,
                                    &rc );
        free(binding_h);

        /*
         * Since the entry already exists, get the entries object uuid
         * from the namespace, so we can use it hereafter.
         */

        TRACE("Getting object UUID of existing server entry\n");

        rpc_ns_entry_object_inq_begin( rpc_c_ns_syntax_default,
                                        entry_name,
                                        &import_context,
                                        &status);

        CHECK_STATUS( "rpc_ns_entry_object_inq_begin()\n",
                      status,
                      RETURN );

         rpc_ns_entry_object_inq_next( import_context,
                                       &g_server_object_uuid,
                                       &status);

         /* delete the import(inquiry) context, anyway. */
         rpc_ns_entry_object_inq_done( &import_context,
                                       &rc);

         /*
          * If the object uuid does not exist in the server entry,
          * return.
          */

        CHECK_STATUS( "rpc_ns_entry_object_inq_next()\n",
                      status,
                      RETURN );

       entry_exit = TRUE;

    }
    else
    {
        free(binding_h);
        rpc_ns_binding_import_done( &import_context,
                                    &status );

        CHECK_STATUS( "rpc_ns_binding_import_done()\n",
                      status,
                      RETURN );

        /*
         *  Since no server entry exists in the namespace,
         *  create a uuid that will be associated with the server.
         */

        TRACE("Creating a new server entry in the namespace\n");

        uuid_create( &g_server_object_uuid, &status );
        CHECK_STATUS( "uuid_create()\n",
                      status,
                      RETURN );

    } /* end if-else (status != ept_s_not_registered) */

    /*
     *  Initialize the object database.
     */

    if ((status = object_db_init()) == FAILURE)
    {
        PRINT("Error initializing object database\n");
        return(status);
    }

    /*
     * The administrator interface has only one management entry
     * point vector, so there is no need to provide a type uuid or
     * an mepv.
     */

    rpc_server_register_if( tm_admin_v1_0_s_ifspec,
                            NULL,
                            NULL,
                            &status );

    CHECK_STATUS( "rpc_server_register_if() [admin]\n",
                  status,
                  RETURN );

    /*
     *  The only protseq supported right now is Datagram (ncadg_ip_udp),
     *  but we'll register to use them all, so sometime in the future
     *  it will be able to handle clients over tcp/ip, etc...
     */

    rpc_server_use_all_protseqs( rpc_c_protseq_max_calls_default,
                                 &status );

    CHECK_STATUS( "rpc_server_use_all_protseqs()\n",
                  status,
                  RETURN );

    rpc_server_inq_bindings( &binding_vector,
                             &status );

    CHECK_STATUS( "rpc_server_inq_bindings()\n",
                  status,
                  RETURN );

    /*
     *  Register an endpoint for the tm_admin interface.
     */

    uuid_vec.count = 1 ;
    uuid_vec.uuid[0] = &g_server_object_uuid;

    sprintf( annotation,
             "Example Type Manager [ admin Binding of %s ]",
             g_server_ns_entry );

    rpc_ep_register( tm_admin_v1_0_s_ifspec,
                     binding_vector,
                     &uuid_vec,
                     (unsigned_char_t *) annotation,
                     &status );

    CHECK_STATUS( "rpc_ep_register()\n",
                  status,
                  RETURN );


    /*
     *  Export the server binding information to the namespace.  The
     *  entry will be placed in the directory specified by the pre-defined
     *  directory path.
     */

    if ( ! entry_exit )
    {
        /* If this is the first time of this server is started,
           then export the server name to the name space */

        rpc_ns_binding_export( rpc_c_ns_syntax_default,
                               (unsigned_char_t *)entry_name,
                               tm_admin_v1_0_s_ifspec,
                               binding_vector,
                               &uuid_vec,
                               &status );

        CHECK_STATUS( "rpc_ns_binding_export()\n",
                      status,
                      RETURN );

    }

    /*
     *  Free up the binding vector that was returned to us previously.
     */

    rpc_binding_vector_free( &binding_vector,
                             &status );

    CHECK_STATUS( "rpc_binding_vector_free()\n",
                  status,
                  RETURN );

    /* add this server to the server group entry. */

    sprintf( group_name, "%s%s", NAME_SERVICE_PATH,
                                 NAME_SERVICE_TM_SERVER_GROUP );

    rpc_ns_group_mbr_add( rpc_c_ns_syntax_default,
              group_name,
              rpc_c_ns_syntax_default,
              entry_name,
              &status);

    CHECK_STATUS( "rpc_ns_group_mbr_add()\n",
                  status,
                  RETURN );

    /*
     *  Setup a new authorization function for controlling access to
     *  remote calls to the server's administration routines.  See the
     *  management authorization function to see what authorizations
     *  are set.
     */

    rpc_mgmt_set_authorization_fn(
                         (rpc_mgmt_authorization_fn_t) server_set_auth_fn,
                         &status
                                 );

    CHECK_STATUS( "rpc_mgmt_set_authorization_fn()\n",
                  status,
                  RETURN );

    TRACE("Administrative interface has been setup successfully\n");
    return(status);

} /* end server_setup_admin() */


/*********************************************************************
 *   Function    :  is_net_addr_local()                              *
 *********************************************************************
 *                                                                   *
 *   Description :  Compares the network address of the binding      *
 *                  handle passed in, with the internet family       *
 *                  ip addresses, to determine if the binding        *
 *                  handle is for a server on the local host.        *
 *                                                                   *
 *   Returns     :  TRUE/FALSE                                       *
 *                                                                   *
 *********************************************************************/

boolean32 is_net_addr_local( rpc_binding_handle_t bh,
                             unsigned32           *status )
{
    int i, cnt;
    char addrvec[20][16];
    char *string_binding;
    char *netaddr;

    rpc_binding_to_string_binding( bh,
                                   &string_binding,
                                   status );

    CHECK_STATUS( "rpc_binding_to_string_binding()\n",
                  *status,
                  RETURN );

    rpc_string_binding_parse( string_binding,
                              NULL,
                              NULL,
                              &netaddr,
                              NULL,
                              NULL,
                              status );

    CHECK_STATUS( "rpc_string_binding_parse()\n",
                  *status,
                  RETURN );

    rpc_string_free( &string_binding, status );

    /*
     * Returns a vector of ip addresses for the local host machine.
     */

    if ((*status) = get_local_host_net_addrs( &cnt,
                                              addrvec ))
    {
        PRINT("Could not get local host network addresses!\n");
        rpc_string_free( &netaddr, status );
        return(FALSE);
    }

    for (i=0; i<cnt; i++)
    {
        if ((strcmp( netaddr, &addrvec[i][0] ) == 0))
        {
            break;
        }
    }

    rpc_string_free( &netaddr, status );

    if ( i == cnt )
    {
        return(FALSE);
    }

    return(TRUE);

} /* end is_net_addr_local() */


/*********************************************************************
 *   Function    :  server_set_auth_fn()                             *
 *********************************************************************
 *                                                                   *
 *   Description :  Routine to set controls for remote administration*
 *                  routines.                                        *
 *                                                                   *
 *   Returns     :  TRUE                                             *
 *                                                                   *
 *********************************************************************/

static boolean32 server_set_auth_fn( rpc_binding_handle_t client_binding,
                              unsigned32           requested_mgmt_op,
                              unsigned32           *status )
{
    /*
     *  This simply allows the client access to all client management
     *  functions.  In a real application this function would have
     *  a more sophisticated approach to authorizing client access
     *  to RPC runtime management functions.
     */

    *status = rpc_s_ok;
    return( (boolean32) TRUE );

} /* end server_set_auth_fn() */

/* EOF server_setup_admin.c */


