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**      dgclive.c
82**
83**  FACILITY:
84**
85**      Remote Procedure Call (RPC)
86**
87**  ABSTRACT:
88**
89**  Routines for maintaining liveness of clients.
90**
91**
92*/
93
94#include <dg.h>
95#include <dgglob.h>
96
97#include <dce/conv.h>
98#include <dce/convc.h>
99
100/* =============================================================================== */
101
102/*
103 * Define a linked list element which the client runtime can use to keep
104 * track of which servers it needs to maintain liveness with.
105 *
106 * Here's the list elements...
107 */
108typedef struct maint_elt_t
109{
110    struct maint_elt_t   *next;       /* -> to next entry in list.       */
111    rpc_binding_rep_p_t  shand;       /* -> to server binding handle     */
112    unsigned8            refcnt;      /* -> # of entries for this server */
113} maint_elt_t, *maint_elt_p_t;
114
115/*
116 * And here's the head of the list...
117 */
118INTERNAL maint_elt_p_t maint_head;
119
120/*
121 * And here's the mutex that protects the list...
122 */
123INTERNAL rpc_mutex_t   rpc_g_maint_mutex;
124INTERNAL rpc_cond_t    maintain_cond;
125
126INTERNAL boolean maintain_thread_running = false;
127INTERNAL boolean maintain_thread_was_running = false;
128INTERNAL boolean stop_maintain_thread = false;
129
130INTERNAL dcethread*  maintain_task;
131
132/*
133 * Mutex lock macros
134 */
135
136#define RPC_MAINT_LOCK_INIT()   RPC_MUTEX_INIT (rpc_g_maint_mutex)
137#define RPC_MAINT_LOCK()        RPC_MUTEX_LOCK (rpc_g_maint_mutex)
138#define RPC_MAINT_UNLOCK()      RPC_MUTEX_UNLOCK (rpc_g_maint_mutex)
139
140/* ========================================================================= */
141
142INTERNAL void * network_maintain_liveness (void*);
143
144/* ========================================================================= */
145
146/*
147 * R P C _ _ D G _ N E T W O R K _ M A I N T
148 *
149 * This function is called, via the network listener service, by a client
150 * stub which needs to maintain context with a server.  A copy of the
151 * binding handle is made and entered into a list associated with a timer
152 * monitor.  This monitor will then periodically send an identifier to
153 * the server to assure it that this client is still alive.
154 */
155
156PRIVATE void rpc__dg_network_maint
157(
158    rpc_binding_rep_p_t binding_r,
159    unsigned32 *st
160)
161{
162    maint_elt_p_t maint;
163    rpc_dg_binding_client_p_t chand = (rpc_dg_binding_client_p_t) binding_r;
164
165    *st = rpc_s_ok;
166
167    RPC_MAINT_LOCK();
168
169    /*
170     * First, we need to traverse the list of maintained contexts to see
171     * if this server is already on it.  If we find a matching  address,
172     * we can just return.
173     */
174
175    for (maint = maint_head; maint != NULL; maint = maint->next)
176    {
177        if (rpc__naf_addr_compare(maint->shand->rpc_addr, binding_r->rpc_addr, st))
178        {
179            /*
180             * If we find a matching element, store a pointer to it in the
181             * client binding handle (so we don't have to do these compares
182             * when maint_stop gets called) and note the reference.
183             */
184
185            chand->maint_binding = maint->shand;
186            maint->refcnt++;
187            RPC_MAINT_UNLOCK();
188            return;
189        }
190    }
191
192    /*
193     * Need to make a new entry in the maintain list.  Alloc up a
194     * list element.
195     */
196
197    RPC_MEM_ALLOC(maint, maint_elt_p_t, sizeof *maint,
198            RPC_C_MEM_DG_MAINT, RPC_C_MEM_NOWAIT);
199
200    /*
201     * Make our own copy of the binding handle (so we have a handle to
202     * send INDY's to this server).  Reference the new binding in the
203     * client handle and note the reference.
204     */
205
206    rpc_binding_copy((rpc_binding_handle_t) chand,
207                     (rpc_binding_handle_t *) &maint->shand, st);
208    chand->maint_binding = maint->shand;
209    maint->refcnt = 1;
210
211    /*
212     * and thread it onto the head of the list.
213     */
214
215    maint->next = maint_head;
216    maint_head = maint;
217
218    /*
219     * If the binding handle had any authentication info associated with
220     * it, free it up now.   We don't want the convc_indy() calls using
221     * authentication.
222     */
223    rpc_binding_set_auth_info((rpc_binding_handle_t) maint->shand, NULL,
224                       rpc_c_protect_level_none, rpc_c_authn_none, NULL,
225                       rpc_c_authz_none, st);
226
227    /*
228     * Finally, check to make sure the 'context maintainer' thread has
229     * been started, and if not, start it up.
230     */
231
232    if (! maintain_thread_running)
233    {
234        maintain_thread_running = true;
235        dcethread_create_throw(&maintain_task, NULL,
236            (void*)network_maintain_liveness,
237            NULL);
238    }
239
240    RPC_MAINT_UNLOCK();
241}
242
243/*
244 * R P C _ _ D G _ N E T W O R K _ S T O P _ M A I N T
245 *
246 * This routine is called, via the network listener service, by a client stub
247 * when it wishes to discontinue maintaining context with a server.
248 */
249
250PRIVATE void rpc__dg_network_stop_maint
251(
252    rpc_binding_rep_p_t binding_r,
253    unsigned32 *st
254)
255{
256    maint_elt_p_t maint, prev = NULL;
257    rpc_dg_binding_client_p_t chand = (rpc_dg_binding_client_p_t) binding_r;
258
259    RPC_MAINT_LOCK();
260
261    /*
262     * Search through the list for the element which references this
263     * binding handle.
264     */
265
266    for (maint = maint_head; maint != NULL; maint = maint->next)
267    {
268        if (chand->maint_binding == maint->shand)
269        {
270            /*
271             * Remove the reference from the binding handle, and decrement
272             * the reference count in the list element.  If the count
273             * falls to 0, remove the element from the list.
274             */
275
276            chand->maint_binding = NULL;
277            if (--maint->refcnt == 0)
278            {
279                if (prev == NULL)
280                    maint_head = maint->next;
281                else
282                    prev->next = maint->next;
283                rpc_binding_free((rpc_binding_handle_t *)&maint->shand, st);
284                RPC_MEM_FREE(maint, RPC_C_MEM_DG_MAINT);
285            }
286            *st = rpc_s_ok;
287            RPC_MAINT_UNLOCK();
288            return;
289        }
290        prev = maint;
291    }
292
293    RPC_MAINT_UNLOCK();
294    *st = -1;           /*!!! didn't find it, need real error value here */
295}
296
297/*
298 * R P C _ _ D G _ N E T W O R K _ C L O S E
299 *
300 * This routine is called, via the network listener service, by a client stub
301 * when it wishes to disconnect frmo a server.
302 */
303
304PRIVATE void rpc__dg_network_close
305(
306    ATTRIBUTE_UNUSED rpc_binding_rep_p_t binding_r,
307    unsigned32 *st
308)
309{
310    /* this is a NOOP for datagram transports */
311    *st = rpc_s_ok;
312}
313
314/*
315 * N E T W O R K _ M A I N T A I N _ L I V E N E S S
316 *
317 * Base routine for the thread which periodically sends out the address
318 * space UUID of this process.  This UUID uniquely identifies this
319 * particular instance of this particular client process for use in
320 * maintaing context with servers.
321 */
322
323INTERNAL void * network_maintain_liveness(void * unused ATTRIBUTE_UNUSED)
324{
325    maint_elt_p_t ptr;
326    struct timespec next_ts;
327
328    RPC_DBG_PRINTF(rpc_e_dbg_conv_thread, 1,
329                   ("(network_maintain_liveness) starting up...\n"));
330
331    RPC_MAINT_LOCK();
332
333    while (stop_maintain_thread == false)
334    {
335        /*
336         * Send out INDYs every 20 seconds.
337         */
338        rpc__clock_timespec(rpc__clock_stamp()+20, &next_ts);
339
340        RPC_COND_TIMED_WAIT(maintain_cond, rpc_g_maint_mutex, &next_ts);
341        if (stop_maintain_thread == true)
342            break;
343
344        for (ptr = maint_head; ptr != NULL; ptr = ptr->next)
345        {
346            RPC_DBG_PRINTF(rpc_e_dbg_general, 3,
347                ("(maintain_liveness_timer) Doing convc_indy call\n"));
348
349            (*convc_v1_0_c_epv.convc_indy)((handle_t) ptr->shand,
350                                                   &rpc_g_dg_my_cas_uuid);
351        }
352
353        /*
354         * See if the list is empty...
355         */
356
357        if (maint_head == NULL)
358        {
359            /*
360             * Nothing left to do, so terminate the thread.
361             */
362			  /* FIXME: MNE */
363			  DCETHREAD_TRY	{
364            dcethread_detach_throw(maintain_task);
365			  }
366			  DCETHREAD_CATCH_ALL(THIS_CATCH) {
367				  fprintf(stderr, "XXX MIREK: %s: %s: %d: caught exception from detach\n",
368						  __FILE__, __PRETTY_FUNCTION__, __LINE__);
369				  DCETHREAD_RERAISE;
370			  }
371			  DCETHREAD_ENDTRY;
372            maintain_thread_running = false;
373            break;
374        }
375    }
376    RPC_DBG_PRINTF(rpc_e_dbg_conv_thread, 1,
377                   ("(network_maintain_liveness) shutting down%s...\n",
378                    (maint_head == NULL)?" (no active)":""));
379
380    RPC_MAINT_UNLOCK();
381	 return NULL;
382}
383
384/*
385 * R P C _ _ D G _ M A I N T A I N _ I N I T
386 *
387 * This routine performs any initializations required for the network
388 * listener service maintain/monitor functions.  Note that way2 liveness
389 * callbacks are handled by the conversation manager interface; we do
390 * not need to register the interfaces because the runtime intercepts
391 * and handles way2 callbacks as part of listener thread request
392 * processing.
393 */
394
395PRIVATE void rpc__dg_maintain_init(void)
396{
397    unsigned32 st;
398
399    /*
400     * Gen the address space UUID that we will send to servers
401     * to indicate that we're still alive.
402     */
403
404    uuid_create(&rpc_g_dg_my_cas_uuid, &st);
405    if (st != rpc_s_ok)
406    {
407        /*
408         * rpc_m_cant_create_uuid
409         * "(%s) Can't create UUID"
410         */
411        rpc_dce_svc_printf (
412            __FILE__, __LINE__,
413            "%s",
414            rpc_svc_general,
415            svc_c_sev_fatal | svc_c_action_exit_bad,
416            rpc_m_cant_create_uuid,
417            "rpc__dg_maintain_init" );
418    }
419
420    /*
421     * Initialize a private mutex.
422     */
423
424    RPC_MAINT_LOCK_INIT();
425    RPC_COND_INIT(maintain_cond, rpc_g_maint_mutex);
426
427    maint_head = NULL;
428    maintain_thread_running = false;
429    maintain_thread_was_running = false;
430    stop_maintain_thread = false;
431}
432
433#ifdef ATFORK_SUPPORTED
434/*
435 * R P C _ _ D G _ M A I N T A I N _ F O R K _ H A N D L E R
436 *
437 * Handle fork related processing for this module.
438 */
439
440PRIVATE void rpc__dg_maintain_fork_handler
441(
442    rpc_fork_stage_id_t stage
443)
444{
445    unsigned32 st;
446
447    switch ((int)stage)
448    {
449    case RPC_C_PREFORK:
450        RPC_MAINT_LOCK();
451        maintain_thread_was_running = false;
452
453        if (maintain_thread_running)
454        {
455            stop_maintain_thread = true;
456            RPC_COND_SIGNAL(maintain_cond, rpc_g_maint_mutex);
457            RPC_MAINT_UNLOCK();
458            dcethread_join_throw (maintain_task, (void**) &st);
459            RPC_MAINT_LOCK();/* FIXME: wtf
460				DCETHREAD_TRY	{
461                                    dcethread_detach_throw(maintain_task);
462				}
463				DCETHREAD_CATCH(dcethread_use_error_e)	{
464				}
465				DCETHREAD_ENDTRY; */
466            maintain_thread_running = false;
467            maintain_thread_was_running = true;
468        }
469        break;
470    case RPC_C_POSTFORK_PARENT:
471        if (maintain_thread_was_running)
472        {
473            maintain_thread_was_running = false;
474            maintain_thread_running = true;
475            stop_maintain_thread = false;
476            dcethread_create_throw(&maintain_task, NULL,
477                           network_maintain_liveness,
478                           NULL);
479        }
480        RPC_MAINT_UNLOCK();
481        break;
482    case RPC_C_POSTFORK_CHILD:
483        maintain_thread_was_running = false;
484        maintain_thread_running = false;
485        stop_maintain_thread = false;
486
487        /*
488         * Clear out the list... we should free resources...
489         */
490        maint_head = NULL;
491
492        RPC_MAINT_UNLOCK();
493        break;
494    }
495}
496#endif /* ATFORK_SUPPORTED */
497