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