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 20 typedef struct fork_hook { 21 struct fork_hook *next; 22 void (*function)(void); 23 } fork_hook; 24 25 #define FORK_LOCK_NAME "fork lock" 26 27 static fork_hook *sPrepareHooks, *sParentHooks, *sChildHooks; 28 static fork_hook *sLastParentHook, *sLastChildHook; 29 static mutex sForkLock = MUTEX_INITIALIZER(FORK_LOCK_NAME); 30 31 extern 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 41 static status_t 42 add_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 89 static void 90 call_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 104 status_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 125 pid_t 126 fork(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 168 pid_t 169 vfork(void) 170 { 171 return fork(); 172 } 173 174