]>
Commit | Line | Data |
---|---|---|
03aae005 | 1 | /* spawn a new process running an executable. Hurd version. |
f7a9f785 | 2 | Copyright (C) 2001-2016 Free Software Foundation, Inc. |
03aae005 RM |
3 | This file is part of the GNU C Library. |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
cc7375ce RM |
6 | modify it under the terms of the GNU Lesser General Public License as |
7 | published by the Free Software Foundation; either version 2.1 of the | |
03aae005 RM |
8 | License, or (at your option) any later version. |
9 | ||
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
cc7375ce | 13 | Lesser General Public License for more details. |
03aae005 | 14 | |
cc7375ce | 15 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 PE |
16 | License along with the GNU C Library; see the file COPYING.LIB. If |
17 | not, see <http://www.gnu.org/licenses/>. */ | |
03aae005 RM |
18 | |
19 | #include <errno.h> | |
20 | #include <fcntl.h> | |
21 | #include <paths.h> | |
22 | #include <spawn.h> | |
23 | #include <stdlib.h> | |
24 | #include <string.h> | |
25 | #include <unistd.h> | |
26 | #include <hurd.h> | |
27 | #include <hurd/signal.h> | |
28 | #include <hurd/fd.h> | |
29 | #include <hurd/id.h> | |
30 | #include <hurd/lookup.h> | |
31 | #include <hurd/resource.h> | |
32 | #include <assert.h> | |
33 | #include <argz.h> | |
34 | #include "spawn_int.h" | |
35 | ||
36 | /* Spawn a new process executing PATH with the attributes describes in *ATTRP. | |
37 | Before running the process perform the actions described in FILE-ACTIONS. */ | |
38 | int | |
39 | __spawni (pid_t *pid, const char *file, | |
40 | const posix_spawn_file_actions_t *file_actions, | |
41 | const posix_spawnattr_t *attrp, | |
42 | char *const argv[], char *const envp[], | |
d96de963 | 43 | int xflags) |
03aae005 RM |
44 | { |
45 | pid_t new_pid; | |
46 | char *path, *p, *name; | |
47 | size_t len; | |
48 | size_t pathlen; | |
49 | short int flags; | |
50 | ||
51 | /* The generic POSIX.1 implementation of posix_spawn uses fork and exec. | |
52 | In traditional POSIX systems (Unix, Linux, etc), the only way to | |
53 | create a new process is by fork, which also copies all the things from | |
54 | the parent process that will be immediately wiped and replaced by the | |
55 | exec. | |
56 | ||
57 | This Hurd implementation works by doing an exec on a fresh task, | |
58 | without ever doing all the work of fork. The only work done by fork | |
59 | that remains visible after an exec is registration with the proc | |
60 | server, and the inheritance of various values and ports. All those | |
61 | inherited values and ports are what get collected up and passed in the | |
62 | file_exec RPC by an exec call. So we do the proc server registration | |
63 | here, following the model of fork (see fork.c). We then collect up | |
64 | the inherited values and ports from this (parent) process following | |
65 | the model of exec (see hurd/hurdexec.c), modify or replace each value | |
66 | that fork would (plus the specific changes demanded by ATTRP and | |
67 | FILE_ACTIONS), and make the file_exec RPC on the requested executable | |
68 | file with the child process's task port rather than our own. This | |
69 | should be indistinguishable from the fork + exec implementation, | |
70 | except that all errors will be detected here (in the parent process) | |
71 | and return proper errno codes rather than the child dying with 127. | |
72 | ||
73 | XXX The one exception to this supposed indistinguishableness is that | |
74 | when posix_spawn_file_actions_addopen has been used, the parent | |
75 | process can do various filesystem RPCs on the child's behalf, rather | |
76 | than the child process doing it. If these block due to a broken or | |
77 | malicious filesystem server or just a blocked network fs or a serial | |
78 | port waiting for carrier detect (!!), the parent's posix_spawn call | |
79 | can block arbitrarily rather than just the child blocking. Possible | |
80 | solutions include: | |
81 | * punt to plain fork + exec implementation if addopen was used | |
82 | ** easy to do | |
83 | ** gives up all benefits of this implementation in that case | |
84 | * if addopen was used, don't do any file actions at all here; | |
85 | instead, exec an installed helper program e.g.: | |
86 | /libexec/spawn-helper close 3 dup2 1 2 open 0 /file 0x123 0666 exec /bin/foo foo a1 a2 | |
87 | ** extra exec might be more or less overhead than fork | |
88 | * could do some weird half-fork thing where the child would inherit | |
89 | our vm and run some code here, but not do the full work of fork | |
90 | ||
91 | XXX Actually, the parent opens the executable file on behalf of | |
92 | the child, and that has all the same issues. | |
93 | ||
94 | I am favoring the half-fork solution. That is, we do task_create with | |
95 | vm inheritance, and we setjmp/longjmp the child like fork does. But | |
96 | rather than all the fork hair, the parent just packs up init/dtable | |
97 | ports and does a single IPC to a receive right inserted in the child. */ | |
98 | ||
99 | error_t err; | |
100 | task_t task; | |
101 | file_t execfile; | |
102 | process_t proc; | |
103 | auth_t auth; | |
104 | int ints[INIT_INT_MAX]; | |
105 | file_t *dtable; | |
106 | unsigned int dtablesize, orig_dtablesize, i; | |
107 | struct hurd_port **dtable_cells; | |
108 | char *dtable_cloexec; | |
109 | struct hurd_userlink *ulink_dtable = NULL; | |
110 | struct hurd_sigstate *ss; | |
111 | ||
112 | /* For POSIX_SPAWN_RESETIDS, this reauthenticates our root/current | |
113 | directory ports with the new AUTH port. */ | |
114 | file_t rcrdir = MACH_PORT_NULL, rcwdir = MACH_PORT_NULL; | |
115 | error_t reauthenticate (int which, file_t *result) | |
116 | { | |
117 | error_t err; | |
118 | mach_port_t ref; | |
119 | if (*result != MACH_PORT_NULL) | |
120 | return 0; | |
121 | ref = __mach_reply_port (); | |
122 | err = HURD_PORT_USE | |
123 | (&_hurd_ports[which], | |
124 | ({ | |
125 | err = __io_reauthenticate (port, ref, MACH_MSG_TYPE_MAKE_SEND); | |
126 | if (!err) | |
127 | err = __auth_user_authenticate (auth, | |
128 | ref, MACH_MSG_TYPE_MAKE_SEND, | |
129 | result); | |
130 | err; | |
131 | })); | |
132 | __mach_port_destroy (__mach_task_self (), ref); | |
133 | return err; | |
134 | } | |
135 | ||
136 | /* Reauthenticate one of our file descriptors for the child. A null | |
137 | element of DTABLE_CELLS indicates a descriptor that was already | |
138 | reauthenticated, or was newly opened on behalf of the child. */ | |
139 | error_t reauthenticate_fd (int fd) | |
140 | { | |
141 | if (dtable_cells[fd] != NULL) | |
142 | { | |
143 | file_t newfile; | |
144 | mach_port_t ref = __mach_reply_port (); | |
145 | error_t err = __io_reauthenticate (dtable[fd], | |
146 | ref, MACH_MSG_TYPE_MAKE_SEND); | |
147 | if (!err) | |
148 | err = __auth_user_authenticate (auth, | |
149 | ref, MACH_MSG_TYPE_MAKE_SEND, | |
150 | &newfile); | |
151 | __mach_port_destroy (__mach_task_self (), ref); | |
152 | if (err) | |
153 | return err; | |
154 | _hurd_port_free (dtable_cells[fd], &ulink_dtable[fd], dtable[fd]); | |
155 | dtable_cells[fd] = NULL; | |
156 | dtable[fd] = newfile; | |
157 | } | |
158 | return 0; | |
159 | } | |
160 | ||
161 | /* These callbacks are for looking up file names on behalf of the child. */ | |
162 | error_t child_init_port (int which, error_t (*operate) (mach_port_t)) | |
163 | { | |
164 | if (flags & POSIX_SPAWN_RESETIDS) | |
165 | switch (which) | |
166 | { | |
167 | case INIT_PORT_AUTH: | |
168 | return (*operate) (auth); | |
169 | case INIT_PORT_CRDIR: | |
170 | return (reauthenticate (INIT_PORT_CRDIR, &rcrdir) | |
171 | ?: (*operate) (rcrdir)); | |
172 | case INIT_PORT_CWDIR: | |
173 | return (reauthenticate (INIT_PORT_CWDIR, &rcwdir) | |
174 | ?: (*operate) (rcwdir)); | |
175 | } | |
176 | assert (which != INIT_PORT_PROC); | |
177 | return _hurd_ports_use (which, operate); | |
178 | } | |
179 | file_t child_fd (int fd) | |
180 | { | |
181 | if ((unsigned int) fd < dtablesize && dtable[fd] != MACH_PORT_NULL) | |
182 | { | |
183 | if (flags & POSIX_SPAWN_RESETIDS) | |
184 | { | |
185 | /* Reauthenticate this descriptor right now, | |
186 | since it is going to be used on behalf of the child. */ | |
187 | errno = reauthenticate_fd (fd); | |
188 | if (errno) | |
189 | return MACH_PORT_NULL; | |
190 | } | |
191 | __mach_port_mod_refs (__mach_task_self (), dtable[fd], | |
192 | MACH_PORT_RIGHT_SEND, +1); | |
193 | return dtable[fd]; | |
194 | } | |
195 | errno = EBADF; | |
196 | return MACH_PORT_NULL; | |
197 | } | |
198 | inline error_t child_lookup (const char *file, int oflag, mode_t mode, | |
199 | file_t *result) | |
200 | { | |
201 | return __hurd_file_name_lookup (&child_init_port, &child_fd, 0, | |
202 | file, oflag, mode, result); | |
203 | } | |
204 | ||
205 | ||
206 | /* Do this once. */ | |
207 | flags = attrp == NULL ? 0 : attrp->__flags; | |
208 | ||
209 | /* Generate the new process. We create a task that does not inherit our | |
210 | memory, and then register it as our child like fork does. See fork.c | |
211 | for comments about the sequencing of these proc operations. */ | |
212 | ||
7595ddb8 RM |
213 | err = __task_create (__mach_task_self (), |
214 | #ifdef KERN_INVALID_LEDGER | |
215 | NULL, 0, /* OSF Mach */ | |
216 | #endif | |
217 | 0, &task); | |
03aae005 RM |
218 | if (err) |
219 | return __hurd_fail (err); | |
220 | // From here down we must deallocate TASK and PROC before returning. | |
221 | proc = MACH_PORT_NULL; | |
222 | auth = MACH_PORT_NULL; | |
223 | err = __USEPORT (PROC, __proc_task2pid (port, task, &new_pid)); | |
224 | if (!err) | |
225 | err = __USEPORT (PROC, __proc_task2proc (port, task, &proc)); | |
226 | if (!err) | |
227 | err = __USEPORT (PROC, __proc_child (port, task)); | |
228 | if (err) | |
229 | goto out; | |
230 | ||
231 | /* Load up the ints to give the new program. */ | |
232 | memset (ints, 0, sizeof ints); | |
233 | ints[INIT_UMASK] = _hurd_umask; | |
234 | ints[INIT_TRACEMASK] = _hurdsig_traced; | |
235 | ||
236 | ss = _hurd_self_sigstate (); | |
237 | ||
238 | assert (! __spin_lock_locked (&ss->critical_section_lock)); | |
239 | __spin_lock (&ss->critical_section_lock); | |
240 | ||
241 | __spin_lock (&ss->lock); | |
242 | ints[INIT_SIGMASK] = ss->blocked; | |
243 | ints[INIT_SIGPENDING] = ss->pending; | |
244 | ints[INIT_SIGIGN] = 0; | |
245 | /* Unless we were asked to reset all handlers to SIG_DFL, | |
246 | pass down the set of signals that were set to SIG_IGN. */ | |
247 | if ((flags & POSIX_SPAWN_SETSIGDEF) == 0) | |
248 | for (i = 1; i < NSIG; ++i) | |
249 | if (ss->actions[i].sa_handler == SIG_IGN) | |
250 | ints[INIT_SIGIGN] |= __sigmask (i); | |
251 | ||
252 | /* We hold the sigstate lock until the exec has failed so that no signal | |
253 | can arrive between when we pack the blocked and ignored signals, and | |
254 | when the exec actually happens. A signal handler could change what | |
255 | signals are blocked and ignored. Either the change will be reflected | |
256 | in the exec, or the signal will never be delivered. Setting the | |
257 | critical section flag avoids anything we call trying to acquire the | |
258 | sigstate lock. */ | |
259 | ||
260 | __spin_unlock (&ss->lock); | |
261 | ||
262 | /* Set signal mask. */ | |
263 | if ((flags & POSIX_SPAWN_SETSIGMASK) != 0) | |
264 | ints[INIT_SIGMASK] = attrp->__ss; | |
265 | ||
266 | #ifdef _POSIX_PRIORITY_SCHEDULING | |
267 | /* Set the scheduling algorithm and parameters. */ | |
268 | # error implement me | |
269 | if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) | |
270 | == POSIX_SPAWN_SETSCHEDPARAM) | |
271 | { | |
272 | if (__sched_setparam (0, &attrp->__sp) == -1) | |
273 | _exit (SPAWN_ERROR); | |
274 | } | |
275 | else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0) | |
276 | { | |
277 | if (__sched_setscheduler (0, attrp->__policy, | |
278 | (flags & POSIX_SPAWN_SETSCHEDPARAM) != 0 | |
279 | ? &attrp->__sp : NULL) == -1) | |
280 | _exit (SPAWN_ERROR); | |
281 | } | |
282 | #endif | |
283 | ||
284 | /* Set the process group ID. */ | |
285 | if (!err && (flags & POSIX_SPAWN_SETPGROUP) != 0) | |
286 | err = __proc_setpgrp (proc, new_pid, attrp->__pgrp); | |
287 | ||
288 | /* Set the effective user and group IDs. */ | |
289 | if (!err && (flags & POSIX_SPAWN_RESETIDS) != 0) | |
290 | { | |
291 | /* We need a different auth port for the child. */ | |
292 | ||
293 | __mutex_lock (&_hurd_id.lock); | |
294 | err = _hurd_check_ids (); /* Get _hurd_id up to date. */ | |
295 | if (!err && _hurd_id.rid_auth == MACH_PORT_NULL) | |
296 | { | |
297 | /* Set up _hurd_id.rid_auth. This is a special auth server port | |
298 | which uses the real uid and gid (the first aux uid and gid) as | |
299 | the only effective uid and gid. */ | |
300 | ||
301 | if (_hurd_id.aux.nuids < 1 || _hurd_id.aux.ngids < 1) | |
302 | /* We do not have a real UID and GID. Lose, lose, lose! */ | |
303 | err = EGRATUITOUS; | |
304 | ||
305 | /* Create a new auth port using our real UID and GID (the first | |
306 | auxiliary UID and GID) as the only effective IDs. */ | |
307 | if (!err) | |
308 | err = __USEPORT (AUTH, | |
309 | __auth_makeauth (port, | |
310 | NULL, MACH_MSG_TYPE_COPY_SEND, 0, | |
311 | _hurd_id.aux.uids, 1, | |
312 | _hurd_id.aux.uids, | |
313 | _hurd_id.aux.nuids, | |
314 | _hurd_id.aux.gids, 1, | |
315 | _hurd_id.aux.gids, | |
316 | _hurd_id.aux.ngids, | |
317 | &_hurd_id.rid_auth)); | |
318 | } | |
319 | if (!err) | |
320 | { | |
321 | /* Use the real-ID auth port in place of the normal one. */ | |
322 | assert (_hurd_id.rid_auth != MACH_PORT_NULL); | |
323 | auth = _hurd_id.rid_auth; | |
324 | __mach_port_mod_refs (__mach_task_self (), auth, | |
325 | MACH_PORT_RIGHT_SEND, +1); | |
326 | } | |
327 | __mutex_unlock (&_hurd_id.lock); | |
328 | } | |
329 | else | |
330 | /* Copy our existing auth port. */ | |
331 | err = __USEPORT (AUTH, __mach_port_mod_refs (__mach_task_self (), | |
332 | (auth = port), | |
333 | MACH_PORT_RIGHT_SEND, +1)); | |
334 | ||
335 | if (err) | |
336 | goto out; | |
337 | ||
338 | /* Pack up the descriptor table to give the new program. | |
339 | These descriptors will need to be reauthenticated below | |
340 | if POSIX_SPAWN_RESETIDS is set. */ | |
341 | __mutex_lock (&_hurd_dtable_lock); | |
342 | dtablesize = _hurd_dtablesize; | |
343 | orig_dtablesize = _hurd_dtablesize; | |
344 | dtable = __alloca (dtablesize * sizeof (dtable[0])); | |
345 | ulink_dtable = __alloca (dtablesize * sizeof (ulink_dtable[0])); | |
346 | dtable_cells = __alloca (dtablesize * sizeof (dtable_cells[0])); | |
347 | dtable_cloexec = __alloca (dtablesize); | |
348 | for (i = 0; i < dtablesize; ++i) | |
349 | { | |
350 | struct hurd_fd *const d = _hurd_dtable[i]; | |
351 | if (d == NULL) | |
352 | { | |
353 | dtable[i] = MACH_PORT_NULL; | |
354 | dtable_cells[i] = NULL; | |
355 | continue; | |
356 | } | |
357 | /* Note that this might return MACH_PORT_NULL. */ | |
358 | dtable[i] = _hurd_port_get (&d->port, &ulink_dtable[i]); | |
359 | dtable_cells[i] = &d->port; | |
360 | dtable_cloexec[i] = (d->flags & FD_CLOEXEC) != 0; | |
361 | } | |
362 | __mutex_unlock (&_hurd_dtable_lock); | |
363 | ||
364 | /* Safe to let signals happen now. */ | |
365 | _hurd_critical_section_unlock (ss); | |
366 | ||
367 | /* Execute the file actions. */ | |
368 | if (file_actions != NULL) | |
369 | for (i = 0; i < file_actions->__used; ++i) | |
370 | { | |
371 | /* Close a file descriptor in the child. */ | |
372 | error_t do_close (int fd) | |
373 | { | |
374 | if ((unsigned int)fd < dtablesize | |
375 | && dtable[fd] != MACH_PORT_NULL) | |
376 | { | |
377 | if (dtable_cells[fd] == NULL) | |
378 | __mach_port_deallocate (__mach_task_self (), dtable[fd]); | |
379 | else | |
380 | { | |
381 | _hurd_port_free (dtable_cells[fd], | |
382 | &ulink_dtable[fd], dtable[fd]); | |
383 | } | |
384 | dtable_cells[fd] = NULL; | |
385 | dtable[fd] = MACH_PORT_NULL; | |
386 | return 0; | |
387 | } | |
388 | return EBADF; | |
389 | } | |
390 | ||
391 | /* Make sure the dtable can hold NEWFD. */ | |
392 | #define EXPAND_DTABLE(newfd) \ | |
d96de963 | 393 | ({ \ |
03aae005 RM |
394 | if ((unsigned int)newfd >= dtablesize \ |
395 | && newfd < _hurd_rlimits[RLIMIT_OFILE].rlim_cur) \ | |
396 | { \ | |
397 | /* We need to expand the dtable for the child. */ \ | |
398 | NEW_TABLE (dtable, newfd); \ | |
399 | NEW_TABLE (ulink_dtable, newfd); \ | |
400 | NEW_TABLE (dtable_cells, newfd); \ | |
d96de963 | 401 | dtablesize = newfd + 1; \ |
03aae005 RM |
402 | } \ |
403 | ((unsigned int)newfd < dtablesize ? 0 : EMFILE); \ | |
404 | }) | |
405 | #define NEW_TABLE(x, newfd) \ | |
406 | do { __typeof (x) new_##x = __alloca ((newfd + 1) * sizeof (x[0])); \ | |
407 | memcpy (new_##x, x, dtablesize * sizeof (x[0])); \ | |
408 | memset (&new_##x[dtablesize], 0, (newfd + 1 - dtablesize) * sizeof (x[0])); \ | |
409 | x = new_##x; } while (0) | |
410 | ||
411 | struct __spawn_action *action = &file_actions->__actions[i]; | |
412 | ||
413 | switch (action->tag) | |
414 | { | |
415 | case spawn_do_close: | |
416 | err = do_close (action->action.close_action.fd); | |
417 | break; | |
418 | ||
419 | case spawn_do_dup2: | |
420 | if ((unsigned int)action->action.dup2_action.fd < dtablesize | |
421 | && dtable[action->action.dup2_action.fd] != MACH_PORT_NULL) | |
422 | { | |
423 | const int fd = action->action.dup2_action.fd; | |
424 | const int newfd = action->action.dup2_action.newfd; | |
425 | // dup2 always clears any old FD_CLOEXEC flag on the new fd. | |
426 | if (newfd < orig_dtablesize) | |
427 | dtable_cloexec[newfd] = 0; | |
428 | if (fd == newfd) | |
429 | // Same is same as same was. | |
430 | break; | |
431 | err = EXPAND_DTABLE (newfd); | |
432 | if (!err) | |
433 | { | |
434 | /* Close the old NEWFD and replace it with FD's | |
435 | contents, which can be either an original | |
436 | descriptor (DTABLE_CELLS[FD] != 0) or a new | |
437 | right that we acquired in this function. */ | |
438 | do_close (newfd); | |
439 | dtable_cells[newfd] = dtable_cells[fd]; | |
440 | if (dtable_cells[newfd] != NULL) | |
441 | dtable[newfd] = _hurd_port_get (dtable_cells[newfd], | |
442 | &ulink_dtable[newfd]); | |
443 | else | |
444 | { | |
445 | dtable[newfd] = dtable[fd]; | |
446 | err = __mach_port_mod_refs (__mach_task_self (), | |
447 | dtable[fd], | |
448 | MACH_PORT_RIGHT_SEND, +1); | |
449 | } | |
450 | } | |
451 | } | |
452 | else | |
453 | // The old FD specified was bogus. | |
454 | err = EBADF; | |
455 | break; | |
456 | ||
457 | case spawn_do_open: | |
458 | /* Open a file on behalf of the child. | |
459 | ||
460 | XXX note that this can subject the parent to arbitrary | |
461 | delays waiting for the files to open. I don't know what the | |
462 | spec says about this. If it's not permissible, then this | |
463 | whole forkless implementation is probably untenable. */ | |
464 | { | |
465 | const int fd = action->action.open_action.fd; | |
466 | ||
467 | do_close (fd); | |
468 | if (fd < orig_dtablesize) | |
469 | dtable_cloexec[fd] = 0; | |
470 | err = EXPAND_DTABLE (fd); | |
471 | if (err) | |
472 | break; | |
473 | ||
474 | err = child_lookup (action->action.open_action.path, | |
475 | action->action.open_action.oflag, | |
476 | action->action.open_action.mode, | |
477 | &dtable[fd]); | |
478 | dtable_cells[fd] = NULL; | |
479 | break; | |
480 | } | |
481 | } | |
482 | ||
483 | if (err) | |
484 | goto out; | |
485 | } | |
486 | ||
487 | /* Only now can we perform FD_CLOEXEC. We had to leave the descriptors | |
488 | unmolested for the file actions to use. Note that the DTABLE_CLOEXEC | |
489 | array is never expanded by file actions, so it might now have fewer | |
490 | than DTABLESIZE elements. */ | |
491 | for (i = 0; i < orig_dtablesize; ++i) | |
492 | if (dtable[i] != MACH_PORT_NULL && dtable_cloexec[i]) | |
493 | { | |
494 | assert (dtable_cells[i] != NULL); | |
495 | _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]); | |
496 | dtable[i] = MACH_PORT_NULL; | |
497 | } | |
498 | ||
499 | /* Prune trailing null ports from the descriptor table. */ | |
500 | while (dtablesize > 0 && dtable[dtablesize - 1] == MACH_PORT_NULL) | |
501 | --dtablesize; | |
502 | ||
503 | if (flags & POSIX_SPAWN_RESETIDS) | |
504 | { | |
505 | /* Reauthenticate all the child's ports with its new auth handle. */ | |
506 | ||
507 | mach_port_t ref; | |
508 | process_t newproc; | |
509 | ||
510 | /* Reauthenticate with the proc server. */ | |
511 | ref = __mach_reply_port (); | |
512 | err = __proc_reauthenticate (proc, ref, MACH_MSG_TYPE_MAKE_SEND); | |
513 | if (!err) | |
514 | err = __auth_user_authenticate (auth, | |
515 | ref, MACH_MSG_TYPE_MAKE_SEND, | |
516 | &newproc); | |
517 | __mach_port_destroy (__mach_task_self (), ref); | |
518 | if (!err) | |
519 | { | |
520 | __mach_port_deallocate (__mach_task_self (), proc); | |
521 | proc = newproc; | |
522 | } | |
523 | ||
524 | if (!err) | |
525 | err = reauthenticate (INIT_PORT_CRDIR, &rcrdir); | |
526 | if (!err) | |
527 | err = reauthenticate (INIT_PORT_CWDIR, &rcwdir); | |
528 | ||
529 | /* We must reauthenticate all the fds except those that came from | |
530 | `spawn_do_open' file actions, which were opened using the child's | |
531 | auth port to begin with. */ | |
532 | for (i = 0; !err && i < dtablesize; ++i) | |
533 | err = reauthenticate_fd (i); | |
534 | } | |
535 | if (err) | |
536 | goto out; | |
537 | ||
538 | /* Now we are ready to open the executable file using the child's ports. | |
539 | We do this after performing all the file actions so the order of | |
540 | events is the same as for a fork, exec sequence. This affects things | |
541 | like the meaning of a /dev/fd file name, as well as which error | |
542 | conditions are diagnosed first and what side effects (file creation, | |
543 | etc) can be observed before what errors. */ | |
544 | ||
d96de963 | 545 | if ((xflags & SPAWN_XFLAGS_USE_PATH) == 0 || strchr (file, '/') != NULL) |
03aae005 RM |
546 | /* The FILE parameter is actually a path. */ |
547 | err = child_lookup (file, O_EXEC, 0, &execfile); | |
548 | else | |
549 | { | |
550 | /* We have to search for FILE on the path. */ | |
551 | path = getenv ("PATH"); | |
552 | if (path == NULL) | |
553 | { | |
554 | /* There is no `PATH' in the environment. | |
555 | The default search path is the current directory | |
556 | followed by the path `confstr' returns for `_CS_PATH'. */ | |
557 | len = confstr (_CS_PATH, (char *) NULL, 0); | |
558 | path = (char *) __alloca (1 + len); | |
559 | path[0] = ':'; | |
560 | (void) confstr (_CS_PATH, path + 1, len); | |
561 | } | |
562 | ||
563 | len = strlen (file) + 1; | |
564 | pathlen = strlen (path); | |
565 | name = __alloca (pathlen + len + 1); | |
566 | /* Copy the file name at the top. */ | |
567 | name = (char *) memcpy (name + pathlen + 1, file, len); | |
568 | /* And add the slash. */ | |
569 | *--name = '/'; | |
570 | ||
571 | p = path; | |
572 | do | |
573 | { | |
574 | char *startp; | |
575 | ||
576 | path = p; | |
577 | p = __strchrnul (path, ':'); | |
578 | ||
579 | if (p == path) | |
580 | /* Two adjacent colons, or a colon at the beginning or the end | |
581 | of `PATH' means to search the current directory. */ | |
582 | startp = name + 1; | |
583 | else | |
584 | startp = (char *) memcpy (name - (p - path), path, p - path); | |
585 | ||
586 | /* Try to open this file name. */ | |
587 | err = child_lookup (startp, O_EXEC, 0, &execfile); | |
588 | switch (err) | |
589 | { | |
590 | case EACCES: | |
591 | case ENOENT: | |
592 | case ESTALE: | |
593 | case ENOTDIR: | |
594 | /* Those errors indicate the file is missing or not executable | |
fbf86dda | 595 | by us, in which case we want to just try the next path |
03aae005 RM |
596 | directory. */ |
597 | continue; | |
598 | ||
599 | case 0: /* Success! */ | |
600 | default: | |
601 | /* Some other error means we found an executable file, but | |
602 | something went wrong executing it; return the error to our | |
603 | caller. */ | |
604 | break; | |
605 | } | |
606 | ||
607 | // We only get here when we are done looking for the file. | |
608 | break; | |
609 | } | |
610 | while (*p++ != '\0'); | |
611 | } | |
612 | if (err) | |
613 | goto out; | |
614 | ||
615 | /* Almost there! */ | |
616 | { | |
617 | mach_port_t ports[_hurd_nports]; | |
618 | struct hurd_userlink ulink_ports[_hurd_nports]; | |
619 | char *args = NULL, *env = NULL; | |
620 | size_t argslen = 0, envlen = 0; | |
621 | ||
622 | inline error_t exec (file_t file) | |
623 | { | |
624 | return __file_exec (file, task, | |
625 | (__sigismember (&_hurdsig_traced, SIGKILL) | |
626 | ? EXEC_SIGTRAP : 0), | |
627 | args, argslen, env, envlen, | |
628 | dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize, | |
629 | ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports, | |
630 | ints, INIT_INT_MAX, | |
631 | NULL, 0, NULL, 0); | |
632 | } | |
633 | ||
634 | /* Now we are out of things that can fail before the file_exec RPC, | |
635 | for which everything else must be prepared. The only thing left | |
636 | to do is packing up the argument and environment strings, | |
637 | and the array of init ports. */ | |
638 | ||
639 | if (argv != NULL) | |
640 | err = __argz_create (argv, &args, &argslen); | |
641 | if (!err && envp != NULL) | |
642 | err = __argz_create (envp, &env, &envlen); | |
643 | ||
644 | /* Load up the ports to give to the new program. | |
645 | Note the loop/switch below must parallel exactly to release refs. */ | |
646 | for (i = 0; i < _hurd_nports; ++i) | |
647 | { | |
648 | switch (i) | |
649 | { | |
650 | case INIT_PORT_AUTH: | |
651 | ports[i] = auth; | |
652 | continue; | |
653 | case INIT_PORT_PROC: | |
654 | ports[i] = proc; | |
655 | continue; | |
656 | case INIT_PORT_CRDIR: | |
657 | if (flags & POSIX_SPAWN_RESETIDS) | |
658 | { | |
659 | ports[i] = rcrdir; | |
660 | continue; | |
661 | } | |
662 | break; | |
663 | case INIT_PORT_CWDIR: | |
664 | if (flags & POSIX_SPAWN_RESETIDS) | |
665 | { | |
666 | ports[i] = rcwdir; | |
667 | continue; | |
668 | } | |
669 | break; | |
670 | } | |
671 | ports[i] = _hurd_port_get (&_hurd_ports[i], &ulink_ports[i]); | |
672 | } | |
673 | ||
674 | /* Finally, try executing the file we opened. */ | |
675 | if (!err) | |
676 | err = exec (execfile); | |
677 | __mach_port_deallocate (__mach_task_self (), execfile); | |
678 | ||
679 | if (err == ENOEXEC) | |
680 | { | |
681 | /* The file is accessible but it is not an executable file. | |
682 | Invoke the shell to interpret it as a script. */ | |
683 | err = __argz_insert (&args, &argslen, args, _PATH_BSHELL); | |
684 | if (!err) | |
685 | err = child_lookup (_PATH_BSHELL, O_EXEC, 0, &execfile); | |
686 | if (!err) | |
687 | { | |
688 | err = exec (execfile); | |
689 | __mach_port_deallocate (__mach_task_self (), execfile); | |
690 | } | |
691 | } | |
692 | ||
693 | /* Release the references just packed up in PORTS. | |
694 | This switch must always parallel the one above that fills PORTS. */ | |
695 | for (i = 0; i < _hurd_nports; ++i) | |
696 | { | |
697 | switch (i) | |
698 | { | |
699 | case INIT_PORT_AUTH: | |
700 | case INIT_PORT_PROC: | |
701 | continue; | |
702 | case INIT_PORT_CRDIR: | |
703 | if (flags & POSIX_SPAWN_RESETIDS) | |
704 | continue; | |
705 | break; | |
706 | case INIT_PORT_CWDIR: | |
707 | if (flags & POSIX_SPAWN_RESETIDS) | |
708 | continue; | |
709 | break; | |
710 | } | |
711 | _hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]); | |
712 | } | |
713 | ||
714 | free (args); | |
715 | free (env); | |
716 | } | |
717 | ||
718 | /* We did it! We have a child! */ | |
719 | if (pid != NULL) | |
720 | *pid = new_pid; | |
721 | ||
722 | out: | |
723 | /* Clean up all the references we are now holding. */ | |
724 | ||
725 | if (task != MACH_PORT_NULL) | |
726 | { | |
727 | if (err) | |
728 | /* We failed after creating the task, so kill it. */ | |
729 | __task_terminate (task); | |
730 | __mach_port_deallocate (__mach_task_self (), task); | |
731 | } | |
732 | __mach_port_deallocate (__mach_task_self (), auth); | |
733 | __mach_port_deallocate (__mach_task_self (), proc); | |
734 | if (rcrdir != MACH_PORT_NULL) | |
735 | __mach_port_deallocate (__mach_task_self (), rcrdir); | |
736 | if (rcwdir != MACH_PORT_NULL) | |
737 | __mach_port_deallocate (__mach_task_self (), rcwdir); | |
738 | ||
739 | if (ulink_dtable) | |
740 | /* Release references to the file descriptor ports. */ | |
741 | for (i = 0; i < dtablesize; ++i) | |
742 | if (dtable[i] != MACH_PORT_NULL) | |
743 | { | |
744 | if (dtable_cells[i] == NULL) | |
745 | __mach_port_deallocate (__mach_task_self (), dtable[i]); | |
746 | else | |
747 | _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]); | |
748 | } | |
749 | ||
750 | if (err) | |
751 | /* This hack canonicalizes the error code that we return. */ | |
752 | err = (__hurd_fail (err), errno); | |
753 | ||
754 | return err; | |
755 | } |