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