]>
Commit | Line | Data |
---|---|---|
03aae005 | 1 | /* spawn a new process running an executable. Hurd version. |
2b778ceb | 2 | Copyright (C) 2001-2021 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 | 16 | License along with the GNU C Library; see the file COPYING.LIB. If |
5a82c748 | 17 | not, see <https://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 | { | |
a04549c1 JM |
267 | return (which == INIT_PORT_CWDIR ? (*operate) (startdir) |
268 | : child_init_port (which, operate)); | |
7c857b6f ST |
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 | ||
653d74f1 | 339 | _hurd_sigstate_lock (ss); |
03aae005 | 340 | ints[INIT_SIGMASK] = ss->blocked; |
653d74f1 | 341 | ints[INIT_SIGPENDING] = 0; |
03aae005 RM |
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. */ | |
653d74f1 JK |
345 | { |
346 | struct sigaction *actions = _hurd_sigstate_actions (ss); | |
347 | if ((flags & POSIX_SPAWN_SETSIGDEF) == 0) | |
348 | for (i = 1; i < NSIG; ++i) | |
349 | if (actions[i].sa_handler == SIG_IGN) | |
350 | ints[INIT_SIGIGN] |= __sigmask (i); | |
351 | } | |
352 | ||
353 | /* We hold the critical section lock until the exec has failed so that no | |
354 | signal can arrive between when we pack the blocked and ignored signals, | |
355 | and when the exec actually happens. A signal handler could change what | |
03aae005 RM |
356 | signals are blocked and ignored. Either the change will be reflected |
357 | in the exec, or the signal will never be delivered. Setting the | |
358 | critical section flag avoids anything we call trying to acquire the | |
359 | sigstate lock. */ | |
360 | ||
653d74f1 | 361 | _hurd_sigstate_unlock (ss); |
03aae005 RM |
362 | |
363 | /* Set signal mask. */ | |
364 | if ((flags & POSIX_SPAWN_SETSIGMASK) != 0) | |
365 | ints[INIT_SIGMASK] = attrp->__ss; | |
366 | ||
367 | #ifdef _POSIX_PRIORITY_SCHEDULING | |
368 | /* Set the scheduling algorithm and parameters. */ | |
369 | # error implement me | |
370 | if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) | |
371 | == POSIX_SPAWN_SETSCHEDPARAM) | |
372 | { | |
373 | if (__sched_setparam (0, &attrp->__sp) == -1) | |
374 | _exit (SPAWN_ERROR); | |
375 | } | |
376 | else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0) | |
377 | { | |
378 | if (__sched_setscheduler (0, attrp->__policy, | |
379 | (flags & POSIX_SPAWN_SETSCHEDPARAM) != 0 | |
380 | ? &attrp->__sp : NULL) == -1) | |
381 | _exit (SPAWN_ERROR); | |
382 | } | |
383 | #endif | |
384 | ||
daeb1fa2 AZ |
385 | if (!err && (flags & POSIX_SPAWN_SETSID) != 0) |
386 | err = __proc_setsid (proc); | |
387 | ||
03aae005 RM |
388 | /* Set the process group ID. */ |
389 | if (!err && (flags & POSIX_SPAWN_SETPGROUP) != 0) | |
390 | err = __proc_setpgrp (proc, new_pid, attrp->__pgrp); | |
391 | ||
392 | /* Set the effective user and group IDs. */ | |
393 | if (!err && (flags & POSIX_SPAWN_RESETIDS) != 0) | |
394 | { | |
395 | /* We need a different auth port for the child. */ | |
396 | ||
397 | __mutex_lock (&_hurd_id.lock); | |
398 | err = _hurd_check_ids (); /* Get _hurd_id up to date. */ | |
399 | if (!err && _hurd_id.rid_auth == MACH_PORT_NULL) | |
400 | { | |
401 | /* Set up _hurd_id.rid_auth. This is a special auth server port | |
402 | which uses the real uid and gid (the first aux uid and gid) as | |
403 | the only effective uid and gid. */ | |
404 | ||
405 | if (_hurd_id.aux.nuids < 1 || _hurd_id.aux.ngids < 1) | |
406 | /* We do not have a real UID and GID. Lose, lose, lose! */ | |
407 | err = EGRATUITOUS; | |
408 | ||
409 | /* Create a new auth port using our real UID and GID (the first | |
410 | auxiliary UID and GID) as the only effective IDs. */ | |
411 | if (!err) | |
412 | err = __USEPORT (AUTH, | |
413 | __auth_makeauth (port, | |
414 | NULL, MACH_MSG_TYPE_COPY_SEND, 0, | |
415 | _hurd_id.aux.uids, 1, | |
416 | _hurd_id.aux.uids, | |
417 | _hurd_id.aux.nuids, | |
418 | _hurd_id.aux.gids, 1, | |
419 | _hurd_id.aux.gids, | |
420 | _hurd_id.aux.ngids, | |
421 | &_hurd_id.rid_auth)); | |
422 | } | |
423 | if (!err) | |
424 | { | |
425 | /* Use the real-ID auth port in place of the normal one. */ | |
426 | assert (_hurd_id.rid_auth != MACH_PORT_NULL); | |
427 | auth = _hurd_id.rid_auth; | |
428 | __mach_port_mod_refs (__mach_task_self (), auth, | |
429 | MACH_PORT_RIGHT_SEND, +1); | |
430 | } | |
431 | __mutex_unlock (&_hurd_id.lock); | |
432 | } | |
433 | else | |
434 | /* Copy our existing auth port. */ | |
435 | err = __USEPORT (AUTH, __mach_port_mod_refs (__mach_task_self (), | |
436 | (auth = port), | |
437 | MACH_PORT_RIGHT_SEND, +1)); | |
438 | ||
439 | if (err) | |
440 | goto out; | |
441 | ||
442 | /* Pack up the descriptor table to give the new program. | |
443 | These descriptors will need to be reauthenticated below | |
444 | if POSIX_SPAWN_RESETIDS is set. */ | |
445 | __mutex_lock (&_hurd_dtable_lock); | |
446 | dtablesize = _hurd_dtablesize; | |
447 | orig_dtablesize = _hurd_dtablesize; | |
448 | dtable = __alloca (dtablesize * sizeof (dtable[0])); | |
449 | ulink_dtable = __alloca (dtablesize * sizeof (ulink_dtable[0])); | |
450 | dtable_cells = __alloca (dtablesize * sizeof (dtable_cells[0])); | |
58bb655a | 451 | dtable_cloexec = __alloca (orig_dtablesize); |
03aae005 RM |
452 | for (i = 0; i < dtablesize; ++i) |
453 | { | |
454 | struct hurd_fd *const d = _hurd_dtable[i]; | |
455 | if (d == NULL) | |
456 | { | |
457 | dtable[i] = MACH_PORT_NULL; | |
458 | dtable_cells[i] = NULL; | |
459 | continue; | |
460 | } | |
461 | /* Note that this might return MACH_PORT_NULL. */ | |
462 | dtable[i] = _hurd_port_get (&d->port, &ulink_dtable[i]); | |
463 | dtable_cells[i] = &d->port; | |
464 | dtable_cloexec[i] = (d->flags & FD_CLOEXEC) != 0; | |
465 | } | |
466 | __mutex_unlock (&_hurd_dtable_lock); | |
467 | ||
468 | /* Safe to let signals happen now. */ | |
469 | _hurd_critical_section_unlock (ss); | |
470 | ||
471 | /* Execute the file actions. */ | |
472 | if (file_actions != NULL) | |
473 | for (i = 0; i < file_actions->__used; ++i) | |
474 | { | |
475 | /* Close a file descriptor in the child. */ | |
476 | error_t do_close (int fd) | |
477 | { | |
478 | if ((unsigned int)fd < dtablesize | |
479 | && dtable[fd] != MACH_PORT_NULL) | |
480 | { | |
481 | if (dtable_cells[fd] == NULL) | |
482 | __mach_port_deallocate (__mach_task_self (), dtable[fd]); | |
483 | else | |
484 | { | |
485 | _hurd_port_free (dtable_cells[fd], | |
486 | &ulink_dtable[fd], dtable[fd]); | |
487 | } | |
488 | dtable_cells[fd] = NULL; | |
489 | dtable[fd] = MACH_PORT_NULL; | |
490 | return 0; | |
491 | } | |
492 | return EBADF; | |
493 | } | |
494 | ||
495 | /* Make sure the dtable can hold NEWFD. */ | |
496 | #define EXPAND_DTABLE(newfd) \ | |
d96de963 | 497 | ({ \ |
03aae005 RM |
498 | if ((unsigned int)newfd >= dtablesize \ |
499 | && newfd < _hurd_rlimits[RLIMIT_OFILE].rlim_cur) \ | |
500 | { \ | |
501 | /* We need to expand the dtable for the child. */ \ | |
502 | NEW_TABLE (dtable, newfd); \ | |
7fa495cd | 503 | NEW_ULINK_TABLE (ulink_dtable, newfd); \ |
03aae005 | 504 | NEW_TABLE (dtable_cells, newfd); \ |
d96de963 | 505 | dtablesize = newfd + 1; \ |
03aae005 RM |
506 | } \ |
507 | ((unsigned int)newfd < dtablesize ? 0 : EMFILE); \ | |
508 | }) | |
509 | #define NEW_TABLE(x, newfd) \ | |
510 | do { __typeof (x) new_##x = __alloca ((newfd + 1) * sizeof (x[0])); \ | |
511 | memcpy (new_##x, x, dtablesize * sizeof (x[0])); \ | |
7fa495cd ST |
512 | memset (&new_##x[dtablesize], 0, (newfd + 1 - dtablesize) * sizeof (x[0])); \ |
513 | x = new_##x; } while (0) | |
514 | #define NEW_ULINK_TABLE(x, newfd) \ | |
515 | do { __typeof (x) new_##x = __alloca ((newfd + 1) * sizeof (x[0])); \ | |
516 | unsigned i; \ | |
517 | for (i = 0; i < dtablesize; i++) \ | |
518 | if (dtable_cells[i] != NULL) \ | |
519 | _hurd_port_move (dtable_cells[i], &new_##x[i], &x[i]); \ | |
520 | else \ | |
c4f50205 | 521 | memset (&new_##x[i], 0, sizeof (new_##x[i])); \ |
03aae005 RM |
522 | memset (&new_##x[dtablesize], 0, (newfd + 1 - dtablesize) * sizeof (x[0])); \ |
523 | x = new_##x; } while (0) | |
524 | ||
525 | struct __spawn_action *action = &file_actions->__actions[i]; | |
526 | ||
527 | switch (action->tag) | |
528 | { | |
529 | case spawn_do_close: | |
530 | err = do_close (action->action.close_action.fd); | |
531 | break; | |
532 | ||
533 | case spawn_do_dup2: | |
534 | if ((unsigned int)action->action.dup2_action.fd < dtablesize | |
535 | && dtable[action->action.dup2_action.fd] != MACH_PORT_NULL) | |
536 | { | |
537 | const int fd = action->action.dup2_action.fd; | |
538 | const int newfd = action->action.dup2_action.newfd; | |
539 | // dup2 always clears any old FD_CLOEXEC flag on the new fd. | |
540 | if (newfd < orig_dtablesize) | |
541 | dtable_cloexec[newfd] = 0; | |
542 | if (fd == newfd) | |
543 | // Same is same as same was. | |
544 | break; | |
545 | err = EXPAND_DTABLE (newfd); | |
546 | if (!err) | |
547 | { | |
548 | /* Close the old NEWFD and replace it with FD's | |
549 | contents, which can be either an original | |
550 | descriptor (DTABLE_CELLS[FD] != 0) or a new | |
551 | right that we acquired in this function. */ | |
552 | do_close (newfd); | |
553 | dtable_cells[newfd] = dtable_cells[fd]; | |
554 | if (dtable_cells[newfd] != NULL) | |
555 | dtable[newfd] = _hurd_port_get (dtable_cells[newfd], | |
556 | &ulink_dtable[newfd]); | |
557 | else | |
558 | { | |
559 | dtable[newfd] = dtable[fd]; | |
560 | err = __mach_port_mod_refs (__mach_task_self (), | |
561 | dtable[fd], | |
562 | MACH_PORT_RIGHT_SEND, +1); | |
563 | } | |
564 | } | |
565 | } | |
566 | else | |
567 | // The old FD specified was bogus. | |
568 | err = EBADF; | |
569 | break; | |
570 | ||
571 | case spawn_do_open: | |
572 | /* Open a file on behalf of the child. | |
573 | ||
574 | XXX note that this can subject the parent to arbitrary | |
575 | delays waiting for the files to open. I don't know what the | |
576 | spec says about this. If it's not permissible, then this | |
577 | whole forkless implementation is probably untenable. */ | |
578 | { | |
579 | const int fd = action->action.open_action.fd; | |
580 | ||
581 | do_close (fd); | |
582 | if (fd < orig_dtablesize) | |
583 | dtable_cloexec[fd] = 0; | |
584 | err = EXPAND_DTABLE (fd); | |
585 | if (err) | |
586 | break; | |
587 | ||
588 | err = child_lookup (action->action.open_action.path, | |
589 | action->action.open_action.oflag, | |
590 | action->action.open_action.mode, | |
591 | &dtable[fd]); | |
592 | dtable_cells[fd] = NULL; | |
593 | break; | |
594 | } | |
434c34bd ST |
595 | |
596 | case spawn_do_chdir: | |
597 | err = child_chdir (action->action.chdir_action.path); | |
598 | break; | |
7c857b6f ST |
599 | |
600 | case spawn_do_fchdir: | |
601 | err = child_fchdir (action->action.fchdir_action.fd); | |
602 | break; | |
03aae005 RM |
603 | } |
604 | ||
605 | if (err) | |
606 | goto out; | |
607 | } | |
608 | ||
609 | /* Only now can we perform FD_CLOEXEC. We had to leave the descriptors | |
610 | unmolested for the file actions to use. Note that the DTABLE_CLOEXEC | |
611 | array is never expanded by file actions, so it might now have fewer | |
612 | than DTABLESIZE elements. */ | |
613 | for (i = 0; i < orig_dtablesize; ++i) | |
614 | if (dtable[i] != MACH_PORT_NULL && dtable_cloexec[i]) | |
615 | { | |
616 | assert (dtable_cells[i] != NULL); | |
617 | _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]); | |
618 | dtable[i] = MACH_PORT_NULL; | |
619 | } | |
620 | ||
621 | /* Prune trailing null ports from the descriptor table. */ | |
622 | while (dtablesize > 0 && dtable[dtablesize - 1] == MACH_PORT_NULL) | |
623 | --dtablesize; | |
624 | ||
625 | if (flags & POSIX_SPAWN_RESETIDS) | |
626 | { | |
627 | /* Reauthenticate all the child's ports with its new auth handle. */ | |
628 | ||
629 | mach_port_t ref; | |
630 | process_t newproc; | |
631 | ||
632 | /* Reauthenticate with the proc server. */ | |
633 | ref = __mach_reply_port (); | |
634 | err = __proc_reauthenticate (proc, ref, MACH_MSG_TYPE_MAKE_SEND); | |
635 | if (!err) | |
636 | err = __auth_user_authenticate (auth, | |
637 | ref, MACH_MSG_TYPE_MAKE_SEND, | |
638 | &newproc); | |
639 | __mach_port_destroy (__mach_task_self (), ref); | |
640 | if (!err) | |
641 | { | |
642 | __mach_port_deallocate (__mach_task_self (), proc); | |
643 | proc = newproc; | |
644 | } | |
645 | ||
646 | if (!err) | |
647 | err = reauthenticate (INIT_PORT_CRDIR, &rcrdir); | |
648 | if (!err) | |
649 | err = reauthenticate (INIT_PORT_CWDIR, &rcwdir); | |
650 | ||
651 | /* We must reauthenticate all the fds except those that came from | |
652 | `spawn_do_open' file actions, which were opened using the child's | |
653 | auth port to begin with. */ | |
654 | for (i = 0; !err && i < dtablesize; ++i) | |
655 | err = reauthenticate_fd (i); | |
656 | } | |
657 | if (err) | |
658 | goto out; | |
659 | ||
660 | /* Now we are ready to open the executable file using the child's ports. | |
661 | We do this after performing all the file actions so the order of | |
662 | events is the same as for a fork, exec sequence. This affects things | |
663 | like the meaning of a /dev/fd file name, as well as which error | |
664 | conditions are diagnosed first and what side effects (file creation, | |
665 | etc) can be observed before what errors. */ | |
666 | ||
d96de963 | 667 | if ((xflags & SPAWN_XFLAGS_USE_PATH) == 0 || strchr (file, '/') != NULL) |
03aae005 | 668 | /* The FILE parameter is actually a path. */ |
311ba8dc | 669 | err = child_lookup (relpath = file, O_EXEC, 0, &execfile); |
03aae005 RM |
670 | else |
671 | { | |
672 | /* We have to search for FILE on the path. */ | |
673 | path = getenv ("PATH"); | |
674 | if (path == NULL) | |
675 | { | |
676 | /* There is no `PATH' in the environment. | |
677 | The default search path is the current directory | |
678 | followed by the path `confstr' returns for `_CS_PATH'. */ | |
45f33aac | 679 | len = __confstr (_CS_PATH, (char *) NULL, 0); |
03aae005 RM |
680 | path = (char *) __alloca (1 + len); |
681 | path[0] = ':'; | |
45f33aac | 682 | (void) __confstr (_CS_PATH, path + 1, len); |
03aae005 RM |
683 | } |
684 | ||
685 | len = strlen (file) + 1; | |
686 | pathlen = strlen (path); | |
687 | name = __alloca (pathlen + len + 1); | |
688 | /* Copy the file name at the top. */ | |
689 | name = (char *) memcpy (name + pathlen + 1, file, len); | |
690 | /* And add the slash. */ | |
691 | *--name = '/'; | |
692 | ||
693 | p = path; | |
694 | do | |
695 | { | |
696 | char *startp; | |
697 | ||
698 | path = p; | |
699 | p = __strchrnul (path, ':'); | |
700 | ||
701 | if (p == path) | |
702 | /* Two adjacent colons, or a colon at the beginning or the end | |
703 | of `PATH' means to search the current directory. */ | |
704 | startp = name + 1; | |
705 | else | |
706 | startp = (char *) memcpy (name - (p - path), path, p - path); | |
707 | ||
708 | /* Try to open this file name. */ | |
709 | err = child_lookup (startp, O_EXEC, 0, &execfile); | |
710 | switch (err) | |
711 | { | |
712 | case EACCES: | |
713 | case ENOENT: | |
714 | case ESTALE: | |
715 | case ENOTDIR: | |
716 | /* Those errors indicate the file is missing or not executable | |
fbf86dda | 717 | by us, in which case we want to just try the next path |
03aae005 RM |
718 | directory. */ |
719 | continue; | |
720 | ||
721 | case 0: /* Success! */ | |
722 | default: | |
723 | /* Some other error means we found an executable file, but | |
724 | something went wrong executing it; return the error to our | |
725 | caller. */ | |
726 | break; | |
727 | } | |
728 | ||
729 | // We only get here when we are done looking for the file. | |
311ba8dc | 730 | relpath = startp; |
03aae005 RM |
731 | break; |
732 | } | |
733 | while (*p++ != '\0'); | |
734 | } | |
735 | if (err) | |
736 | goto out; | |
737 | ||
311ba8dc ST |
738 | if (relpath[0] == '/') |
739 | { | |
740 | /* Already an absolute path */ | |
741 | abspath = relpath; | |
742 | } | |
743 | else | |
744 | { | |
745 | /* Relative path */ | |
746 | char *cwd = __getcwd (NULL, 0); | |
747 | if (cwd == NULL) | |
748 | goto out; | |
749 | ||
750 | res = __asprintf (&concat_name, "%s/%s", cwd, relpath); | |
751 | free (cwd); | |
752 | if (res == -1) | |
753 | goto out; | |
754 | ||
755 | abspath = concat_name; | |
756 | } | |
757 | ||
03aae005 RM |
758 | /* Almost there! */ |
759 | { | |
760 | mach_port_t ports[_hurd_nports]; | |
761 | struct hurd_userlink ulink_ports[_hurd_nports]; | |
762 | char *args = NULL, *env = NULL; | |
763 | size_t argslen = 0, envlen = 0; | |
764 | ||
765 | inline error_t exec (file_t file) | |
766 | { | |
311ba8dc ST |
767 | error_t err = __file_exec_paths |
768 | (file, task, | |
769 | __sigismember (&_hurdsig_traced, SIGKILL) ? EXEC_SIGTRAP : 0, | |
770 | relpath, abspath, args, argslen, env, envlen, | |
771 | dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize, | |
772 | ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports, | |
773 | ints, INIT_INT_MAX, | |
774 | NULL, 0, NULL, 0); | |
775 | ||
776 | /* Fallback for backwards compatibility. This can just be removed | |
777 | when __file_exec goes away. */ | |
778 | if (err == MIG_BAD_ID) | |
779 | return __file_exec (file, task, | |
780 | (__sigismember (&_hurdsig_traced, SIGKILL) | |
781 | ? EXEC_SIGTRAP : 0), | |
782 | args, argslen, env, envlen, | |
783 | dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize, | |
784 | ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports, | |
785 | ints, INIT_INT_MAX, | |
786 | NULL, 0, NULL, 0); | |
787 | ||
788 | return err; | |
03aae005 RM |
789 | } |
790 | ||
791 | /* Now we are out of things that can fail before the file_exec RPC, | |
792 | for which everything else must be prepared. The only thing left | |
793 | to do is packing up the argument and environment strings, | |
794 | and the array of init ports. */ | |
795 | ||
796 | if (argv != NULL) | |
797 | err = __argz_create (argv, &args, &argslen); | |
798 | if (!err && envp != NULL) | |
799 | err = __argz_create (envp, &env, &envlen); | |
800 | ||
801 | /* Load up the ports to give to the new program. | |
802 | Note the loop/switch below must parallel exactly to release refs. */ | |
803 | for (i = 0; i < _hurd_nports; ++i) | |
804 | { | |
805 | switch (i) | |
806 | { | |
807 | case INIT_PORT_AUTH: | |
808 | ports[i] = auth; | |
809 | continue; | |
810 | case INIT_PORT_PROC: | |
811 | ports[i] = proc; | |
812 | continue; | |
813 | case INIT_PORT_CRDIR: | |
814 | if (flags & POSIX_SPAWN_RESETIDS) | |
815 | { | |
816 | ports[i] = rcrdir; | |
817 | continue; | |
818 | } | |
819 | break; | |
820 | case INIT_PORT_CWDIR: | |
821 | if (flags & POSIX_SPAWN_RESETIDS) | |
822 | { | |
823 | ports[i] = rcwdir; | |
824 | continue; | |
825 | } | |
434c34bd ST |
826 | if (ccwdir != MACH_PORT_NULL) |
827 | { | |
828 | ports[i] = ccwdir; | |
829 | continue; | |
830 | } | |
03aae005 RM |
831 | break; |
832 | } | |
833 | ports[i] = _hurd_port_get (&_hurd_ports[i], &ulink_ports[i]); | |
834 | } | |
835 | ||
836 | /* Finally, try executing the file we opened. */ | |
837 | if (!err) | |
838 | err = exec (execfile); | |
839 | __mach_port_deallocate (__mach_task_self (), execfile); | |
840 | ||
13adfa34 | 841 | if ((err == ENOEXEC) && (xflags & SPAWN_XFLAGS_TRY_SHELL) != 0) |
03aae005 RM |
842 | { |
843 | /* The file is accessible but it is not an executable file. | |
844 | Invoke the shell to interpret it as a script. */ | |
a39b95b9 ST |
845 | err = 0; |
846 | if (!argslen) | |
847 | err = __argz_insert (&args, &argslen, args, relpath); | |
848 | if (!err) | |
849 | err = __argz_insert (&args, &argslen, args, _PATH_BSHELL); | |
03aae005 RM |
850 | if (!err) |
851 | err = child_lookup (_PATH_BSHELL, O_EXEC, 0, &execfile); | |
852 | if (!err) | |
853 | { | |
854 | err = exec (execfile); | |
855 | __mach_port_deallocate (__mach_task_self (), execfile); | |
856 | } | |
857 | } | |
858 | ||
859 | /* Release the references just packed up in PORTS. | |
860 | This switch must always parallel the one above that fills PORTS. */ | |
861 | for (i = 0; i < _hurd_nports; ++i) | |
862 | { | |
863 | switch (i) | |
864 | { | |
865 | case INIT_PORT_AUTH: | |
866 | case INIT_PORT_PROC: | |
867 | continue; | |
868 | case INIT_PORT_CRDIR: | |
869 | if (flags & POSIX_SPAWN_RESETIDS) | |
870 | continue; | |
871 | break; | |
872 | case INIT_PORT_CWDIR: | |
873 | if (flags & POSIX_SPAWN_RESETIDS) | |
874 | continue; | |
434c34bd ST |
875 | if (ccwdir != MACH_PORT_NULL) |
876 | continue; | |
03aae005 RM |
877 | break; |
878 | } | |
879 | _hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]); | |
880 | } | |
881 | ||
882 | free (args); | |
883 | free (env); | |
884 | } | |
885 | ||
886 | /* We did it! We have a child! */ | |
887 | if (pid != NULL) | |
888 | *pid = new_pid; | |
889 | ||
890 | out: | |
891 | /* Clean up all the references we are now holding. */ | |
892 | ||
893 | if (task != MACH_PORT_NULL) | |
894 | { | |
895 | if (err) | |
896 | /* We failed after creating the task, so kill it. */ | |
897 | __task_terminate (task); | |
898 | __mach_port_deallocate (__mach_task_self (), task); | |
899 | } | |
900 | __mach_port_deallocate (__mach_task_self (), auth); | |
901 | __mach_port_deallocate (__mach_task_self (), proc); | |
434c34bd ST |
902 | if (ccwdir != MACH_PORT_NULL) |
903 | __mach_port_deallocate (__mach_task_self (), ccwdir); | |
03aae005 RM |
904 | if (rcrdir != MACH_PORT_NULL) |
905 | __mach_port_deallocate (__mach_task_self (), rcrdir); | |
906 | if (rcwdir != MACH_PORT_NULL) | |
907 | __mach_port_deallocate (__mach_task_self (), rcwdir); | |
908 | ||
909 | if (ulink_dtable) | |
910 | /* Release references to the file descriptor ports. */ | |
911 | for (i = 0; i < dtablesize; ++i) | |
912 | if (dtable[i] != MACH_PORT_NULL) | |
913 | { | |
914 | if (dtable_cells[i] == NULL) | |
915 | __mach_port_deallocate (__mach_task_self (), dtable[i]); | |
916 | else | |
917 | _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]); | |
918 | } | |
919 | ||
311ba8dc ST |
920 | free (concat_name); |
921 | ||
03aae005 RM |
922 | if (err) |
923 | /* This hack canonicalizes the error code that we return. */ | |
924 | err = (__hurd_fail (err), errno); | |
925 | ||
926 | return err; | |
927 | } |