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