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