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