1999-11-18 Roland McGrath <roland@baalperazim.frob.com>
[kopensolaris-gnu/glibc.git] / sysdeps / mach / hurd / fork.c
1 /* Copyright (C) 1994,95,96,97,99 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public
15    License along with the GNU C Library; see the file COPYING.LIB.  If not,
16    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17    Boston, MA 02111-1307, USA.  */
18
19 #include <errno.h>
20 #include <unistd.h>
21 #include <hurd.h>
22 #include <hurd/signal.h>
23 #include <setjmp.h>
24 #include "thread_state.h"
25 #include <sysdep.h>             /* For stack growth direction.  */
26 #include "set-hooks.h"
27 #include <assert.h>
28 #include "hurdmalloc.h"         /* XXX */
29
30
31 /* Things that want to be locked while forking.  */
32 symbol_set_declare (_hurd_fork_locks)
33
34
35 /* Things that want to be called before we fork, to prepare the parent for
36    task_create, when the new child task will inherit our address space.  */
37 DEFINE_HOOK (_hurd_fork_prepare_hook, (void));
38
39 /* Things that want to be called when we are forking, with the above all
40    locked.  They are passed the task port of the child.  The child process
41    is all set up except for doing proc_child, and has no threads yet.  */
42 DEFINE_HOOK (_hurd_fork_setup_hook, (void));
43
44 /* Things to be run in the child fork.  */
45 DEFINE_HOOK (_hurd_fork_child_hook, (void));
46
47 /* Things to be run in the parent fork.  */
48 DEFINE_HOOK (_hurd_fork_parent_hook, (void));
49
50
51 /* Clone the calling process, creating an exact copy.
52    Return -1 for errors, 0 to the new process,
53    and the process ID of the new process to the old process.  */
54 pid_t
55 __fork (void)
56 {
57   jmp_buf env;
58   pid_t pid;
59   size_t i;
60   error_t err;
61   struct hurd_sigstate *volatile ss;
62
63   ss = _hurd_self_sigstate ();
64   __spin_lock (&ss->critical_section_lock);
65
66 #undef  LOSE
67 #define LOSE assert_perror (err) /* XXX */
68
69   if (! setjmp (env))
70     {
71       process_t newproc;
72       task_t newtask;
73       thread_t thread, sigthread;
74       mach_port_urefs_t thread_refs, sigthread_refs;
75       struct machine_thread_state state;
76       mach_msg_type_number_t statecount;
77       mach_port_t *portnames = NULL;
78       mach_msg_type_number_t nportnames = 0;
79       mach_port_type_t *porttypes = NULL;
80       mach_msg_type_number_t nporttypes = 0;
81       thread_t *threads = NULL;
82       mach_msg_type_number_t nthreads = 0;
83       int ports_locked = 0, stopped = 0;
84
85       void resume_threads (void)
86         {
87           if (! stopped)
88             return;
89
90           assert (threads);
91
92           for (i = 0; i < nthreads; ++i)
93             if (threads[i] != ss->thread)
94               __thread_resume (threads[i]);
95           stopped = 0;
96         }
97
98       /* Run things that prepare for forking before we create the task.  */
99       RUN_HOOK (_hurd_fork_prepare_hook, ());
100
101       /* Lock things that want to be locked before we fork.  */
102       {
103         void *const *p;
104         for (p = symbol_set_first_element (_hurd_fork_locks);
105              ! symbol_set_end_p (_hurd_fork_locks, p);
106              ++p)
107           __mutex_lock (*p);
108       }
109       __mutex_lock (&_hurd_siglock);
110
111       newtask = MACH_PORT_NULL;
112       thread = sigthread = MACH_PORT_NULL;
113       newproc = MACH_PORT_NULL;
114
115       /* Lock all the port cells for the standard ports while we copy the
116          address space.  We want to insert all the send rights into the
117          child with the same names.  */
118       for (i = 0; i < _hurd_nports; ++i)
119         __spin_lock (&_hurd_ports[i].lock);
120       ports_locked = 1;
121
122
123       /* Stop all other threads while copying the address space,
124          so nothing changes.  */
125       err = __proc_dostop (_hurd_ports[INIT_PORT_PROC].port, ss->thread);
126       if (!err)
127         {
128           stopped = 1;
129
130 #define XXX_KERNEL_PAGE_FAULT_BUG /* XXX work around page fault bug in mk */
131
132 #ifdef XXX_KERNEL_PAGE_FAULT_BUG
133           /* Gag me with a pitchfork.
134              The bug scenario is this:
135
136              - The page containing __mach_task_self_ is paged out.
137              - The signal thread was faulting on that page when we
138                suspended it via proc_dostop.  It holds some lock, or set
139                some busy bit, or somesuch.
140              - Now this thread faults on that same page.
141              - GRATUIOUS DEADLOCK
142
143              We can break the deadlock by aborting the thread that faulted
144              first, which if the bug happened was the signal thread because
145              it is the only other thread and we just suspended it.
146              */
147           __thread_abort (_hurd_msgport_thread);
148 #endif
149           /* Create the child task.  It will inherit a copy of our memory.  */
150           err = __task_create (__mach_task_self (), 1, &newtask);
151         }
152
153       /* Unlock the global signal state lock, so we do not
154          block the signal thread any longer than necessary.  */
155       __mutex_unlock (&_hurd_siglock);
156
157       if (err)
158         LOSE;
159
160       /* Fetch the names of all ports used in this task.  */
161       if (err = __mach_port_names (__mach_task_self (),
162                                    &portnames, &nportnames,
163                                    &porttypes, &nporttypes))
164         LOSE;
165       if (nportnames != nporttypes)
166         {
167           err = EGRATUITOUS;
168           LOSE;
169         }
170
171       /* Get send rights for all the threads in this task.
172          We want to avoid giving these rights to the child.  */
173       if (err = __task_threads (__mach_task_self (), &threads, &nthreads))
174         LOSE;
175
176       /* Get the child process's proc server port.  We will insert it into
177          the child with the same name as we use for our own proc server
178          port; and we will need it to set the child's message port.  */
179       if (err = __proc_task2proc (_hurd_ports[INIT_PORT_PROC].port,
180                                   newtask, &newproc))
181         LOSE;
182
183       /* Insert all our port rights into the child task.  */
184       thread_refs = sigthread_refs = 0;
185       for (i = 0; i < nportnames; ++i)
186         {
187           if (porttypes[i] & MACH_PORT_TYPE_RECEIVE)
188             {
189               /* This is a receive right.  We want to give the child task
190                  its own new receive right under the same name.  */
191               err = __mach_port_allocate_name (newtask,
192                                                MACH_PORT_RIGHT_RECEIVE,
193                                                portnames[i]);
194               if (err == KERN_NAME_EXISTS)
195                 {
196                   /* It already has a right under this name (?!).  Well,
197                      there is this bizarre old Mach IPC feature (in #ifdef
198                      MACH_IPC_COMPAT in the ukernel) which results in new
199                      tasks getting a new receive right for task special
200                      port number 2.  What else might be going on I'm not
201                      sure.  So let's check.  */
202 #if !MACH_IPC_COMPAT
203 #define TASK_NOTIFY_PORT 2
204 #endif
205                   assert (({ mach_port_t thisport, notify_port;
206                              mach_msg_type_name_t poly;
207                              (__task_get_special_port (newtask,
208                                                        TASK_NOTIFY_PORT,
209                                                        &notify_port) == 0 &&
210                               __mach_port_extract_right
211                               (newtask,
212                                portnames[i],
213                                MACH_MSG_TYPE_MAKE_SEND,
214                                &thisport, &poly) == 0 &&
215                               (thisport == notify_port) &&
216                               __mach_port_deallocate (__mach_task_self (),
217                                                       thisport) == 0 &&
218                               __mach_port_deallocate (__mach_task_self (),
219                                                       notify_port) == 0);
220                            }));
221                 }
222               else if (err)
223                 LOSE;
224               if (porttypes[i] & MACH_PORT_TYPE_SEND)
225                 {
226                   /* Give the child as many send rights for its receive
227                      right as we have for ours.  */
228                   mach_port_urefs_t refs;
229                   mach_port_t port;
230                   mach_msg_type_name_t poly;
231                   if (err = __mach_port_get_refs (__mach_task_self (),
232                                                   portnames[i],
233                                                   MACH_PORT_RIGHT_SEND,
234                                                   &refs))
235                     LOSE;
236                   if (err = __mach_port_extract_right (newtask,
237                                                        portnames[i],
238                                                        MACH_MSG_TYPE_MAKE_SEND,
239                                                        &port, &poly))
240                     LOSE;
241                   if (portnames[i] == _hurd_msgport)
242                     {
243                       /* We just created a receive right for the child's
244                          message port and are about to insert send rights
245                          for it.  Now, while we happen to have a send right
246                          for it, give it to the proc server.  */
247                       mach_port_t old;
248                       if (err = __proc_setmsgport (newproc, port, &old))
249                         LOSE;
250                       if (old != MACH_PORT_NULL)
251                         /* XXX what to do here? */
252                         __mach_port_deallocate (__mach_task_self (), old);
253                       /* The new task will receive its own exceptions
254                          on its message port.  */
255                       if (err = __task_set_special_port (newtask,
256                                                          TASK_EXCEPTION_PORT,
257                                                          port))
258                         LOSE;
259                     }
260                   if (err = __mach_port_insert_right (newtask,
261                                                       portnames[i],
262                                                       port,
263                                                       MACH_MSG_TYPE_MOVE_SEND))
264                     LOSE;
265                   if (refs > 1 &&
266                       (err = __mach_port_mod_refs (newtask,
267                                                    portnames[i],
268                                                    MACH_PORT_RIGHT_SEND,
269                                                    refs - 1)))
270                     LOSE;
271                 }
272               if (porttypes[i] & MACH_PORT_TYPE_SEND_ONCE)
273                 {
274                   /* Give the child a send-once right for its receive right,
275                      since we have one for ours.  */
276                   mach_port_t port;
277                   mach_msg_type_name_t poly;
278                   if (err = __mach_port_extract_right
279                       (newtask,
280                        portnames[i],
281                        MACH_MSG_TYPE_MAKE_SEND_ONCE,
282                        &port, &poly))
283                     LOSE;
284                   if (err = __mach_port_insert_right
285                       (newtask,
286                        portnames[i], port,
287                        MACH_MSG_TYPE_MOVE_SEND_ONCE))
288                     LOSE;
289                 }
290             }
291           else if (porttypes[i] &
292                    (MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_DEAD_NAME))
293             {
294               /* This is a send right or a dead name.
295                  Give the child as many references for it as we have.  */
296               mach_port_urefs_t refs, *record_refs = NULL;
297               mach_port_t insert;
298               mach_msg_type_name_t insert_type = MACH_MSG_TYPE_COPY_SEND;
299               if (portnames[i] == newtask)
300                 /* Skip the name we use for the child's task port.  */
301                 continue;
302               if (portnames[i] == __mach_task_self ())
303                 /* For the name we use for our own task port,
304                    insert the child's task port instead.  */
305                 insert = newtask;
306               else if (portnames[i] == _hurd_ports[INIT_PORT_PROC].port)
307                 {
308                   /* Get the proc server port for the new task.  */
309                   if (err = __proc_task2proc (portnames[i], newtask, &insert))
310                     LOSE;
311                   insert_type = MACH_MSG_TYPE_MOVE_SEND;
312                 }
313               else if (portnames[i] == ss->thread)
314                 {
315                   /* For the name we use for our own thread port, we will
316                      insert the thread port for the child main user thread
317                      after we create it.  */
318                   insert = MACH_PORT_NULL;
319                   record_refs = &thread_refs;
320                   /* Allocate a dead name right for this name as a
321                      placeholder, so the kernel will not chose this name
322                      for any other new port (it might use it for one of the
323                      rights created when a thread is created).  */
324                   if (err = __mach_port_allocate_name
325                       (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
326                     LOSE;
327                 }
328               else if (portnames[i] == _hurd_msgport_thread)
329                 /* For the name we use for our signal thread's thread port,
330                    we will insert the thread port for the child's signal
331                    thread after we create it.  */
332                 {
333                   insert = MACH_PORT_NULL;
334                   record_refs = &sigthread_refs;
335                   /* Allocate a dead name right as a placeholder.  */
336                   if (err = __mach_port_allocate_name
337                       (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
338                     LOSE;
339                 }
340               else
341                 {
342                   /* Skip the name we use for any of our own thread ports.  */
343                   mach_msg_type_number_t j;
344                   for (j = 0; j < nthreads; ++j)
345                     if (portnames[i] == threads[j])
346                       break;
347                   if (j < nthreads)
348                     continue;
349
350                   /* Copy our own send right.  */
351                   insert = portnames[i];
352                 }
353               /* Find out how many user references we have for
354                  the send right with this name.  */
355               if (err = __mach_port_get_refs (__mach_task_self (),
356                                               portnames[i],
357                                               MACH_PORT_RIGHT_SEND,
358                                               record_refs ?: &refs))
359                 LOSE;
360               if (insert == MACH_PORT_NULL)
361                 continue;
362               if (insert == portnames[i] &&
363                   (porttypes[i] & MACH_PORT_TYPE_DEAD_NAME))
364                 /* This is a dead name; allocate another dead name
365                    with the same name in the child.  */
366               allocate_dead_name:
367                 err = __mach_port_allocate_name (newtask,
368                                                  MACH_PORT_RIGHT_DEAD_NAME,
369                                                  portnames[i]);
370               else
371                 /* Insert the chosen send right into the child.  */
372                 err = __mach_port_insert_right (newtask,
373                                                 portnames[i],
374                                                 insert, insert_type);
375               switch (err)
376                 {
377                 case KERN_NAME_EXISTS:
378                   {
379                     /* It already has a send right under this name (?!).
380                        Well, it starts out with a send right for its task
381                        port, and inherits the bootstrap and exception ports
382                        from us.  */
383                     mach_port_t childport;
384                     mach_msg_type_name_t poly;
385                     assert (__mach_port_extract_right (newtask, portnames[i],
386                                                        MACH_MSG_TYPE_COPY_SEND,
387                                                        &childport,
388                                                        &poly) == 0 &&
389                             childport == insert &&
390                             __mach_port_deallocate (__mach_task_self (),
391                                                     childport) == 0);
392                     break;
393                   }
394
395                 case KERN_INVALID_CAPABILITY:
396                   /* The port just died.  It was a send right,
397                      and now it's a dead name.  */
398                   goto allocate_dead_name;
399
400                 default:
401                   LOSE;
402                   break;
403
404                 case KERN_SUCCESS:
405                   /* Give the child as many user references as we have.  */
406                   if (refs > 1 &&
407                       (err = __mach_port_mod_refs (newtask,
408                                                    portnames[i],
409                                                    MACH_PORT_RIGHT_SEND,
410                                                    refs - 1)))
411                     LOSE;
412                 }
413             }
414         }
415
416       /* Unlock the standard port cells.  The child must unlock its own
417          copies too.  */
418       for (i = 0; i < _hurd_nports; ++i)
419         __spin_unlock (&_hurd_ports[i].lock);
420       ports_locked = 0;
421
422       /* All state has now been copied from the parent.  It is safe to
423          resume other parent threads.  */
424       resume_threads ();
425
426       /* Create the child main user thread and signal thread.  */
427       if ((err = __thread_create (newtask, &thread)) ||
428           (err = __thread_create (newtask, &sigthread)))
429         LOSE;
430
431       /* Insert send rights for those threads.  We previously allocated
432          dead name rights with the names we want to give the thread ports
433          in the child as placeholders.  Now deallocate them so we can use
434          the names.  */
435       if ((err = __mach_port_deallocate (newtask, ss->thread)) ||
436           (err = __mach_port_insert_right (newtask, ss->thread,
437                                            thread, MACH_MSG_TYPE_COPY_SEND)))
438         LOSE;
439       /* We have one extra user reference created at the beginning of this
440          function, accounted for by mach_port_names (and which will thus be
441          accounted for in the child below).  This extra right gets consumed
442          in the child by the store into _hurd_sigthread in the child fork.  */
443       if (thread_refs > 1 &&
444           (err = __mach_port_mod_refs (newtask, ss->thread,
445                                        MACH_PORT_RIGHT_SEND,
446                                        thread_refs)))
447         LOSE;
448       if ((_hurd_msgport_thread != MACH_PORT_NULL) /* Let user have none.  */
449           && ((err = __mach_port_deallocate (newtask, _hurd_msgport_thread)) ||
450               (err = __mach_port_insert_right (newtask, _hurd_msgport_thread,
451                                                sigthread,
452                                                MACH_MSG_TYPE_COPY_SEND))))
453         LOSE;
454       if (sigthread_refs > 1 &&
455           (err = __mach_port_mod_refs (newtask, _hurd_msgport_thread,
456                                        MACH_PORT_RIGHT_SEND,
457                                        sigthread_refs - 1)))
458         LOSE;
459
460       /* This seems like a convenient juncture to copy the proc server's
461          idea of what addresses our argv and envp are found at from the
462          parent into the child.  Since we happen to know that the child
463          shares our memory image, it is we who should do this copying.  */
464       {
465         vm_address_t argv, envp;
466         err = (__USEPORT (PROC, __proc_get_arg_locations (port, &argv, &envp))
467                ?: __proc_set_arg_locations (newproc, argv, envp));
468         if (err)
469           LOSE;
470       }
471
472       /* Set the child signal thread up to run the msgport server function
473          using the same signal thread stack copied from our address space.
474          We fetch the state before longjmp'ing it so that miscellaneous
475          registers not affected by longjmp (such as i386 segment registers)
476          are in their normal default state.  */
477       statecount = MACHINE_THREAD_STATE_COUNT;
478       if (err = __thread_get_state (_hurd_msgport_thread,
479                                     MACHINE_THREAD_STATE_FLAVOR,
480                                     (natural_t *) &state, &statecount))
481         LOSE;
482 #if STACK_GROWTH_UP
483 #define THREADVAR_SPACE (__hurd_threadvar_max \
484                          * sizeof *__hurd_sightread_variables)
485       if (__hurd_sigthread_stack_base == 0)
486         {
487           state.SP &= __hurd_threadvar_stack_mask;
488           state.SP += __hurd_threadvar_stack_offset + THREADVAR_SPACE;
489         }
490       else
491         state.SP = __hurd_sigthread_stack_base;
492 #else
493       if (__hurd_sigthread_stack_end == 0)
494         {
495           /* The signal thread has a normal stack assigned by cthreads.
496              The threadvar_stack variables conveniently tell us how
497              to get to the highest address in the stack, just below
498              the per-thread variables.  */
499           state.SP &= __hurd_threadvar_stack_mask;
500           state.SP += __hurd_threadvar_stack_offset;
501         }
502       else
503         state.SP = __hurd_sigthread_stack_end;
504 #endif
505       MACHINE_THREAD_STATE_SET_PC (&state,
506                                    (unsigned long int) _hurd_msgport_receive);
507       if (err = __thread_set_state (sigthread, MACHINE_THREAD_STATE_FLAVOR,
508                                     (natural_t *) &state, statecount))
509         LOSE;
510       /* We do not thread_resume SIGTHREAD here because the child
511          fork needs to do more setup before it can take signals.  */
512
513       /* Set the child user thread up to return 1 from the setjmp above.  */
514       _hurd_longjmp_thread_state (&state, env, 1);
515       if (err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR,
516                                     (natural_t *) &state, statecount))
517         LOSE;
518
519       /* Get the PID of the child from the proc server.  We must do this
520          before calling proc_child below, because at that point any
521          authorized POSIX.1 process may kill the child task with SIGKILL.  */
522       if (err = __USEPORT (PROC, __proc_task2pid (port, newtask, &pid)))
523         LOSE;
524
525       /* Register the child with the proc server.  It is important that
526          this be that last thing we do before starting the child thread
527          running.  Once proc_child has been done for the task, it appears
528          as a POSIX.1 process.  Any errors we get must be detected before
529          this point, and the child must have a message port so it responds
530          to POSIX.1 signals.  */
531       if (err = __USEPORT (PROC, __proc_child (port, newtask)))
532         LOSE;
533
534       /* This must be the absolutely last thing we do; we can't assume that
535          the child will remain alive for even a moment once we do this.  We
536          ignore errors because we have committed to the fork and are not
537          allowed to return them after the process becomes visible to
538          POSIX.1 (which happened right above when we called proc_child).  */
539       (void) __thread_resume (thread);
540
541     lose:
542       if (ports_locked)
543         for (i = 0; i < _hurd_nports; ++i)
544           __spin_unlock (&_hurd_ports[i].lock);
545
546       resume_threads ();
547
548       if (newtask != MACH_PORT_NULL)
549         {
550           if (err)
551             __task_terminate (newtask);
552           __mach_port_deallocate (__mach_task_self (), newtask);
553         }
554       if (thread != MACH_PORT_NULL)
555         __mach_port_deallocate (__mach_task_self (), thread);
556       if (sigthread != MACH_PORT_NULL)
557         __mach_port_deallocate (__mach_task_self (), sigthread);
558       if (newproc != MACH_PORT_NULL)
559         __mach_port_deallocate (__mach_task_self (), newproc);
560
561       if (portnames)
562         __vm_deallocate (__mach_task_self (),
563                          (vm_address_t) portnames,
564                          nportnames * sizeof (*portnames));
565       if (porttypes)
566         __vm_deallocate (__mach_task_self (),
567                          (vm_address_t) porttypes,
568                          nporttypes * sizeof (*porttypes));
569       if (threads)
570         {
571           for (i = 0; i < nthreads; ++i)
572             __mach_port_deallocate (__mach_task_self (), threads[i]);
573           __vm_deallocate (__mach_task_self (),
574                            (vm_address_t) threads,
575                            nthreads * sizeof (*threads));
576         }
577
578       /* Run things that want to run in the parent to restore it to
579          normality.  Usually prepare hooks and parent hooks are
580          symmetrical: the prepare hook arrests state in some way for the
581          fork, and the parent hook restores the state for the parent to
582          continue executing normally.  */
583       RUN_HOOK (_hurd_fork_parent_hook, ());
584     }
585   else
586     {
587       struct hurd_sigstate *oldstates;
588
589       /* We are the child task.  Unlock the standard port cells, which were
590          locked in the parent when we copied its memory.  The parent has
591          inserted send rights with the names that were in the cells then.  */
592       for (i = 0; i < _hurd_nports; ++i)
593         __spin_unlock (&_hurd_ports[i].lock);
594
595       /* We are the only thread in this new task, so we will
596          take the task-global signals.  */
597       _hurd_sigthread = ss->thread;
598
599       /* Unchain the sigstate structures for threads that existed in the
600          parent task but don't exist in this task (the child process).
601          Delay freeing them until later because some of the further setup
602          and unlocking might be required for free to work.  */
603       oldstates = _hurd_sigstates;
604       if (oldstates == ss)
605         oldstates = ss->next;
606       else
607         {
608           while (_hurd_sigstates->next != ss)
609             _hurd_sigstates = _hurd_sigstates->next;
610           _hurd_sigstates->next = ss->next;
611         }
612       ss->next = NULL;
613       _hurd_sigstates = ss;
614       __mutex_unlock (&_hurd_siglock);
615
616       /* Fetch our new process IDs from the proc server.  No need to
617          refetch our pgrp; it is always inherited from the parent (so
618          _hurd_pgrp is already correct), and the proc server will send us a
619          proc_newids notification when it changes.  */
620       err = __USEPORT (PROC, __proc_getpids (port, &_hurd_pid, &_hurd_ppid,
621                                              &_hurd_orphaned));
622
623       /* Forking clears the trace flag.  */
624       __sigemptyset (&_hurdsig_traced);
625
626       /* Run things that want to run in the child task to set up.  */
627       RUN_HOOK (_hurd_fork_child_hook, ());
628
629       /* Set up proc server-assisted fault recovery for the signal thread.  */
630       _hurdsig_fault_init ();
631
632       /* Start the signal thread listening on the message port.  */
633       if (!err)
634         err = __thread_resume (_hurd_msgport_thread);
635
636       /* Free the old sigstate structures.  */
637       while (oldstates != NULL)
638         {
639           struct hurd_sigstate *next = oldstates->next;
640           free (oldstates);
641           oldstates = next;
642         }
643       /* XXX what to do if we have any errors here? */
644
645       pid = 0;
646     }
647
648   /* Unlock things we locked before creating the child task.
649      They are locked in both the parent and child tasks.  */
650   {
651     void *const *p;
652     for (p = symbol_set_first_element (_hurd_fork_locks);
653          ! symbol_set_end_p (_hurd_fork_locks, p);
654          ++p)
655       __mutex_unlock (*p);
656   }
657
658   _hurd_critical_section_unlock (ss);
659
660   return err ? __hurd_fail (err) : pid;
661 }
662
663 weak_alias (__fork, fork)