1/* 2 * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include <fork.h> 8 9#include <unistd.h> 10#include <stdlib.h> 11#include <errno.h> 12 13#include <errno_private.h> 14#include <locks.h> 15#include <libroot_private.h> 16#include <runtime_loader.h> 17#include <syscalls.h> 18 19 20typedef struct fork_hook { 21 struct fork_hook *next; 22 void (*function)(void); 23} fork_hook; 24 25#define FORK_LOCK_NAME "fork lock" 26 27static fork_hook *sPrepareHooks, *sParentHooks, *sChildHooks; 28static fork_hook *sLastParentHook, *sLastChildHook; 29static mutex sForkLock = MUTEX_INITIALIZER(FORK_LOCK_NAME); 30 31extern thread_id __main_thread_id; 32 33 34/** Adds a hook to the specified list. 35 * If \a _lastHook is NULL, the hook will be added at the head of the list, 36 * else it will be added at the tail of the list. 37 * Since this function allocates memory, it can fail, and returns B_NO_MEMORY 38 * in that case. It returns B_OK on success. 39 */ 40 41static status_t 42add_fork_hook(fork_hook **_hooks, fork_hook **_lastHook, void (*function)(void)) 43{ 44 fork_hook *hook = (fork_hook *)malloc(sizeof(struct fork_hook)); 45 if (hook == NULL) 46 return B_NO_MEMORY; 47 48 hook->function = function; 49 50 if (_lastHook) { 51 // add hook at the end of the list 52 53 if (*_hooks == NULL) { 54 // first entry of this list 55 *_hooks = hook; 56 *_lastHook = hook; 57 } else { 58 // any other item 59#if 0 60 if (*_lastHook == NULL) { 61 // search for last hook (need if an item was added to the beginning only -- 62 // this can only be the case if this function is called directly, though) 63 fork_hook *last = *_hooks; 64 while (last->next) 65 last = last->next; 66 67 *_lastHook = last; 68 } 69#endif 70 71 (*_lastHook)->next = hook; 72 *_lastHook = hook; 73 } 74 75 hook->next = NULL; 76 } else { 77 // add hook at the beginning of the list 78 hook->next = *_hooks; 79 *_hooks = hook; 80 } 81 82 return B_OK; 83} 84 85 86/** Calls all hooks in the specified list in ascending order. 87 */ 88 89static void 90call_fork_hooks(fork_hook *hook) 91{ 92 while (hook) { 93 hook->function(); 94 hook = hook->next; 95 } 96} 97 98 99/** Private support function that registers the hooks that will be executed 100 * before and after the team is fork()ed. 101 * It is called from pthread_atfork() and atfork(). 102 */ 103 104status_t 105__register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) 106{ 107 status_t status = mutex_lock(&sForkLock); 108 if (status != B_OK) 109 return status; 110 111 if (prepare) 112 status = add_fork_hook(&sPrepareHooks, NULL, prepare); 113 114 if (status == B_OK && parent) 115 status = add_fork_hook(&sParentHooks, &sLastParentHook, parent); 116 117 if (status == B_OK && child) 118 status = add_fork_hook(&sChildHooks, &sLastChildHook, child); 119 120 mutex_unlock(&sForkLock); 121 return status; 122} 123 124 125pid_t 126fork(void) 127{ 128 thread_id thread; 129 status_t status; 130 131 status = mutex_lock(&sForkLock); 132 if (status != B_OK) 133 return status; 134 135 // call preparation hooks 136 call_fork_hooks(sPrepareHooks); 137 138 thread = _kern_fork(); 139 if (thread < 0) { 140 // something went wrong 141 mutex_unlock(&sForkLock); 142 __set_errno(thread); 143 return -1; 144 } 145 146 if (thread == 0) { 147 // we are the child 148 // ToDo: initialize child 149 __main_thread_id = find_thread(NULL); 150 mutex_init(&sForkLock, FORK_LOCK_NAME); 151 // TODO: The lock is already initialized and we in the fork()ing 152 // process we should make sure that it is in a consistent state when 153 // calling the kernel. 154 __gRuntimeLoader->reinit_after_fork(); 155 __reinit_pwd_backend_after_fork(); 156 157 call_fork_hooks(sChildHooks); 158 } else { 159 // we are the parent 160 call_fork_hooks(sParentHooks); 161 mutex_unlock(&sForkLock); 162 } 163 164 return thread; 165} 166 167 168pid_t 169vfork(void) 170{ 171 return fork(); 172} 173 174