1999-04-27 Roland McGrath <roland@baalperazim.frob.com>
[platform/upstream/glibc.git] / hurd / hurdexec.c
1 /* Copyright (C) 1991,92,93,94,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 <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   /* The information is all set up now.  Try to exec the file.  */
190
191   {
192     int flags;
193
194     if (pdp)
195       {
196         /* Request the exec server to deallocate some ports from us if the exec
197            succeeds.  The init ports and descriptor ports will arrive in the
198            new program's exec_startup message.  If we failed to deallocate
199            them, the new program would have duplicate user references for them.
200            But we cannot deallocate them ourselves, because we must still have
201            them after a failed exec call.  */
202
203         for (i = 0; i < _hurd_nports; ++i)
204           *pdp++ = ports[i];
205         for (i = 0; i < dtablesize; ++i)
206           *pdp++ = dtable[i];
207       }
208
209     flags = 0;
210 #ifdef EXEC_SIGTRAP
211     /* PTRACE_TRACEME sets all bits in _hurdsig_traced, which is propagated
212        through exec by INIT_TRACEMASK, so this checks if PTRACE_TRACEME has
213        been called in this process in any of its current or prior lives.  */
214     if (__sigismember (&_hurdsig_traced, SIGKILL))
215       flags |= EXEC_SIGTRAP;
216 #endif
217     err = __file_exec (file, task, flags,
218                        args, argslen, env, envlen,
219                        dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize,
220                        ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports,
221                        ints, INIT_INT_MAX,
222                        please_dealloc, pdp - please_dealloc,
223                        &_hurd_msgport, task == __mach_task_self () ? 1 : 0);
224   }
225
226   /* Release references to the standard ports.  */
227   for (i = 0; i < _hurd_nports; ++i)
228     if (i == INIT_PORT_PROC && task != __mach_task_self ())
229       __mach_port_deallocate (__mach_task_self (), ports[i]);
230     else
231       _hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]);
232
233   if (ulink_dtable != NULL)
234     /* Release references to the file descriptor ports.  */
235     for (i = 0; i < dtablesize; ++i)
236       if (dtable[i] != MACH_PORT_NULL)
237         _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]);
238
239   /* Release lock on the file descriptor table. */
240   __mutex_unlock (&_hurd_dtable_lock);
241
242   /* Safe to let signals happen now.  */
243   _hurd_critical_section_unlock (ss);
244
245  outargs:
246   free (args);
247  outenv:
248   free (env);
249   return err;
250 }