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