dgclive.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:
**
**      dgclive.c
**
**  FACILITY:
**
**      Remote Procedure Call (RPC)
**
**  ABSTRACT:
**
**  Routines for maintaining liveness of clients.
**
**
*/

#include <dg.h>
#include <dgglob.h>

#include <dce/conv.h>
#include <dce/convc.h>

/* =============================================================================== */

/*
 * Define a linked list element which the client runtime can use to keep
 * track of which servers it needs to maintain liveness with.
 *
 * Here's the list elements...
 */
typedef struct maint_elt_t
{
    struct maint_elt_t   *next;       /* -> to next entry in list.       */
    rpc_binding_rep_p_t  shand;       /* -> to server binding handle     */
    unsigned8            refcnt;      /* -> # of entries for this server */
} maint_elt_t, *maint_elt_p_t;

/*
 * And here's the head of the list...
 */
INTERNAL maint_elt_p_t maint_head;

/*
 * And here's the mutex that protects the list...
 */
INTERNAL rpc_mutex_t   rpc_g_maint_mutex;
INTERNAL rpc_cond_t    maintain_cond;

INTERNAL boolean maintain_thread_running = false;
INTERNAL boolean maintain_thread_was_running = false;
INTERNAL boolean stop_maintain_thread = false;

INTERNAL dcethread*  maintain_task;

/*
 * Mutex lock macros
 */

#define RPC_MAINT_LOCK_INIT()   RPC_MUTEX_INIT (rpc_g_maint_mutex)
#define RPC_MAINT_LOCK()        RPC_MUTEX_LOCK (rpc_g_maint_mutex)
#define RPC_MAINT_UNLOCK()      RPC_MUTEX_UNLOCK (rpc_g_maint_mutex)

/* ========================================================================= */

INTERNAL void * network_maintain_liveness (void*);

/* ========================================================================= */

/*
 * R P C _ _ D G _ N E T W O R K _ M A I N T
 *
 * This function is called, via the network listener service, by a client
 * stub which needs to maintain context with a server.  A copy of the
 * binding handle is made and entered into a list associated with a timer
 * monitor.  This monitor will then periodically send an identifier to
 * the server to assure it that this client is still alive.
 */

PRIVATE void rpc__dg_network_maint
(
    rpc_binding_rep_p_t binding_r,
    unsigned32 *st
)
{
    maint_elt_p_t maint;
    rpc_dg_binding_client_p_t chand = (rpc_dg_binding_client_p_t) binding_r;

    *st = rpc_s_ok;

    RPC_MAINT_LOCK();

    /*
     * First, we need to traverse the list of maintained contexts to see
     * if this server is already on it.  If we find a matching  address,
     * we can just return.
     */

    for (maint = maint_head; maint != NULL; maint = maint->next)
    {
        if (rpc__naf_addr_compare(maint->shand->rpc_addr, binding_r->rpc_addr, st))
        {
            /*
             * If we find a matching element, store a pointer to it in the
             * client binding handle (so we don't have to do these compares
             * when maint_stop gets called) and note the reference.
             */

            chand->maint_binding = maint->shand;
            maint->refcnt++;
            RPC_MAINT_UNLOCK();
            return;
        }
    }

    /*
     * Need to make a new entry in the maintain list.  Alloc up a
     * list element.
     */

    RPC_MEM_ALLOC(maint, maint_elt_p_t, sizeof *maint,
            RPC_C_MEM_DG_MAINT, RPC_C_MEM_NOWAIT);

    /*
     * Make our own copy of the binding handle (so we have a handle to
     * send INDY's to this server).  Reference the new binding in the
     * client handle and note the reference.
     */

    rpc_binding_copy((rpc_binding_handle_t) chand,
                     (rpc_binding_handle_t *) &maint->shand, st);
    chand->maint_binding = maint->shand;
    maint->refcnt = 1;

    /*
     * and thread it onto the head of the list.
     */

    maint->next = maint_head;
    maint_head = maint;

    /*
     * If the binding handle had any authentication info associated with
     * it, free it up now.   We don't want the convc_indy() calls using
     * authentication.
     */
    rpc_binding_set_auth_info((rpc_binding_handle_t) maint->shand, NULL,
                       rpc_c_protect_level_none, rpc_c_authn_none, NULL,
                       rpc_c_authz_none, st);

    /*
     * Finally, check to make sure the 'context maintainer' thread has
     * been started, and if not, start it up.
     */

    if (! maintain_thread_running)
    {
        maintain_thread_running = true;
        dcethread_create_throw(&maintain_task, NULL,
            (void*)network_maintain_liveness,
            NULL);
    }

    RPC_MAINT_UNLOCK();
}

