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