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