1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1.  Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 * 2.  Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
16 *     contributors may be used to endorse or promote products derived from
17 *     this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Portions of this software have been released under the following terms:
31 *
32 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
33 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
34 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
35 *
36 * To anyone who acknowledges that this file is provided "AS IS"
37 * without any express or implied warranty:
38 * permission to use, copy, modify, and distribute this file for any
39 * purpose is hereby granted without fee, provided that the above
40 * copyright notices and this notice appears in all source code copies,
41 * and that none of the names of Open Software Foundation, Inc., Hewlett-
42 * Packard Company or Digital Equipment Corporation be used
43 * in advertising or publicity pertaining to distribution of the software
44 * without specific, written prior permission.  Neither Open Software
45 * Foundation, Inc., Hewlett-Packard Company nor Digital
46 * Equipment Corporation makes any representations about the suitability
47 * of this software for any purpose.
48 *
49 * Copyright (c) 2007, Novell, Inc. All rights reserved.
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions
52 * are met:
53 *
54 * 1.  Redistributions of source code must retain the above copyright
55 *     notice, this list of conditions and the following disclaimer.
56 * 2.  Redistributions in binary form must reproduce the above copyright
57 *     notice, this list of conditions and the following disclaimer in the
58 *     documentation and/or other materials provided with the distribution.
59 * 3.  Neither the name of Novell Inc. nor the names of its contributors
60 *     may be used to endorse or promote products derived from this
61 *     this software without specific prior written permission.
62 *
63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
64 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
65 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
66 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
67 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
69 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
70 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
71 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
72 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
73 *
74 * @APPLE_LICENSE_HEADER_END@
75 */
76
77/*
78**
79**  NAME
80**
81**      cnsm.c
82**
83**  FACILITY:
84**
85**      Remote Procedure Call (RPC)
86**
87**  ABSTRACT:
88**
89**  The NCA Connection Protocol State Machine Service
90**
91**
92*/
93
94#include <commonp.h>    /* Common declarations for all RPC runtime */
95#include <com.h>        /* Common communications services */
96#include <comprot.h>    /* Common protocol services */
97#include <cnp.h>        /* NCA Connection private declarations */
98#include <cnsm.h>
99
100/***********************************************************************/
101
102
103/*
104**++
105**
106**  ROUTINE NAME:       rpc__cn_sm_init
107**
108**  SCOPE:              PRIVATE - declared in cnsm.h
109**
110**  DESCRIPTION:
111**
112**  The routine will be used to initialize a state machine control
113**  block. Depending on the type of state machine it will be called
114**  in various places. It basically just fills in the table pointers
115**  given, sets the state to closed and initializes the event list.
116**
117**  INPUTS:
118**
119**      state_tbl       The state table this state machine is to use.
120**      action_tbl      The action routine table this state machine
121**                      is to use.
122**	tbl_id		The identifier of the particular action table
123**			we are storing in the control block.
124**
125**  INPUTS/OUTPUTS:     none
126**
127**  OUTPUTS:
128**
129**      sm              The state machine control block which is to
130**                      be initialized.
131**
132**  IMPLICIT INPUTS:    none
133**
134**  IMPLICIT OUTPUTS:   none
135**
136**  FUNCTION VALUE:     none
137**
138**  SIDE EFFECTS:       none
139**
140**--
141**/
142
143PRIVATE void rpc__cn_sm_init
144(
145rpc_cn_sm_state_entry_p_t       *state_tbl,
146rpc_cn_sm_action_fn_p_t         action_tbl,
147rpc_cn_sm_ctlblk_p_t            sm,
148unsigned32			tbl_id
149)
150{
151
152    /*
153     * Put the pointers to the given tables into the state machine
154     * control block.
155     */
156    sm->state_tbl = state_tbl;
157    sm->action_tbl = action_tbl;
158
159    /*
160     * Set the initial state in the state machine control block to
161     * "closed".
162     */
163    sm->cur_state = RPC_C_SM_CLOSED_STATE;
164
165    /*
166     * Store the tbl_id in the controlblock and use it later
167     * to selectively bypass calls to the event evaluation
168     * routine, going directly to the action routines.
169     */
170     sm->tbl_id = tbl_id;
171
172    /*
173     * Initialize the state machine control block event list.
174     */
175    rpc__cn_sm_init_event_list(sm);
176}
177
178
179/*
180**++
181**
182**  ROUTINE NAME:       rpc__cn_sm_init_event_list
183**
184**  SCOPE:              PRIVATE - declared in cnsm.h
185**
186**  DESCRIPTION:
187**
188** This routine will initialize the event list contained in the
189** specified state machine control block. This routine is called as
190** part of initializing the state machine control block.
191**
192**  INPUTS:             none
193**
194**  INPUTS/OUTPUTS:     none
195**
196**  OUTPUTS:
197**
198**      sm              The state machine control block containing
199**                      the event list to be initialized.
200**
201**  IMPLICIT INPUTS:    none
202**
203**  IMPLICIT OUTPUTS:   none
204**
205**  FUNCTION VALUE:     none
206**
207**  SIDE EFFECTS:       none
208**
209**--
210**/
211
212PRIVATE void rpc__cn_sm_init_event_list
213(
214 rpc_cn_sm_ctlblk_t      *sm
215)
216{
217    /*
218     * Set up the event list so that it's empty. This means the state
219     * must be set to "empty" and the head and tail indices must be
220     * zeroed.
221     */
222    sm->event_list_state = RPC_C_CN_SM_EVENT_LIST_EMPTY;
223    sm->event_list_hindex = 0;
224    sm->event_list_tindex = 0;
225}
226
227
228/*
229**++
230**
231**  ROUTINE NAME:       rpc__cn_sm_eval_event
232**
233**  SCOPE:              PRIVATE - declared in cnsm.h
234**
235**  DESCRIPTION:
236**
237**  This routine will be used to evaluate an event for the specified
238**  state machine. It handles the main work of running a state
239**  machine. It will look up either action or state transition for
240**  events.  The lookup will return either a state or action.
241**  Distinguish between states and actions by numeric range.
242**
243**  INPUTS:
244**
245**      event_id        The number of the event to be processed.
246**      event_param     The special event related parameter which is
247**                      to be passed to the predicate and action
248**                      routines for the processing of this event.
249**      spc_struct      A special parameter which is to be passed to
250**                      the predicate and action routines for the
251**                      processing of this event and any subsequent
252**                      events which are inserted on the event list.
253**
254**  INPUTS/OUTPUTS:
255**
256**      sm              The state machine control block to be used
257**                      in processing this event.
258**
259**  OUTPUTS:            none
260**
261**  IMPLICIT INPUTS:    none
262**
263**  IMPLICIT OUTPUTS:   none
264**
265**  FUNCTION VALUE:     The status returned by the last action
266**                      routine invoked.
267**
268**  SIDE EFFECTS:       none
269**
270**--
271**/
272
273PRIVATE unsigned32     rpc__cn_sm_eval_event
274(
275  unsigned32              event_id,
276  dce_pointer_t               event_parameter,
277  dce_pointer_t               spc_struct,
278  rpc_cn_sm_ctlblk_t      *sm
279)
280{
281    rpc_cn_sm_event_entry_t     next_event;
282    unsigned8                   action_index;
283    boolean                     more_events;
284    rpc_cn_sm_state_entry_t     *state_entry_p;
285
286    /*
287     * Initialize action status to ok.  This allows state transitions
288     * which do not invoke action routines to signal normal completion.
289     */
290    sm->action_status = rpc_s_ok;
291
292    /*
293     * Set up the first event to be evaluated using the input args.
294     */
295    next_event.event_id = event_id;
296    next_event.event_param = event_parameter;
297
298    /*
299     * Process the first event and any events which get added.
300     */
301    more_events = true;
302    while (more_events)
303    {
304        /*
305         * Pick up the state table entry to the current state. The
306	 * value in the state table is going to be either a next
307	 * state or an action.  If it is an action, we take that
308	 * action and within the action routine, update sm->cur_state.
309	 * In cases where there is no action but there is a next state,
310	 * we can just store the next state.  We distinguish between
311	 * actions and state by their numeric value.  Next states are
312	 * always greater than or equal to rpc_c_cn_statebase.
313	 * Since states are also used to access an array entry, we need
314	 * to subtract the value we added in order to distinguish it
315	 * from an action routine, before we can access the array.
316         */
317        state_entry_p = sm->state_tbl[(sm->cur_state -
318			RPC_C_CN_STATEBASE)];
319
320        /*
321         * Look up the index of the action routine using the current
322         * state and the id of the event being processed.
323         */
324        action_index = state_entry_p[(next_event.event_id -
325			RPC_C_CN_STATEBASE )].action;
326
327        /*
328         * If there is no action to take, just transition to the next
329	 * state which is the value returned from  the state table
330	 * lookup (state_entry_p).
331         */
332	if (action_index >= RPC_C_CN_STATEBASE)
333	{
334            sm->cur_state = action_index;
335	}
336	else
337	{
338            /*
339             * Call the action routine.  The spc_struct and event_param
340	     * and sm will be passed to the action routine.  Note
341	     * that sm is changed within the action routines.  Each
342	     * action routine will update sm->cur_state.
343             */
344	    sm->cur_event = next_event.event_id;
345	    sm->action_status =
346		(*(sm->action_tbl[action_index]))
347		(spc_struct, next_event.event_param, sm);
348         }
349
350        /*
351         * Get the next event, if any, off the event list in the
352         * state machine control block.  RPC_CN_SM_GET_NEXT_EVENT
353	 * will set more_events to true or false depending on
354	 * whether there are more events to process.
355         */
356        RPC_CN_SM_GET_NEXT_EVENT (sm, &next_event, more_events);
357    }
358    return (sm->action_status);
359}
360
361
362/*
363**++
364**
365**  ROUTINE NAME:       rpc__cn_sm_insert_event
366**
367**  SCOPE:              PRIVATE - declared in cnsm.h
368**
369**  DESCRIPTION:
370**
371**  This routine inserts a new event entry on the state machine
372**  control block event list. If the list is full the event can't be
373**  inserted and false will be returned.
374**
375**  INPUTS:
376**
377**      event           The event entry being inserted.
378**
379**  INPUTS/OUTPUTS:
380**
381**      sm              The state machine control block containing
382**                      the event list.
383**
384**  OUTPUTS:
385**
386**  IMPLICIT INPUTS:    none
387**
388**  IMPLICIT OUTPUTS:   none
389**
390**  FUNCTION VALUE:     none
391**
392**  SIDE EFFECTS:       none
393**
394**--
395**/
396
397PRIVATE void rpc__cn_sm_insert_event
398(
399  rpc_cn_sm_event_entry_p_t       event,
400  rpc_cn_sm_ctlblk_t              *sm
401)
402{
403#ifdef DEBUG
404    /*
405     * Check whether the event list is full. This condition occurs
406     * when the head and tail indices are equal and the state
407     * indicates the list is not empty.
408     */
409    if ((sm->event_list_hindex == sm->event_list_tindex) &&
410        (sm->event_list_state != RPC_C_CN_SM_EVENT_LIST_EMPTY))
411    {
412        /*
413         * rpc_m_eventlist_full
414         * "(%s) Event list full"
415         */
416        rpc_dce_svc_printf (
417            __FILE__, __LINE__,
418            "%s",
419            rpc_svc_cn_state,
420            svc_c_sev_fatal | svc_c_action_abort,
421            rpc_m_eventlist_full,
422            "rpc__cn_sm_insert_event" );
423    }
424#endif
425
426    /*
427     * There's room on the event list. Add the new entry to the tail
428     * of the list.
429     */
430    sm->event_list[sm->event_list_tindex].event_id = event->event_id;
431    sm->event_list[sm->event_list_tindex].event_param = event->event_param;
432
433    /*
434     * Add the event to the event list by incrementing the tail
435     * index and checking for wraparound. Also set the state of the
436     * event list to non-empty.
437     */
438    sm->event_list_tindex = (sm->event_list_tindex + 1) &
439                            (RPC_C_CN_SM_EVENT_LIST_MAX_ENTRIES - 1);
440    sm->event_list_state = ~RPC_C_CN_SM_EVENT_LIST_EMPTY;
441}
442