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