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