/*
 * R P C _ _ D G _ N E T W O R K _ S T O P _ M A I N T
 *
 * This routine is called, via the network listener service, by a client stub
 * when it wishes to discontinue maintaining context with a server.
 */

PRIVATE void rpc__dg_network_stop_maint
(
    rpc_binding_rep_p_t binding_r,
    unsigned32 *st
)
{
    maint_elt_p_t maint, prev = NULL;
    rpc_dg_binding_client_p_t chand = (rpc_dg_binding_client_p_t) binding_r;

    RPC_MAINT_LOCK();

    /*
     * Search through the list for the element which references this
     * binding handle.
     */

    for (maint = maint_head; maint != NULL; maint = maint->next)
    {
        if (chand->maint_binding == maint->shand)
        {
            /*
             * Remove the reference from the binding handle, and decrement
             * the reference count in the list element.  If the count
             * falls to 0, remove the element from the list.
             */

            chand->maint_binding = NULL;
            if (--maint->refcnt == 0)
            {
                if (prev == NULL)
                    maint_head = maint->next;
                else
                    prev->next = maint->next;
                rpc_binding_free((rpc_binding_handle_t *)&maint->shand, st);
                RPC_MEM_FREE(maint, RPC_C_MEM_DG_MAINT);
            }
            *st = rpc_s_ok;
            RPC_MAINT_UNLOCK();
            return;
        }
        prev = maint;
    }

    RPC_MAINT_UNLOCK();
    *st = -1;           /*!!! didn't find it, need real error value here */
}

/*
 * R P C _ _ D G _ N E T W O R K _ C L O S E
 *
 * This routine is called, via the network listener service, by a client stub
 * when it wishes to disconnect frmo a server.
 */

PRIVATE void rpc__dg_network_close
(
    ATTRIBUTE_UNUSED rpc_binding_rep_p_t binding_r,
    unsigned32 *st
)
{
    /* this is a NOOP for datagram transports */
    *st = rpc_s_ok;
}

/*
 * N E T W O R K _ M A I N T A I N _ L I V E N E S S
 *
 * Base routine for the thread which periodically sends out the address
 * space UUID of this process.  This UUID uniquely identifies this
 * particular instance of this particular client process for use in
 * maintaing context with servers.
 */

INTERNAL void * network_maintain_liveness(void * unused ATTRIBUTE_UNUSED)
{
    maint_elt_p_t ptr;
    struct timespec next_ts;

    RPC_DBG_PRINTF(rpc_e_dbg_conv_thread, 1,
                   ("(network_maintain_liveness) starting up...\n"));

    RPC_MAINT_LOCK();

    while (stop_maintain_thread == false)
    {
        /*
         * Send out INDYs every 20 seconds.
         */
        rpc__clock_timespec(rpc__clock_stamp()+20, &next_ts);

        RPC_COND_TIMED_WAIT(maintain_cond, rpc_g_maint_mutex, &next_ts);
        if (stop_maintain_thread == true)
            break;

        for (ptr = maint_head; ptr != NULL; ptr = ptr->next)
        {
            RPC_DBG_PRINTF(rpc_e_dbg_general, 3,
                ("(maintain_liveness_timer) Doing convc_indy call\n"));

            (*convc_v1_0_c_epv.convc_indy)((handle_t) ptr->shand,
                                                   &rpc_g_dg_my_cas_uuid);
        }

        /*
         * See if the list is empty...
         */

        if (maint_head == NULL)
        {
            /*
             * Nothing left to do, so terminate the thread.
             */
			  /* FIXME: MNE */
			  DCETHREAD_TRY	{
            dcethread_detach_throw(maintain_task);
			  }
			  DCETHREAD_CATCH_ALL(THIS_CATCH) {
				  fprintf(stderr, "XXX MIREK: %s: %s: %d: caught exception from detach\n",
						  __FILE__, __PRETTY_FUNCTION__, __LINE__);
				  DCETHREAD_RERAISE;
			  }
			  DCETHREAD_ENDTRY;
            maintain_thread_running = false;
            break;
        }
    }
    RPC_DBG_PRINTF(rpc_e_dbg_conv_thread, 1,
                   ("(network_maintain_liveness) shutting down%s...\n",
                    (maint_head == NULL)?" (no active)":""));

    RPC_MAINT_UNLOCK();
	 return NULL;
}

