| /* Copyright (C) 1994-2014 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <errno.h> |
| #include <unistd.h> |
| #include <hurd.h> |
| #include <hurd/signal.h> |
| #include <setjmp.h> |
| #include <thread_state.h> |
| #include <sysdep.h> /* For stack growth direction. */ |
| #include "set-hooks.h" |
| #include <assert.h> |
| #include "hurdmalloc.h" /* XXX */ |
| #include <tls.h> |
| |
| #undef __fork |
| |
| |
| /* Things that want to be locked while forking. */ |
| symbol_set_declare (_hurd_fork_locks) |
| |
| |
| /* Application callbacks registered through pthread_atfork. */ |
| DEFINE_HOOK (_hurd_atfork_prepare_hook, (void)); |
| DEFINE_HOOK (_hurd_atfork_child_hook, (void)); |
| DEFINE_HOOK (_hurd_atfork_parent_hook, (void)); |
| |
| /* Things that want to be called before we fork, to prepare the parent for |
| task_create, when the new child task will inherit our address space. */ |
| DEFINE_HOOK (_hurd_fork_prepare_hook, (void)); |
| |
| /* Things that want to be called when we are forking, with the above all |
| locked. They are passed the task port of the child. The child process |
| is all set up except for doing proc_child, and has no threads yet. */ |
| DEFINE_HOOK (_hurd_fork_setup_hook, (void)); |
| |
| /* Things to be run in the child fork. */ |
| DEFINE_HOOK (_hurd_fork_child_hook, (void)); |
| |
| /* Things to be run in the parent fork. */ |
| DEFINE_HOOK (_hurd_fork_parent_hook, (void)); |
| |
| |
| /* Clone the calling process, creating an exact copy. |
| Return -1 for errors, 0 to the new process, |
| and the process ID of the new process to the old process. */ |
| pid_t |
| __fork (void) |
| { |
| jmp_buf env; |
| pid_t pid; |
| size_t i; |
| error_t err; |
| struct hurd_sigstate *volatile ss; |
| |
| RUN_HOOK (_hurd_atfork_prepare_hook, ()); |
| |
| ss = _hurd_self_sigstate (); |
| __spin_lock (&ss->critical_section_lock); |
| |
| #undef LOSE |
| #define LOSE do { assert_perror (err); goto lose; } while (0) /* XXX */ |
| |
| if (! setjmp (env)) |
| { |
| process_t newproc; |
| task_t newtask; |
| thread_t thread, sigthread; |
| mach_port_urefs_t thread_refs, sigthread_refs; |
| struct machine_thread_state state; |
| mach_msg_type_number_t statecount; |
| mach_port_t *portnames = NULL; |
| mach_msg_type_number_t nportnames = 0; |
| mach_port_type_t *porttypes = NULL; |
| mach_msg_type_number_t nporttypes = 0; |
| thread_t *threads = NULL; |
| mach_msg_type_number_t nthreads = 0; |
| int ports_locked = 0, stopped = 0; |
| |
| void resume_threads (void) |
| { |
| if (! stopped) |
| return; |
| |
| assert (threads); |
| |
| for (i = 0; i < nthreads; ++i) |
| if (threads[i] != ss->thread) |
| __thread_resume (threads[i]); |
| stopped = 0; |
| } |
| |
| /* Run things that prepare for forking before we create the task. */ |
| RUN_HOOK (_hurd_fork_prepare_hook, ()); |
| |
| /* Lock things that want to be locked before we fork. */ |
| { |
| void *const *p; |
| for (p = symbol_set_first_element (_hurd_fork_locks); |
| ! symbol_set_end_p (_hurd_fork_locks, p); |
| ++p) |
| __mutex_lock (*p); |
| } |
| __mutex_lock (&_hurd_siglock); |
| |
| newtask = MACH_PORT_NULL; |
| thread = sigthread = MACH_PORT_NULL; |
| newproc = MACH_PORT_NULL; |
| |
| /* Lock all the port cells for the standard ports while we copy the |
| address space. We want to insert all the send rights into the |
| child with the same names. */ |
| for (i = 0; i < _hurd_nports; ++i) |
| __spin_lock (&_hurd_ports[i].lock); |
| ports_locked = 1; |
| |
| |
| /* Stop all other threads while copying the address space, |
| so nothing changes. */ |
| err = __proc_dostop (_hurd_ports[INIT_PORT_PROC].port, ss->thread); |
| if (!err) |
| { |
| stopped = 1; |
| |
| #define XXX_KERNEL_PAGE_FAULT_BUG /* XXX work around page fault bug in mk */ |
| |
| #ifdef XXX_KERNEL_PAGE_FAULT_BUG |
| /* Gag me with a pitchfork. |
| The bug scenario is this: |
| |
| - The page containing __mach_task_self_ is paged out. |
| - The signal thread was faulting on that page when we |
| suspended it via proc_dostop. It holds some lock, or set |
| some busy bit, or somesuch. |
| - Now this thread faults on that same page. |
| - GRATUIOUS DEADLOCK |
| |
| We can break the deadlock by aborting the thread that faulted |
| first, which if the bug happened was the signal thread because |
| it is the only other thread and we just suspended it. |
| */ |
| __thread_abort (_hurd_msgport_thread); |
| #endif |
| /* Create the child task. It will inherit a copy of our memory. */ |
| err = __task_create (__mach_task_self (), |
| #ifdef KERN_INVALID_LEDGER |
| NULL, 0, /* OSF Mach */ |
| #endif |
| 1, &newtask); |
| } |
| |
| /* Unlock the global signal state lock, so we do not |
| block the signal thread any longer than necessary. */ |
| __mutex_unlock (&_hurd_siglock); |
| |
| if (err) |
| LOSE; |
| |
| /* Fetch the names of all ports used in this task. */ |
| if (err = __mach_port_names (__mach_task_self (), |
| &portnames, &nportnames, |
| &porttypes, &nporttypes)) |
| LOSE; |
| if (nportnames != nporttypes) |
| { |
| err = EGRATUITOUS; |
| LOSE; |
| } |
| |
| /* Get send rights for all the threads in this task. |
| We want to avoid giving these rights to the child. */ |
| if (err = __task_threads (__mach_task_self (), &threads, &nthreads)) |
| LOSE; |
| |
| /* Get the child process's proc server port. We will insert it into |
| the child with the same name as we use for our own proc server |
| port; and we will need it to set the child's message port. */ |
| if (err = __proc_task2proc (_hurd_ports[INIT_PORT_PROC].port, |
| newtask, &newproc)) |
| LOSE; |
| |
| /* Insert all our port rights into the child task. */ |
| thread_refs = sigthread_refs = 0; |
| for (i = 0; i < nportnames; ++i) |
| { |
| if (porttypes[i] & MACH_PORT_TYPE_RECEIVE) |
| { |
| /* This is a receive right. We want to give the child task |
| its own new receive right under the same name. */ |
| err = __mach_port_allocate_name (newtask, |
| MACH_PORT_RIGHT_RECEIVE, |
| portnames[i]); |
| if (err == KERN_NAME_EXISTS) |
| { |
| /* It already has a right under this name (?!). Well, |
| there is this bizarre old Mach IPC feature (in #ifdef |
| MACH_IPC_COMPAT in the ukernel) which results in new |
| tasks getting a new receive right for task special |
| port number 2. What else might be going on I'm not |
| sure. So let's check. */ |
| #if !MACH_IPC_COMPAT |
| #define TASK_NOTIFY_PORT 2 |
| #endif |
| assert (({ mach_port_t thisport, notify_port; |
| mach_msg_type_name_t poly; |
| (__task_get_special_port (newtask, |
| TASK_NOTIFY_PORT, |
| ¬ify_port) == 0 && |
| __mach_port_extract_right |
| (newtask, |
| portnames[i], |
| MACH_MSG_TYPE_MAKE_SEND, |
| &thisport, &poly) == 0 && |
| (thisport == notify_port) && |
| __mach_port_deallocate (__mach_task_self (), |
| thisport) == 0 && |
| __mach_port_deallocate (__mach_task_self (), |
| notify_port) == 0); |
| })); |
| } |
| else if (err) |
| LOSE; |
| if (porttypes[i] & MACH_PORT_TYPE_SEND) |
| { |
| /* Give the child as many send rights for its receive |
| right as we have for ours. */ |
| mach_port_urefs_t refs; |
| mach_port_t port; |
| mach_msg_type_name_t poly; |
| if (err = __mach_port_get_refs (__mach_task_self (), |
| portnames[i], |
| MACH_PORT_RIGHT_SEND, |
| &refs)) |
| LOSE; |
| if (err = __mach_port_extract_right (newtask, |
| portnames[i], |
| MACH_MSG_TYPE_MAKE_SEND, |
| &port, &poly)) |
| LOSE; |
| if (portnames[i] == _hurd_msgport) |
| { |
| /* We just created a receive right for the child's |
| message port and are about to insert send rights |
| for it. Now, while we happen to have a send right |
| for it, give it to the proc server. */ |
| mach_port_t old; |
| if (err = __proc_setmsgport (newproc, port, &old)) |
| LOSE; |
| if (old != MACH_PORT_NULL) |
| /* XXX what to do here? */ |
| __mach_port_deallocate (__mach_task_self (), old); |
| /* The new task will receive its own exceptions |
| on its message port. */ |
| if (err = |
| #ifdef TASK_EXCEPTION_PORT |
| __task_set_special_port (newtask, |
| TASK_EXCEPTION_PORT, |
| port) |
| #elif defined (EXC_MASK_ALL) |
| __task_set_exception_ports |
| (newtask, EXC_MASK_ALL & ~(EXC_MASK_SYSCALL |
| | EXC_MASK_MACH_SYSCALL |
| | EXC_MASK_RPC_ALERT), |
| port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE) |
| #else |
| # error task_set_exception_port? |
| #endif |
| ) |
| LOSE; |
| } |
| if (err = __mach_port_insert_right (newtask, |
| portnames[i], |
| port, |
| MACH_MSG_TYPE_MOVE_SEND)) |
| LOSE; |
| if (refs > 1 && |
| (err = __mach_port_mod_refs (newtask, |
| portnames[i], |
| MACH_PORT_RIGHT_SEND, |
| refs - 1))) |
| LOSE; |
| } |
| if (porttypes[i] & MACH_PORT_TYPE_SEND_ONCE) |
| { |
| /* Give the child a send-once right for its receive right, |
| since we have one for ours. */ |
| mach_port_t port; |
| mach_msg_type_name_t poly; |
| if (err = __mach_port_extract_right |
| (newtask, |
| portnames[i], |
| MACH_MSG_TYPE_MAKE_SEND_ONCE, |
| &port, &poly)) |
| LOSE; |
| if (err = __mach_port_insert_right |
| (newtask, |
| portnames[i], port, |
| MACH_MSG_TYPE_MOVE_SEND_ONCE)) |
| LOSE; |
| } |
| } |
| else if (porttypes[i] & |
| (MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_DEAD_NAME)) |
| { |
| /* This is a send right or a dead name. |
| Give the child as many references for it as we have. */ |
| mach_port_urefs_t refs = 0, *record_refs = NULL; |
| mach_port_t insert; |
| mach_msg_type_name_t insert_type = MACH_MSG_TYPE_COPY_SEND; |
| if (portnames[i] == newtask || portnames[i] == newproc) |
| /* Skip the name we use for the child's task or proc ports. */ |
| continue; |
| if (portnames[i] == __mach_task_self ()) |
| /* For the name we use for our own task port, |
| insert the child's task port instead. */ |
| insert = newtask; |
| else if (portnames[i] == _hurd_ports[INIT_PORT_PROC].port) |
| { |
| /* Use the proc server port for the new task. */ |
| insert = newproc; |
| insert_type = MACH_MSG_TYPE_COPY_SEND; |
| } |
| else if (portnames[i] == ss->thread) |
| { |
| /* For the name we use for our own thread port, we will |
| insert the thread port for the child main user thread |
| after we create it. */ |
| insert = MACH_PORT_NULL; |
| record_refs = &thread_refs; |
| /* Allocate a dead name right for this name as a |
| placeholder, so the kernel will not chose this name |
| for any other new port (it might use it for one of the |
| rights created when a thread is created). */ |
| if (err = __mach_port_allocate_name |
| (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i])) |
| LOSE; |
| } |
| else if (portnames[i] == _hurd_msgport_thread) |
| /* For the name we use for our signal thread's thread port, |
| we will insert the thread port for the child's signal |
| thread after we create it. */ |
| { |
| insert = MACH_PORT_NULL; |
| record_refs = &sigthread_refs; |
| /* Allocate a dead name right as a placeholder. */ |
| if (err = __mach_port_allocate_name |
| (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i])) |
| LOSE; |
| } |
| else |
| { |
| /* Skip the name we use for any of our own thread ports. */ |
| mach_msg_type_number_t j; |
| for (j = 0; j < nthreads; ++j) |
| if (portnames[i] == threads[j]) |
| break; |
| if (j < nthreads) |
| continue; |
| |
| /* Copy our own send right. */ |
| insert = portnames[i]; |
| } |
| /* Find out how many user references we have for |
| the send right with this name. */ |
| if (err = __mach_port_get_refs (__mach_task_self (), |
| portnames[i], |
| MACH_PORT_RIGHT_SEND, |
| record_refs ?: &refs)) |
| LOSE; |
| if (insert == MACH_PORT_NULL) |
| continue; |
| if (insert == portnames[i] && |
| (porttypes[i] & MACH_PORT_TYPE_DEAD_NAME)) |
| /* This is a dead name; allocate another dead name |
| with the same name in the child. */ |
| allocate_dead_name: |
| err = __mach_port_allocate_name (newtask, |
| MACH_PORT_RIGHT_DEAD_NAME, |
| portnames[i]); |
| else |
| /* Insert the chosen send right into the child. */ |
| err = __mach_port_insert_right (newtask, |
| portnames[i], |
| insert, insert_type); |
| switch (err) |
| { |
| case KERN_NAME_EXISTS: |
| { |
| /* It already has a send right under this name (?!). |
| Well, it starts out with a send right for its task |
| port, and inherits the bootstrap and exception ports |
| from us. */ |
| mach_port_t childport; |
| mach_msg_type_name_t poly; |
| assert (__mach_port_extract_right (newtask, portnames[i], |
| MACH_MSG_TYPE_COPY_SEND, |
| &childport, |
| &poly) == 0 && |
| childport == insert && |
| __mach_port_deallocate (__mach_task_self (), |
| childport) == 0); |
| break; |
| } |
| |
| case KERN_INVALID_CAPABILITY: |
| /* The port just died. It was a send right, |
| and now it's a dead name. */ |
| goto allocate_dead_name; |
| |
| default: |
| LOSE; |
| break; |
| |
| case KERN_SUCCESS: |
| /* Give the child as many user references as we have. */ |
| if (refs > 1 && |
| (err = __mach_port_mod_refs (newtask, |
| portnames[i], |
| MACH_PORT_RIGHT_SEND, |
| refs - 1))) |
| LOSE; |
| } |
| } |
| } |
| |
| /* Unlock the standard port cells. The child must unlock its own |
| copies too. */ |
| for (i = 0; i < _hurd_nports; ++i) |
| __spin_unlock (&_hurd_ports[i].lock); |
| ports_locked = 0; |
| |
| /* All state has now been copied from the parent. It is safe to |
| resume other parent threads. */ |
| resume_threads (); |
| |
| /* Create the child main user thread and signal thread. */ |
| if ((err = __thread_create (newtask, &thread)) || |
| (err = __thread_create (newtask, &sigthread))) |
| LOSE; |
| |
| /* Insert send rights for those threads. We previously allocated |
| dead name rights with the names we want to give the thread ports |
| in the child as placeholders. Now deallocate them so we can use |
| the names. */ |
| if ((err = __mach_port_deallocate (newtask, ss->thread)) || |
| (err = __mach_port_insert_right (newtask, ss->thread, |
| thread, MACH_MSG_TYPE_COPY_SEND))) |
| LOSE; |
| /* We have one extra user reference created at the beginning of this |
| function, accounted for by mach_port_names (and which will thus be |
| accounted for in the child below). This extra right gets consumed |
| in the child by the store into _hurd_sigthread in the child fork. */ |
| if (thread_refs > 1 && |
| (err = __mach_port_mod_refs (newtask, ss->thread, |
| MACH_PORT_RIGHT_SEND, |
| thread_refs))) |
| LOSE; |
| if ((_hurd_msgport_thread != MACH_PORT_NULL) /* Let user have none. */ |
| && ((err = __mach_port_deallocate (newtask, _hurd_msgport_thread)) || |
| (err = __mach_port_insert_right (newtask, _hurd_msgport_thread, |
| sigthread, |
| MACH_MSG_TYPE_COPY_SEND)))) |
| LOSE; |
| if (sigthread_refs > 1 && |
| (err = __mach_port_mod_refs (newtask, _hurd_msgport_thread, |
| MACH_PORT_RIGHT_SEND, |
| sigthread_refs - 1))) |
| LOSE; |
| |
| /* This seems like a convenient juncture to copy the proc server's |
| idea of what addresses our argv and envp are found at from the |
| parent into the child. Since we happen to know that the child |
| shares our memory image, it is we who should do this copying. */ |
| { |
| vm_address_t argv, envp; |
| err = (__USEPORT (PROC, __proc_get_arg_locations (port, &argv, &envp)) |
| ?: __proc_set_arg_locations (newproc, argv, envp)); |
| if (err) |
| LOSE; |
| } |
| |
| /* Set the child signal thread up to run the msgport server function |
| using the same signal thread stack copied from our address space. |
| We fetch the state before longjmp'ing it so that miscellaneous |
| registers not affected by longjmp (such as i386 segment registers) |
| are in their normal default state. */ |
| statecount = MACHINE_THREAD_STATE_COUNT; |
| if (err = __thread_get_state (_hurd_msgport_thread, |
| MACHINE_THREAD_STATE_FLAVOR, |
| (natural_t *) &state, &statecount)) |
| LOSE; |
| #if STACK_GROWTH_UP |
| #define THREADVAR_SPACE (__hurd_threadvar_max \ |
| * sizeof *__hurd_sightread_variables) |
| if (__hurd_sigthread_stack_base == 0) |
| { |
| state.SP &= __hurd_threadvar_stack_mask; |
| state.SP += __hurd_threadvar_stack_offset + THREADVAR_SPACE; |
| } |
| else |
| state.SP = __hurd_sigthread_stack_base; |
| #else |
| if (__hurd_sigthread_stack_end == 0) |
| { |
| /* The signal thread has a normal stack assigned by cthreads. |
| The threadvar_stack variables conveniently tell us how |
| to get to the highest address in the stack, just below |
| the per-thread variables. */ |
| state.SP &= __hurd_threadvar_stack_mask; |
| state.SP += __hurd_threadvar_stack_offset; |
| } |
| else |
| state.SP = __hurd_sigthread_stack_end; |
| #endif |
| MACHINE_THREAD_STATE_SET_PC (&state, |
| (unsigned long int) _hurd_msgport_receive); |
| if (err = __thread_set_state (sigthread, MACHINE_THREAD_STATE_FLAVOR, |
| (natural_t *) &state, statecount)) |
| LOSE; |
| /* We do not thread_resume SIGTHREAD here because the child |
| fork needs to do more setup before it can take signals. */ |
| |
| /* Set the child user thread up to return 1 from the setjmp above. */ |
| _hurd_longjmp_thread_state (&state, env, 1); |
| |
| /* Do special thread setup for TLS if needed. */ |
| if (err = _hurd_tls_fork (thread, &state)) |
| LOSE; |
| |
| if (err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR, |
| (natural_t *) &state, statecount)) |
| LOSE; |
| |
| /* Get the PID of the child from the proc server. We must do this |
| before calling proc_child below, because at that point any |
| authorized POSIX.1 process may kill the child task with SIGKILL. */ |
| if (err = __USEPORT (PROC, __proc_task2pid (port, newtask, &pid))) |
| LOSE; |
| |
| /* Register the child with the proc server. It is important that |
| this be that last thing we do before starting the child thread |
| running. Once proc_child has been done for the task, it appears |
| as a POSIX.1 process. Any errors we get must be detected before |
| this point, and the child must have a message port so it responds |
| to POSIX.1 signals. */ |
| if (err = __USEPORT (PROC, __proc_child (port, newtask))) |
| LOSE; |
| |
| /* This must be the absolutely last thing we do; we can't assume that |
| the child will remain alive for even a moment once we do this. We |
| ignore errors because we have committed to the fork and are not |
| allowed to return them after the process becomes visible to |
| POSIX.1 (which happened right above when we called proc_child). */ |
| (void) __thread_resume (thread); |
| |
| lose: |
| if (ports_locked) |
| for (i = 0; i < _hurd_nports; ++i) |
| __spin_unlock (&_hurd_ports[i].lock); |
| |
| resume_threads (); |
| |
| if (newtask != MACH_PORT_NULL) |
| { |
| if (err) |
| __task_terminate (newtask); |
| __mach_port_deallocate (__mach_task_self (), newtask); |
| } |
| if (thread != MACH_PORT_NULL) |
| __mach_port_deallocate (__mach_task_self (), thread); |
| if (sigthread != MACH_PORT_NULL) |
| __mach_port_deallocate (__mach_task_self (), sigthread); |
| if (newproc != MACH_PORT_NULL) |
| __mach_port_deallocate (__mach_task_self (), newproc); |
| |
| if (portnames) |
| __vm_deallocate (__mach_task_self (), |
| (vm_address_t) portnames, |
| nportnames * sizeof (*portnames)); |
| if (porttypes) |
| __vm_deallocate (__mach_task_self (), |
| (vm_address_t) porttypes, |
| nporttypes * sizeof (*porttypes)); |
| if (threads) |
| { |
| for (i = 0; i < nthreads; ++i) |
| __mach_port_deallocate (__mach_task_self (), threads[i]); |
| __vm_deallocate (__mach_task_self (), |
| (vm_address_t) threads, |
| nthreads * sizeof (*threads)); |
| } |
| |
| /* Run things that want to run in the parent to restore it to |
| normality. Usually prepare hooks and parent hooks are |
| symmetrical: the prepare hook arrests state in some way for the |
| fork, and the parent hook restores the state for the parent to |
| continue executing normally. */ |
| RUN_HOOK (_hurd_fork_parent_hook, ()); |
| } |
| else |
| { |
| struct hurd_sigstate *oldstates; |
| |
| /* We are the child task. Unlock the standard port cells, which were |
| locked in the parent when we copied its memory. The parent has |
| inserted send rights with the names that were in the cells then. */ |
| for (i = 0; i < _hurd_nports; ++i) |
| __spin_unlock (&_hurd_ports[i].lock); |
| |
| /* We are one of the (exactly) two threads in this new task, we |
| will take the task-global signals. */ |
| _hurd_sigthread = ss->thread; |
| |
| /* Claim our sigstate structure and unchain the rest: the |
| threads existed in the parent task but don't exist in this |
| task (the child process). Delay freeing them until later |
| because some of the further setup and unlocking might be |
| required for free to work. Before we finish cleaning up, |
| we will reclaim the signal thread's sigstate structure (if |
| it had one). */ |
| oldstates = _hurd_sigstates; |
| if (oldstates == ss) |
| oldstates = ss->next; |
| else |
| { |
| while (_hurd_sigstates->next != ss) |
| _hurd_sigstates = _hurd_sigstates->next; |
| _hurd_sigstates->next = ss->next; |
| } |
| ss->next = NULL; |
| _hurd_sigstates = ss; |
| __mutex_unlock (&_hurd_siglock); |
| |
| /* Fetch our new process IDs from the proc server. No need to |
| refetch our pgrp; it is always inherited from the parent (so |
| _hurd_pgrp is already correct), and the proc server will send us a |
| proc_newids notification when it changes. */ |
| err = __USEPORT (PROC, __proc_getpids (port, &_hurd_pid, &_hurd_ppid, |
| &_hurd_orphaned)); |
| |
| /* Forking clears the trace flag. */ |
| __sigemptyset (&_hurdsig_traced); |
| |
| /* Run things that want to run in the child task to set up. */ |
| RUN_HOOK (_hurd_fork_child_hook, ()); |
| |
| /* Set up proc server-assisted fault recovery for the signal thread. */ |
| _hurdsig_fault_init (); |
| |
| /* Start the signal thread listening on the message port. */ |
| if (!err) |
| err = __thread_resume (_hurd_msgport_thread); |
| |
| /* Reclaim the signal thread's sigstate structure and free the |
| other old sigstate structures. */ |
| while (oldstates != NULL) |
| { |
| struct hurd_sigstate *next = oldstates->next; |
| |
| if (oldstates->thread == _hurd_msgport_thread) |
| { |
| /* If we have a second signal state structure then we |
| must have been through here before--not good. */ |
| assert (_hurd_sigstates->next == 0); |
| _hurd_sigstates->next = oldstates; |
| oldstates->next = 0; |
| } |
| else |
| free (oldstates); |
| |
| oldstates = next; |
| } |
| |
| /* XXX what to do if we have any errors here? */ |
| |
| pid = 0; |
| } |
| |
| /* Unlock things we locked before creating the child task. |
| They are locked in both the parent and child tasks. */ |
| { |
| void *const *p; |
| for (p = symbol_set_first_element (_hurd_fork_locks); |
| ! symbol_set_end_p (_hurd_fork_locks, p); |
| ++p) |
| __mutex_unlock (*p); |
| } |
| |
| _hurd_critical_section_unlock (ss); |
| |
| if (!err) |
| { |
| if (pid != 0) |
| RUN_HOOK (_hurd_atfork_parent_hook, ()); |
| else |
| RUN_HOOK (_hurd_atfork_child_hook, ()); |
| } |
| |
| return err ? __hurd_fail (err) : pid; |
| } |
| libc_hidden_def (__fork) |
| |
| weak_alias (__fork, fork) |