cnsm.c   [plain text]


/*
 * Copyright (c) 2010 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Portions of this software have been released under the following terms:
 *
 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
 *
 * To anyone who acknowledges that this file is provided "AS IS"
 * without any express or implied warranty:
 * permission to use, copy, modify, and distribute this file for any
 * purpose is hereby granted without fee, provided that the above
 * copyright notices and this notice appears in all source code copies,
 * and that none of the names of Open Software Foundation, Inc., Hewlett-
 * Packard Company or Digital Equipment Corporation be used
 * in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  Neither Open Software
 * Foundation, Inc., Hewlett-Packard Company nor Digital
 * Equipment Corporation makes any representations about the suitability
 * of this software for any purpose.
 *
 * Copyright (c) 2007, Novell, Inc. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Novell Inc. nor the names of its contributors
 *     may be used to endorse or promote products derived from this
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

/*
**
**  NAME
**
**      cnsm.c
**
**  FACILITY:
**
**      Remote Procedure Call (RPC)
**
**  ABSTRACT:
**
**  The NCA Connection Protocol State Machine Service
**
**
*/

#include <commonp.h>    /* Common declarations for all RPC runtime */
#include <com.h>        /* Common communications services */
#include <comprot.h>    /* Common protocol services */
#include <cnp.h>        /* NCA Connection private declarations */
#include <cnsm.h>

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

/*
**++
**
**  ROUTINE NAME:       rpc__cn_sm_init
**
**  SCOPE:              PRIVATE - declared in cnsm.h
**
**  DESCRIPTION:
**
**  The routine will be used to initialize a state machine control
**  block. Depending on the type of state machine it will be called
**  in various places. It basically just fills in the table pointers
**  given, sets the state to closed and initializes the event list.
**
**  INPUTS:
**
**      state_tbl       The state table this state machine is to use.
**      action_tbl      The action routine table this state machine
**                      is to use.
**	tbl_id		The identifier of the particular action table
**			we are storing in the control block.
**
**  INPUTS/OUTPUTS:     none
**
**  OUTPUTS:
**
**      sm              The state machine control block which is to
**                      be initialized.
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     none
**
**  SIDE EFFECTS:       none
**
**--
**/

PRIVATE void rpc__cn_sm_init
(
rpc_cn_sm_state_entry_p_t       *state_tbl,
rpc_cn_sm_action_fn_p_t         action_tbl,
rpc_cn_sm_ctlblk_p_t            sm,
unsigned32			tbl_id
)
{

    /*
     * Put the pointers to the given tables into the state machine
     * control block.
     */
    sm->state_tbl = state_tbl;
    sm->action_tbl = action_tbl;

    /*
     * Set the initial state in the state machine control block to
     * "closed".
     */
    sm->cur_state = RPC_C_SM_CLOSED_STATE;

    /*
     * Store the tbl_id in the controlblock and use it later
     * to selectively bypass calls to the event evaluation
     * routine, going directly to the action routines.
     */
     sm->tbl_id = tbl_id;

    /*
     * Initialize the state machine control block event list.
     */
    rpc__cn_sm_init_event_list(sm);
}


/*
**++
**
**  ROUTINE NAME:       rpc__cn_sm_init_event_list
**
**  SCOPE:              PRIVATE - declared in cnsm.h
**
**  DESCRIPTION:
**
** This routine will initialize the event list contained in the
** specified state machine control block. This routine is called as
** part of initializing the state machine control block.
**
**  INPUTS:             none
**
**  INPUTS/OUTPUTS:     none
**
**  OUTPUTS:
**
**      sm              The state machine control block containing
**                      the event list to be initialized.
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     none
**
**  SIDE EFFECTS:       none
**
**--
**/

PRIVATE void rpc__cn_sm_init_event_list
(
 rpc_cn_sm_ctlblk_t      *sm
)
{
    /*
     * Set up the event list so that it's empty. This means the state
     * must be set to "empty" and the head and tail indices must be
     * zeroed.
     */
    sm->event_list_state = RPC_C_CN_SM_EVENT_LIST_EMPTY;
    sm->event_list_hindex = 0;
    sm->event_list_tindex = 0;
}


/*
**++
**
**  ROUTINE NAME:       rpc__cn_sm_eval_event
**
**  SCOPE:              PRIVATE - declared in cnsm.h
**
**  DESCRIPTION:
**
**  This routine will be used to evaluate an event for the specified
**  state machine. It handles the main work of running a state
**  machine. It will look up either action or state transition for
**  events.  The lookup will return either a state or action.
**  Distinguish between states and actions by numeric range.
**
**  INPUTS:
**
**      event_id        The number of the event to be processed.
**      event_param     The special event related parameter which is
**                      to be passed to the predicate and action
**                      routines for the processing of this event.
**      spc_struct      A special parameter which is to be passed to
**                      the predicate and action routines for the
**                      processing of this event and any subsequent
**                      events which are inserted on the event list.
**
**  INPUTS/OUTPUTS:
**
**      sm              The state machine control block to be used
**                      in processing this event.
**
**  OUTPUTS:            none
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     The status returned by the last action
**                      routine invoked.
**
**  SIDE EFFECTS:       none
**
**--
**/