/*
 * R P C _ _ D G _ M A I N T A I N _ I N I T
 *
 * This routine performs any initializations required for the network
 * listener service maintain/monitor functions.  Note that way2 liveness
 * callbacks are handled by the conversation manager interface; we do
 * not need to register the interfaces because the runtime intercepts
 * and handles way2 callbacks as part of listener thread request
 * processing.
 */

PRIVATE void rpc__dg_maintain_init(void)
{
    unsigned32 st;

    /*
     * Gen the address space UUID that we will send to servers
     * to indicate that we're still alive.
     */

    uuid_create(&rpc_g_dg_my_cas_uuid, &st);
    if (st != rpc_s_ok)
    {
        /*
         * rpc_m_cant_create_uuid
         * "(%s) Can't create UUID"
         */
        rpc_dce_svc_printf (
            __FILE__, __LINE__,
            "%s",
            rpc_svc_general,
            svc_c_sev_fatal | svc_c_action_exit_bad,
            rpc_m_cant_create_uuid,
            "rpc__dg_maintain_init" );
    }

    /*
     * Initialize a private mutex.
     */

    RPC_MAINT_LOCK_INIT();
    RPC_COND_INIT(maintain_cond, rpc_g_maint_mutex);

    maint_head = NULL;
    maintain_thread_running = false;
    maintain_thread_was_running = false;
    stop_maintain_thread = false;
}

#ifdef ATFORK_SUPPORTED
/*
 * R P C _ _ D G _ M A I N T A I N _ F O R K _ H A N D L E R
 *
 * Handle fork related processing for this module.
 */

PRIVATE void rpc__dg_maintain_fork_handler
(
    rpc_fork_stage_id_t stage
)
{
    unsigned32 st;

    switch ((int)stage)
    {
    case RPC_C_PREFORK:
        RPC_MAINT_LOCK();
        maintain_thread_was_running = false;

        if (maintain_thread_running)
        {
            stop_maintain_thread = true;
            RPC_COND_SIGNAL(maintain_cond, rpc_g_maint_mutex);
            RPC_MAINT_UNLOCK();
            dcethread_join_throw (maintain_task, (void**) &st);
            RPC_MAINT_LOCK();/* FIXME: wtf
				DCETHREAD_TRY	{
                                    dcethread_detach_throw(maintain_task);
				}
				DCETHREAD_CATCH(dcethread_use_error_e)	{
				}
				DCETHREAD_ENDTRY; */
            maintain_thread_running = false;
            maintain_thread_was_running = true;
        }
        break;
    case RPC_C_POSTFORK_PARENT:
        if (maintain_thread_was_running)
        {
            maintain_thread_was_running = false;
            maintain_thread_running = true;
            stop_maintain_thread = false;
            dcethread_create_throw(&maintain_task, NULL,
                           network_maintain_liveness,
                           NULL);
        }
        RPC_MAINT_UNLOCK();
        break;
    case RPC_C_POSTFORK_CHILD:
        maintain_thread_was_running = false;
        maintain_thread_running = false;
        stop_maintain_thread = false;

        /*
         * Clear out the list... we should free resources...
         */
        maint_head = NULL;

        RPC_MAINT_UNLOCK();
        break;
    }
}
#endif /* ATFORK_SUPPORTED */