1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 * Copyright (c) 2008 Likewise Software, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1.  Redistributions of source code must retain the above copyright
12 *     notice, this list of conditions and the following disclaimer.
13 * 2.  Redistributions in binary form must reproduce the above copyright
14 *     notice, this list of conditions and the following disclaimer in the
15 *     documentation and/or other materials provided with the distribution.
16 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
17 *     contributors may be used to endorse or promote products derived from
18 *     this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Portions of this software have been released under the following terms:
32 *
33 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
34 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
35 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
36 *
37 * To anyone who acknowledges that this file is provided "AS IS"
38 * without any express or implied warranty:
39 * permission to use, copy, modify, and distribute this file for any
40 * purpose is hereby granted without fee, provided that the above
41 * copyright notices and this notice appears in all source code copies,
42 * and that none of the names of Open Software Foundation, Inc., Hewlett-
43 * Packard Company or Digital Equipment Corporation be used
44 * in advertising or publicity pertaining to distribution of the software
45 * without specific, written prior permission.  Neither Open Software
46 * Foundation, Inc., Hewlett-Packard Company nor Digital
47 * Equipment Corporation makes any representations about the suitability
48 * of this software for any purpose.
49 *
50 * Copyright (c) 2007, Novell, Inc. All rights reserved.
51 * Redistribution and use in source and binary forms, with or without
52 * modification, are permitted provided that the following conditions
53 * are met:
54 *
55 * 1.  Redistributions of source code must retain the above copyright
56 *     notice, this list of conditions and the following disclaimer.
57 * 2.  Redistributions in binary form must reproduce the above copyright
58 *     notice, this list of conditions and the following disclaimer in the
59 *     documentation and/or other materials provided with the distribution.
60 * 3.  Neither the name of Novell Inc. nor the names of its contributors
61 *     may be used to endorse or promote products derived from this
62 *     this software without specific prior written permission.
63 *
64 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
65 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
66 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
67 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
68 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
69 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
70 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
71 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
72 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
73 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
74 *
75 * @APPLE_LICENSE_HEADER_END@
76 */
77
78#include <config.h>
79
80/* Work around AIX WEXITSTATUS macro */
81#if defined(_AIX) && defined(_ALL_SOURCE)
82#undef _ALL_SOURCE
83#endif
84
85/* Work around HP-UX WEXITSTATUS macro */
86#if defined(__hpux__) && defined(_BSD)
87#undef _BSD
88#endif
89
90#include <errno.h>
91
92#include "dcethread-private.h"
93#include "dcethread-util.h"
94#include "dcethread-debug.h"
95
96#ifdef API
97
98#define ATFORK_MAX_HANDLERS 256
99
100/* pthread fork handling wrapper
101 *
102 * Using fork() in a process with multiple threads can be tricky because only
103 * the calling thread is duplicated into the child process, leaving things in
104 * a potentially inconsistent state. pthread_atfork() allows callbacks to be run
105 * before and after a fork() in order to perform cleanup, but the version in
106 * the final POSIX spec does not provide a user data pointer to the callbacks
107 * (which the draft spec presumably did).  This wrapper provides this capability
108 * by multiplexing multiple handlers through a single handler with the underlying
109 * pthreads implementation.
110 *
111 * All related functions hold an exclusion lock for the duration of their execution.
112 * This addresses two potential issues:
113 *     - Concurrent modification of the atfork_handlers array
114 *     - Handlers (such as in comfork.c) which are not tolerant of concurrent fork()
115 *       calls
116 * Given this, performing a fork() or registering a fork handler from a fork handler
117 * would be a Bad Idea.
118 */
119
120/* !!! HACK !!!
121 *
122 * Certain versions of SPARC Solaris 10 have a regression in the behavior of pthread_atfork()
123 * that can cause freezes and other bizarre behavior.  Therefore, we do not use it
124 * on that platform.
125 *
126 * The bug in question is Solaris bug 6570016.  It is fixed by patch 127111-03 or Solaris 10 u5.
127 *
128 * This hack means that DCE/RPC applications must always call exec() soon after fork(),
129 * or use dcethread_fork() instead.
130 */
131#if defined (sun) && defined(sparc)
132#  define AVOID_PTHREAD_ATFORK
133#endif
134
135typedef struct
136{
137    void *user_state;
138    void (*pre_fork)(void *);
139    void (*parent_fork)(void *);
140    void (*child_fork)(void *);
141} dcethread_atfork_handler;
142
143/* Initilization once control */
144static pthread_once_t atfork_once = DCETHREAD_ONCE_INIT;
145/* Exclusion lock -- prevents modification of handler list while fork()s are active */
146static pthread_rwlock_t atfork_lock;
147/* Array of handlers */
148static volatile dcethread_atfork_handler atfork_handlers[ATFORK_MAX_HANDLERS];
149/* Current size of the array */
150static volatile unsigned atfork_handlers_len = 0;
151
152/* Proxy callbacks which multiplex calls to all registered handlers */
153static void
154__dcethread_pre_fork(void)
155{
156    unsigned int i;
157
158    pthread_rwlock_rdlock(&atfork_lock);
159
160    for (i = 0; i < atfork_handlers_len; i++)
161    {
162	if (atfork_handlers[i].pre_fork)
163	    atfork_handlers[i].pre_fork(atfork_handlers[i].user_state);
164    }
165
166    pthread_rwlock_unlock(&atfork_lock);
167}
168
169static void
170__dcethread_parent_fork(void)
171{
172    unsigned int i;
173
174    pthread_rwlock_rdlock(&atfork_lock);
175
176    for (i = 0; i < atfork_handlers_len; i++)
177    {
178	if (atfork_handlers[i].parent_fork)
179	    atfork_handlers[i].parent_fork(atfork_handlers[i].user_state);
180    }
181
182    pthread_rwlock_unlock(&atfork_lock);
183}
184
185static void
186__dcethread_child_fork(void)
187{
188    unsigned int i;
189
190    pthread_rwlock_rdlock(&atfork_lock);
191
192    for (i = 0; i < atfork_handlers_len; i++)
193    {
194	if (atfork_handlers[i].child_fork)
195	    atfork_handlers[i].child_fork(atfork_handlers[i].user_state);
196    }
197
198    pthread_rwlock_unlock(&atfork_lock);
199}
200
201/* Registration function to add a new handler */
202
203static
204void
205__dcethread_atfork_init(void)
206{
207    if (pthread_rwlock_init(&atfork_lock, NULL) != 0)
208    {
209        abort();
210    }
211}
212
213static
214void
215dcethread_atfork_init(void)
216{
217    pthread_once(&atfork_once, __dcethread_atfork_init);
218}
219
220int
221dcethread_atfork(void *user_state, void (*pre_fork)(void *), void (*parent_fork)(void *), void (*child_fork)(void *))
222{
223    dcethread_atfork_handler handler;
224
225    dcethread_atfork_init();
226
227    pthread_rwlock_wrlock(&atfork_lock);
228
229    if (atfork_handlers_len >= ATFORK_MAX_HANDLERS)
230    {
231	pthread_rwlock_unlock(&atfork_lock);
232	return dcethread__set_errno(ENOMEM);
233    }
234
235    /* Fill in struct */
236    handler.user_state = user_state;
237    handler.pre_fork = pre_fork;
238    handler.child_fork = child_fork;
239    handler.parent_fork = parent_fork;
240
241#ifndef AVOID_PTHREAD_ATFORK
242    /* If no handlers have been registered yet, register our proxy functions exactly once with the
243       real pthread_atfork */
244    if (atfork_handlers_len == 0)
245    {
246	if (dcethread__set_errno(pthread_atfork(__dcethread_pre_fork, __dcethread_parent_fork, __dcethread_child_fork)))
247	{
248	    pthread_rwlock_unlock(&atfork_lock);
249	    return -1;
250	}
251    }
252#endif
253
254    /* Add handler to array */
255    atfork_handlers[atfork_handlers_len++] = handler;
256
257    pthread_rwlock_unlock(&atfork_lock);
258
259    return dcethread__set_errno(0);
260}
261
262int
263dcethread_atfork_throw(void *user_state, void (*pre_fork)(void *), void (*parent_fork)(void *), void (*child_fork)(void *))
264{
265    DCETHREAD_WRAP_THROW(dcethread_atfork(user_state, pre_fork, parent_fork, child_fork));
266}
267
268pid_t
269dcethread_fork(void)
270{
271    pid_t pid = -1;
272
273    dcethread_atfork_init();
274
275#ifdef AVOID_PTHREAD_ATFORK
276    __dcethread_pre_fork();
277#endif
278
279    pid = fork();
280
281#ifdef AVOID_PTHREAD_ATFORK
282    if (pid == 0)
283    {
284        __dcethread_child_fork();
285    }
286    else if (pid != -1)
287    {
288        __dcethread_parent_fork();
289    }
290#endif
291
292    return pid;
293}
294
295#endif /* API */
296
297#ifdef TEST
298
299#include <sys/types.h>
300#include <sys/wait.h>
301#include <unistd.h>
302
303#include "dcethread-test.h"
304
305struct called_s
306{
307    int pre, parent, child;
308};
309
310static void
311pre_handler(void *_data)
312{
313    MU_TRACE("Fork pre handler active in thread %p", dcethread_self());
314    ((struct called_s*) _data)->pre = 1;
315}
316
317static void
318parent_handler(void *_data)
319{
320    MU_TRACE("Fork parent handler active in thread %p", dcethread_self());
321    ((struct called_s*) _data)->parent = 1;
322}
323
324static void
325child_handler(void *_data)
326{
327    ((struct called_s*) _data)->child = 1;
328}
329
330MU_TEST(dcethread_atfork, basic)
331{
332    struct called_s called = {0,0,0};
333    uid_t child;
334
335    MU_TRY_DCETHREAD( dcethread_atfork(&called, pre_handler, parent_handler, child_handler) );
336
337    if ((child = dcethread_fork()))
338    {
339	if (child == -1)
340	{
341	    MU_FAILURE("fork() failed: %s", strerror(errno));
342	}
343
344	if (waitpid(child, &called.child, 0) != child)
345	{
346	    MU_FAILURE("waitpid() failed: %s", strerror(errno));
347	}
348
349        called.child = WEXITSTATUS(called.child);
350
351	MU_ASSERT(called.pre);
352	MU_ASSERT(called.parent);
353	MU_ASSERT(called.child);
354    }
355    else
356    {
357	exit(called.child);
358    }
359}
360
361#endif /* TEST */
362