2002-01-02 Roland McGrath <roland@frob.com>
[kopensolaris-gnu/glibc.git] / hurd / hurdexec.c
1 /* Copyright (C) 1991,92,93,94,95,96,97,99,2001 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 Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the 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    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <errno.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <limits.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <hurd.h>
26 #include <hurd/fd.h>
27 #include <hurd/signal.h>
28 #include <assert.h>
29 #include <argz.h>
30
31 /* Overlay TASK, executing FILE with arguments ARGV and environment ENVP.
32    If TASK == mach_task_self (), some ports are dealloc'd by the exec server.
33    ARGV and ENVP are terminated by NULL pointers.  */
34 error_t
35 _hurd_exec (task_t task, file_t file,
36             char *const argv[], char *const envp[])
37 {
38   error_t err;
39   char *args, *env;
40   size_t argslen, envlen;
41   int ints[INIT_INT_MAX];
42   mach_port_t ports[_hurd_nports];
43   struct hurd_userlink ulink_ports[_hurd_nports];
44   file_t *dtable;
45   unsigned int dtablesize, i;
46   struct hurd_port **dtable_cells;
47   struct hurd_userlink *ulink_dtable;
48   struct hurd_sigstate *ss;
49   mach_port_t *please_dealloc, *pdp;
50
51   /* XXX needs to be hurdmalloc XXX */
52   if (argv == NULL)
53     args = NULL, argslen = 0;
54   else if (err = __argz_create (argv, &args, &argslen))
55     return err;
56   if (envp == NULL)
57     env = NULL, envlen = 0;
58   else if (err = __argz_create (envp, &env, &envlen))
59     goto outargs;
60
61   /* Load up the ports to give to the new program.  */
62   for (i = 0; i < _hurd_nports; ++i)
63     if (i == INIT_PORT_PROC && task != __mach_task_self ())
64       {
65         /* This is another task, so we need to ask the proc server
66            for the right proc server port for it.  */
67         if (err = __USEPORT (PROC, __proc_task2proc (port, task, &ports[i])))
68           {
69             while (--i > 0)
70               _hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]);
71             goto outenv;
72           }
73       }
74     else
75       ports[i] = _hurd_port_get (&_hurd_ports[i], &ulink_ports[i]);
76
77
78   /* Load up the ints to give the new program.  */
79   for (i = 0; i < INIT_INT_MAX; ++i)
80     switch (i)
81       {
82       case INIT_UMASK:
83         ints[i] = _hurd_umask;
84         break;
85
86       case INIT_SIGMASK:
87       case INIT_SIGIGN:
88       case INIT_SIGPENDING:
89         /* We will set these all below.  */
90         break;
91
92       case INIT_TRACEMASK:
93         ints[i] = _hurdsig_traced;
94         break;
95
96       default:
97         ints[i] = 0;
98       }
99
100   ss = _hurd_self_sigstate ();
101
102   assert (! __spin_lock_locked (&ss->critical_section_lock));
103   __spin_lock (&ss->critical_section_lock);
104
105   __spin_lock (&ss->lock);
106   ints[INIT_SIGMASK] = ss->blocked;
107   ints[INIT_SIGPENDING] = ss->pending;
108   ints[INIT_SIGIGN] = 0;
109   for (i = 1; i < NSIG; ++i)
110     if (ss->actions[i].sa_handler == SIG_IGN)
111       ints[INIT_SIGIGN] |= __sigmask (i);
112
113   /* We hold the sigstate lock until the exec has failed so that no signal
114      can arrive between when we pack the blocked and ignored signals, and
115      when the exec actually happens.  A signal handler could change what
116      signals are blocked and ignored.  Either the change will be reflected
117      in the exec, or the signal will never be delivered.  Setting the
118      critical section flag avoids anything we call trying to acquire the
119      sigstate lock.  */
120
121   __spin_unlock (&ss->lock);
122
123   /* Pack up the descriptor table to give the new program.  */
124   __mutex_lock (&_hurd_dtable_lock);
125
126   dtablesize = _hurd_dtable ? _hurd_dtablesize : _hurd_init_dtablesize;
127
128   if (task == __mach_task_self ())
129     /* Request the exec server to deallocate some ports from us if the exec
130        succeeds.  The init ports and descriptor ports will arrive in the
131        new program's exec_startup message.  If we failed to deallocate
132        them, the new program would have duplicate user references for them.
133        But we cannot deallocate them ourselves, because we must still have
134        them after a failed exec call.  */
135     please_dealloc = __alloca ((_hurd_nports + (2 * dtablesize))
136                                 * sizeof (mach_port_t));
137   else
138     please_dealloc = NULL;
139   pdp = please_dealloc;
140
141   if (_hurd_dtable != NULL)
142     {
143       dtable = __alloca (dtablesize * sizeof (dtable[0]));
144       ulink_dtable = __alloca (dtablesize * sizeof (ulink_dtable[0]));
145       dtable_cells = __alloca (dtablesize * sizeof (dtable_cells[0]));
146       for (i = 0; i < dtablesize; ++i)
147         {
148           struct hurd_fd *const d = _hurd_dtable[i];
149           if (d == NULL)
150             {
151               dtable[i] = MACH_PORT_NULL;
152               continue;
153             }
154           __spin_lock (&d->port.lock);
155           if (d->flags & FD_CLOEXEC)
156             {
157               /* This descriptor is marked to be closed on exec.
158                  So don't pass it to the new program.  */
159               dtable[i] = MACH_PORT_NULL;
160               if (pdp && d->port.port != MACH_PORT_NULL)
161                 {
162                   /* We still need to deallocate the ports.  */
163                   *pdp++ = d->port.port;
164                   if (d->ctty.port != MACH_PORT_NULL)
165                     *pdp++ = d->ctty.port;
166                 }
167               __spin_unlock (&d->port.lock);
168             }
169           else
170             {
171               if (pdp && d->ctty.port != MACH_PORT_NULL)
172                 /* All the elements of DTABLE are added to PLEASE_DEALLOC
173                    below, so we needn't add the port itself.
174                    But we must deallocate the ctty port as well as
175                    the normal port that got installed in DTABLE[I].  */
176                 *pdp++ = d->ctty.port;
177               dtable[i] = _hurd_port_locked_get (&d->port, &ulink_dtable[i]);
178               dtable_cells[i] = &d->port;
179             }
180         }
181     }
182   else
183     {
184       dtable = _hurd_init_dtable;
185       ulink_dtable = NULL;
186       dtable_cells = NULL;
187     }
188
189   /* Prune trailing null ports from the descriptor table.  */
190   while (dtablesize > 0 && dtable[dtablesize - 1] == MACH_PORT_NULL)
191     --dtablesize;
192
193   /* The information is all set up now.  Try to exec the file.  */
194
195   {
196     int flags;
197
198     if (pdp)
199       {
200         /* Request the exec server to deallocate some ports from us if the exec
201            succeeds.  The init ports and descriptor ports will arrive in the
202            new program's exec_startup message.  If we failed to deallocate
203            them, the new program would have duplicate user references for them.
204            But we cannot deallocate them ourselves, because we must still have
205            them after a failed exec call.  */
206
207         for (i = 0; i < _hurd_nports; ++i)
208           *pdp++ = ports[i];
209         for (i = 0; i < dtablesize; ++i)
210           *pdp++ = dtable[i];
211       }
212
213     flags = 0;
214 #ifdef EXEC_SIGTRAP
215     /* PTRACE_TRACEME sets all bits in _hurdsig_traced, which is propagated
216        through exec by INIT_TRACEMASK, so this checks if PTRACE_TRACEME has
217        been called in this process in any of its current or prior lives.  */
218     if (__sigismember (&_hurdsig_traced, SIGKILL))
219       flags |= EXEC_SIGTRAP;
220 #endif
221     err = __file_exec (file, task, flags,
222                        args, argslen, env, envlen,
223                        dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize,
224                        ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports,
225                        ints, INIT_INT_MAX,
226                        please_dealloc, pdp - please_dealloc,
227                        &_hurd_msgport, task == __mach_task_self () ? 1 : 0);
228   }
229
230   /* Release references to the standard ports.  */
231   for (i = 0; i < _hurd_nports; ++i)
232     if (i == INIT_PORT_PROC && task != __mach_task_self ())
233       __mach_port_deallocate (__mach_task_self (), ports[i]);
234     else
235       _hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]);
236
237   if (ulink_dtable != NULL)
238     /* Release references to the file descriptor ports.  */
239     for (i = 0; i < dtablesize; ++i)
240       if (dtable[i] != MACH_PORT_NULL)
241         _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]);
242
243   /* Release lock on the file descriptor table. */
244   __mutex_unlock (&_hurd_dtable_lock);
245
246   /* Safe to let signals happen now.  */
247   _hurd_critical_section_unlock (ss);
248
249  outargs:
250   free (args);
251  outenv:
252   free (env);
253   return err;
254 }