xref: /haiku/src/system/libroot/posix/unistd/fork.c (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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