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