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