Sat May 4 02:11:55 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>
[kopensolaris-gnu/glibc.git] / sysdeps / mach / hurd / fork.c
1 /* Copyright (C) 1994, 1995, 1996 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
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, 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,
375                                                 MACH_MSG_TYPE_COPY_SEND);
376               switch (err)
377                 {
378                 case KERN_NAME_EXISTS:
379                   {
380                     /* It already has a send right under this name (?!).
381                        Well, it starts out with a send right for its task
382                        port, and inherits the bootstrap and exception ports
383                        from us.  */
384                     mach_port_t childport;
385                     mach_msg_type_name_t poly;
386                     assert (__mach_port_extract_right (newtask, portnames[i],
387                                                        MACH_MSG_TYPE_COPY_SEND,
388                                                        &childport,
389                                                        &poly) == 0 &&
390                             childport == insert &&
391                             __mach_port_deallocate (__mach_task_self (),
392                                                     childport) == 0);
393                     break;
394                   }
395
396                 case KERN_INVALID_CAPABILITY:
397                   /* The port just died.  It was a send right,
398                      and now it's a dead name.  */
399                   goto allocate_dead_name;
400
401                 default:
402                   LOSE;
403                   break;
404
405                 case KERN_SUCCESS:
406                   /* Give the child as many user references as we have.  */
407                   if (refs > 1 &&
408                       (err = __mach_port_mod_refs (newtask,
409                                                    portnames[i],
410                                                    MACH_PORT_RIGHT_SEND,
411                                                    refs - 1)))
412                     LOSE;
413                 }
414             }
415         }
416
417       /* Unlock the standard port cells.  The child must unlock its own
418          copies too.  */
419       for (i = 0; i < _hurd_nports; ++i)
420         __spin_unlock (&_hurd_ports[i].lock);
421       ports_locked = 0;
422
423       /* All state has now been copied from the parent.  It is safe to
424          resume other parent threads.  */
425       resume_threads ();
426
427       /* Create the child main user thread and signal thread.  */
428       if ((err = __thread_create (newtask, &thread)) ||
429           (err = __thread_create (newtask, &sigthread)))
430         LOSE;
431
432       /* Insert send rights for those threads.  We previously allocated
433          dead name rights with the names we want to give the thread ports
434          in the child as placeholders.  Now deallocate them so we can use
435          the names.  */
436       if ((err = __mach_port_deallocate (newtask, ss->thread)) ||
437           (err = __mach_port_insert_right (newtask, ss->thread,
438                                            thread, MACH_MSG_TYPE_COPY_SEND)))
439         LOSE;
440       /* We have one extra user reference created at the beginning of this
441          function, accounted for by mach_port_names (and which will thus be
442          accounted for in the child below).  This extra right gets consumed
443          in the child by the store into _hurd_sigthread in the child fork.  */
444       if (thread_refs > 1 &&
445           (err = __mach_port_mod_refs (newtask, ss->thread,
446                                        MACH_PORT_RIGHT_SEND,
447                                        thread_refs)))
448         LOSE;
449       if ((_hurd_msgport_thread != MACH_PORT_NULL) /* Let user have none.  */
450           && ((err = __mach_port_deallocate (newtask, _hurd_msgport_thread)) ||
451               (err = __mach_port_insert_right (newtask, _hurd_msgport_thread,
452                                                sigthread,
453                                                MACH_MSG_TYPE_COPY_SEND))))
454         LOSE;
455       if (sigthread_refs > 1 &&
456           (err = __mach_port_mod_refs (newtask, _hurd_msgport_thread,
457                                        MACH_PORT_RIGHT_SEND,
458                                        sigthread_refs - 1)))
459         LOSE;
460
461       /* This seems like a convenient juncture to copy the proc server's
462          idea of what addresses our argv and envp are found at from the
463          parent into the child.  Since we happen to know that the child
464          shares our memory image, it is we who should do this copying.  */
465       {
466         vm_address_t argv, envp;
467         err = (__USEPORT (PROC, __proc_get_arg_locations (port, &argv, &envp))
468                ?: __proc_set_arg_locations (newproc, argv, envp));
469         if (err)
470           LOSE;
471       }
472
473       /* Set the child signal thread up to run the msgport server function
474          using the same signal thread stack copied from our address space.
475          We fetch the state before longjmp'ing it so that miscellaneous
476          registers not affected by longjmp (such as i386 segment registers)
477          are in their normal default state.  */
478       statecount = MACHINE_THREAD_STATE_COUNT;
479       if (err = __thread_get_state (_hurd_msgport_thread,
480                                     MACHINE_THREAD_STATE_FLAVOR,
481                                     (natural_t *) &state, &statecount))
482         LOSE;
483 #if STACK_GROWTH_UP
484       state.SP = __hurd_sigthread_stack_base;
485 #else
486       state.SP = __hurd_sigthread_stack_end;
487 #endif
488       MACHINE_THREAD_STATE_SET_PC (&state,
489                                    (unsigned long int) _hurd_msgport_receive);
490       if (err = __thread_set_state (sigthread, MACHINE_THREAD_STATE_FLAVOR,
491                                     (natural_t *) &state, statecount))
492         LOSE;
493       /* We do not thread_resume SIGTHREAD here because the child
494          fork needs to do more setup before it can take signals.  */
495
496       /* Set the child user thread up to return 1 from the setjmp above.  */
497       _hurd_longjmp_thread_state (&state, env, 1);
498       if (err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR,
499                                     (natural_t *) &state, statecount))
500         LOSE;
501
502       /* Get the PID of the child from the proc server.  We must do this
503          before calling proc_child below, because at that point any
504          authorized POSIX.1 process may kill the child task with SIGKILL.  */
505       if (err = __USEPORT (PROC, __proc_task2pid (port, newtask, &pid)))
506         LOSE;
507
508       /* Register the child with the proc server.  It is important that
509          this be that last thing we do before starting the child thread
510          running.  Once proc_child has been done for the task, it appears
511          as a POSIX.1 process.  Any errors we get must be detected before
512          this point, and the child must have a message port so it responds
513          to POSIX.1 signals.  */
514       if (err = __USEPORT (PROC, __proc_child (port, newtask)))
515         LOSE;
516
517       /* This must be the absolutely last thing we do; we can't assume that
518          the child will remain alive for even a moment once we do this.  We
519          ignore errors because we have committed to the fork and are not
520          allowed to return them after the process becomes visible to
521          POSIX.1 (which happened right above when we called proc_child).  */
522       (void) __thread_resume (thread);
523
524     lose:
525       if (ports_locked)
526         for (i = 0; i < _hurd_nports; ++i)
527           __spin_unlock (&_hurd_ports[i].lock);
528
529       resume_threads ();
530
531       if (newtask != MACH_PORT_NULL)
532         {
533           if (err)
534             __task_terminate (newtask);
535           __mach_port_deallocate (__mach_task_self (), newtask);
536         }
537       if (thread != MACH_PORT_NULL)
538         __mach_port_deallocate (__mach_task_self (), thread);
539       if (sigthread != MACH_PORT_NULL)
540         __mach_port_deallocate (__mach_task_self (), sigthread);
541       if (newproc != MACH_PORT_NULL)
542         __mach_port_deallocate (__mach_task_self (), newproc);
543
544       if (portnames)
545         __vm_deallocate (__mach_task_self (),
546                          (vm_address_t) portnames,
547                          nportnames * sizeof (*portnames));
548       if (porttypes)
549         __vm_deallocate (__mach_task_self (),
550                          (vm_address_t) porttypes,
551                          nporttypes * sizeof (*porttypes));
552       if (threads)
553         {
554           for (i = 0; i < nthreads; ++i)
555             __mach_port_deallocate (__mach_task_self (), threads[i]);
556           __vm_deallocate (__mach_task_self (),
557                            (vm_address_t) threads,
558                            nthreads * sizeof (*threads));
559         }
560
561       /* Run things that want to run in the parent to restore it to
562          normality.  Usually prepare hooks and parent hooks are
563          symmetrical: the prepare hook arrests state in some way for the
564          fork, and the parent hook restores the state for the parent to
565          continue executing normally.  */
566       RUN_HOOK (_hurd_fork_parent_hook, ());
567     }
568   else
569     {
570       struct hurd_sigstate *oldstates;
571
572       /* We are the child task.  Unlock the standard port cells, which were
573          locked in the parent when we copied its memory.  The parent has
574          inserted send rights with the names that were in the cells then.  */
575       for (i = 0; i < _hurd_nports; ++i)
576         __spin_unlock (&_hurd_ports[i].lock);
577
578       /* We are the only thread in this new task, so we will
579          take the task-global signals.  */
580       _hurd_sigthread = ss->thread;
581
582       /* Unchain the sigstate structures for threads that existed in the
583          parent task but don't exist in this task (the child process).
584          Delay freeing them until later because some of the further setup
585          and unlocking might be required for free to work.  */
586       oldstates = _hurd_sigstates;
587       if (oldstates == ss)
588         oldstates = ss->next;
589       else
590         {
591           while (_hurd_sigstates->next != ss)
592             _hurd_sigstates = _hurd_sigstates->next;
593           _hurd_sigstates->next = ss->next;
594         }
595       ss->next = NULL;
596       _hurd_sigstates = ss;
597       __mutex_unlock (&_hurd_siglock);
598
599       /* Fetch our new process IDs from the proc server.  No need to
600          refetch our pgrp; it is always inherited from the parent (so
601          _hurd_pgrp is already correct), and the proc server will send us a
602          proc_newids notification when it changes.  */
603       err = __USEPORT (PROC, __proc_getpids (port, &_hurd_pid, &_hurd_ppid,
604                                              &_hurd_orphaned));
605
606       /* Forking clears the trace flag.  */
607       __sigemptyset (&_hurdsig_traced);
608
609       /* Run things that want to run in the child task to set up.  */
610       RUN_HOOK (_hurd_fork_child_hook, ());
611
612       /* Set up proc server-assisted fault recovery for the signal thread.  */
613       _hurdsig_fault_init ();
614
615       /* Start the signal thread listening on the message port.  */
616       if (!err)
617         err = __thread_resume (_hurd_msgport_thread);
618
619       /* Free the old sigstate structures.  */
620       while (oldstates != NULL)
621         {
622           struct hurd_sigstate *next = oldstates->next;
623           free (oldstates);
624           oldstates = next;
625         }
626       /* XXX what to do if we have any errors here? */
627
628       pid = 0;
629     }
630
631   /* Unlock things we locked before creating the child task.
632      They are locked in both the parent and child tasks.  */
633   {
634     void *const *p;
635     for (p = symbol_set_first_element (_hurd_fork_locks);
636          ! symbol_set_end_p (_hurd_fork_locks, p);
637          ++p)
638       __mutex_unlock (*p);
639   }
640
641   _hurd_critical_section_unlock (ss);
642
643   return err ? __hurd_fail (err) : pid;
644 }
645
646 weak_alias (__fork, fork)