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