PRIVATE unsigned32     rpc__cn_sm_eval_event
(
  unsigned32              event_id,
  dce_pointer_t               event_parameter,
  dce_pointer_t               spc_struct,
  rpc_cn_sm_ctlblk_t      *sm
)
{
    rpc_cn_sm_event_entry_t     next_event;
    unsigned8                   action_index;
    boolean                     more_events;
    rpc_cn_sm_state_entry_t     *state_entry_p;

    /*
     * Initialize action status to ok.  This allows state transitions
     * which do not invoke action routines to signal normal completion.
     */
    sm->action_status = rpc_s_ok;

    /*
     * Set up the first event to be evaluated using the input args.
     */
    next_event.event_id = event_id;
    next_event.event_param = event_parameter;

    /*
     * Process the first event and any events which get added.
     */
    more_events = true;
    while (more_events)
    {
        /*
         * Pick up the state table entry to the current state. The
	 * value in the state table is going to be either a next
	 * state or an action.  If it is an action, we take that
	 * action and within the action routine, update sm->cur_state.
	 * In cases where there is no action but there is a next state,
	 * we can just store the next state.  We distinguish between
	 * actions and state by their numeric value.  Next states are
	 * always greater than or equal to rpc_c_cn_statebase.
	 * Since states are also used to access an array entry, we need
	 * to subtract the value we added in order to distinguish it
	 * from an action routine, before we can access the array.
         */
        state_entry_p = sm->state_tbl[(sm->cur_state -
			RPC_C_CN_STATEBASE)];

        /*
         * Look up the index of the action routine using the current
         * state and the id of the event being processed.
         */
        action_index = state_entry_p[(next_event.event_id -
			RPC_C_CN_STATEBASE )].action;

        /*
         * If there is no action to take, just transition to the next
	 * state which is the value returned from  the state table
	 * lookup (state_entry_p).
         */
	if (action_index >= RPC_C_CN_STATEBASE)
	{
            sm->cur_state = action_index;
	}
	else
	{
            /*
             * Call the action routine.  The spc_struct and event_param
	     * and sm will be passed to the action routine.  Note
	     * that sm is changed within the action routines.  Each
	     * action routine will update sm->cur_state.
             */
	    sm->cur_event = next_event.event_id;
	    sm->action_status =
		(*(sm->action_tbl[action_index]))
		(spc_struct, next_event.event_param, sm);
         }

        /*
         * Get the next event, if any, off the event list in the
         * state machine control block.  RPC_CN_SM_GET_NEXT_EVENT
	 * will set more_events to true or false depending on
	 * whether there are more events to process.
         */
        RPC_CN_SM_GET_NEXT_EVENT (sm, &next_event, more_events);
    }
    return (sm->action_status);
}


/*
**++
**
**  ROUTINE NAME:       rpc__cn_sm_insert_event
**
**  SCOPE:              PRIVATE - declared in cnsm.h
**
**  DESCRIPTION:
**
**  This routine inserts a new event entry on the state machine
**  control block event list. If the list is full the event can't be
**  inserted and false will be returned.
**
**  INPUTS:
**
**      event           The event entry being inserted.
**
**  INPUTS/OUTPUTS:
**
**      sm              The state machine control block containing
**                      the event list.
**
**  OUTPUTS:
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     none
**
**  SIDE EFFECTS:       none
**
**--
**/

PRIVATE void rpc__cn_sm_insert_event
(
  rpc_cn_sm_event_entry_p_t       event,
  rpc_cn_sm_ctlblk_t              *sm
)
{
#ifdef DEBUG
    /*
     * Check whether the event list is full. This condition occurs
     * when the head and tail indices are equal and the state
     * indicates the list is not empty.
     */
    if ((sm->event_list_hindex == sm->event_list_tindex) &&
        (sm->event_list_state != RPC_C_CN_SM_EVENT_LIST_EMPTY))
    {
        /*
         * rpc_m_eventlist_full
         * "(%s) Event list full"
         */
        rpc_dce_svc_printf (
            __FILE__, __LINE__,
            "%s",
            rpc_svc_cn_state,
            svc_c_sev_fatal | svc_c_action_abort,
            rpc_m_eventlist_full,
            "rpc__cn_sm_insert_event" );
    }
#endif

    /*
     * There's room on the event list. Add the new entry to the tail
     * of the list.
     */
    sm->event_list[sm->event_list_tindex].event_id = event->event_id;
    sm->event_list[sm->event_list_tindex].event_param = event->event_param;

    /*
     * Add the event to the event list by incrementing the tail
     * index and checking for wraparound. Also set the state of the
     * event list to non-empty.
     */
    sm->event_list_tindex = (sm->event_list_tindex + 1) &
                            (RPC_C_CN_SM_EVENT_LIST_MAX_ENTRIES - 1);
    sm->event_list_state = ~RPC_C_CN_SM_EVENT_LIST_EMPTY;
}