3131#include <string.h>
3232#include <fcntl.h>
3333#include <pthread.h>
34-
34+ #include <dirent.h>
3535#include <stdio.h>
3636
37+ #if __has_include (< linux /close_range .h > )
38+ #include <linux/close_range.h>
39+ #endif
40+
41+ #endif // TARGET_OS_WINDOWS
42+
3743#if __has_include (< crt_externs .h > )
3844#include <crt_externs.h>
3945#elif defined(_WIN32)
@@ -364,93 +370,57 @@ static int _subprocess_addchdir_np(
364370#endif
365371}
366372
367- static int _subprocess_posix_spawn_fallback (
368- pid_t * _Nonnull pid ,
369- const char * _Nonnull exec_path ,
370- const char * _Nullable working_directory ,
371- const int file_descriptors [_Nonnull],
372- char * _Nullable const args [_Nonnull],
373- char * _Nullable const env [_Nullable ],
374- gid_t * _Nullable process_group_id
375- ) {
376- // Setup stdin, stdout, and stderr
377- posix_spawn_file_actions_t file_actions ;
378-
379- int rc = posix_spawn_file_actions_init (& file_actions );
380- if (rc != 0 ) { return rc ; }
381- if (file_descriptors [0 ] >= 0 ) {
382- rc = posix_spawn_file_actions_adddup2 (
383- & file_actions , file_descriptors [0 ], STDIN_FILENO
384- );
385- if (rc != 0 ) { return rc ; }
386- }
387- if (file_descriptors [2 ] >= 0 ) {
388- rc = posix_spawn_file_actions_adddup2 (
389- & file_actions , file_descriptors [2 ], STDOUT_FILENO
390- );
391- if (rc != 0 ) { return rc ; }
392- }
393- if (file_descriptors [4 ] >= 0 ) {
394- rc = posix_spawn_file_actions_adddup2 (
395- & file_actions , file_descriptors [4 ], STDERR_FILENO
396- );
397- if (rc != 0 ) { return rc ; }
398- }
399- // Setup working directory
400- if (working_directory != NULL ) {
401- rc = _subprocess_addchdir_np (& file_actions , working_directory );
402- if (rc != 0 ) {
403- return rc ;
373+ static int _positive_int_parse (const char * str ) {
374+ int out = 0 ;
375+ char c = 0 ;
376+
377+ while ((c = * str ++ ) != 0 ) {
378+ out *= 10 ;
379+ if (c >= '0' && c <= '9' ) {
380+ out += c - '0' ;
381+ } else {
382+ return -1 ;
404383 }
405384 }
385+ return out ;
386+ }
406387
407- // Close parent side
408- if (file_descriptors [1 ] >= 0 ) {
409- rc = posix_spawn_file_actions_addclose (& file_actions , file_descriptors [1 ]);
410- if (rc != 0 ) { return rc ; }
411- }
412- if (file_descriptors [3 ] >= 0 ) {
413- rc = posix_spawn_file_actions_addclose (& file_actions , file_descriptors [3 ]);
414- if (rc != 0 ) { return rc ; }
415- }
416- if (file_descriptors [5 ] >= 0 ) {
417- rc = posix_spawn_file_actions_addclose (& file_actions , file_descriptors [5 ]);
418- if (rc != 0 ) { return rc ; }
388+ static int _highest_possibly_open_fd_dir (const char * fd_dir ) {
389+ int highest_fd_so_far = 0 ;
390+ DIR * dir_ptr = opendir (fd_dir );
391+ if (dir_ptr == NULL ) {
392+ return -1 ;
419393 }
420394
421- // Setup spawnattr
422- posix_spawnattr_t spawn_attr ;
423- rc = posix_spawnattr_init (& spawn_attr );
424- if (rc != 0 ) { return rc ; }
425- // Masks
426- sigset_t no_signals ;
427- sigset_t all_signals ;
428- sigemptyset (& no_signals );
429- sigfillset (& all_signals );
430- rc = posix_spawnattr_setsigmask (& spawn_attr , & no_signals );
431- if (rc != 0 ) { return rc ; }
432- rc = posix_spawnattr_setsigdefault (& spawn_attr , & all_signals );
433- if (rc != 0 ) { return rc ; }
434- // Flags
435- short flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF ;
436- if (process_group_id != NULL ) {
437- flags |= POSIX_SPAWN_SETPGROUP ;
438- rc = posix_spawnattr_setpgroup (& spawn_attr , * process_group_id );
439- if (rc != 0 ) { return rc ; }
395+ struct dirent * dir_entry = NULL ;
396+ while ((dir_entry = readdir (dir_ptr )) != NULL ) {
397+ char * entry_name = dir_entry -> d_name ;
398+ int number = _positive_int_parse (entry_name );
399+ if (number > (long )highest_fd_so_far ) {
400+ highest_fd_so_far = number ;
401+ }
440402 }
441- rc = posix_spawnattr_setflags (& spawn_attr , flags );
442403
443- // Spawn!
444- rc = posix_spawn (
445- pid , exec_path ,
446- & file_actions , & spawn_attr ,
447- args , env
448- );
449- posix_spawn_file_actions_destroy (& file_actions );
450- posix_spawnattr_destroy (& spawn_attr );
451- return rc ;
404+ closedir (dir_ptr );
405+ return highest_fd_so_far ;
406+ }
407+
408+ static int _highest_possibly_open_fd (void ) {
409+ #if defined(__APPLE__ )
410+ int hi = _highest_possibly_open_fd_dir ("/dev/fd" );
411+ if (hi < 0 ) {
412+ hi = getdtablesize ();
413+ }
414+ #elif defined(__linux__ )
415+ int hi = _highest_possibly_open_fd_dir ("/proc/self/fd" );
416+ if (hi < 0 ) {
417+ hi = getdtablesize ();
418+ }
419+ #else
420+ int hi = 1024 ;
421+ #endif
422+ return hi ;
452423}
453- #endif // _POSIX_SPAWN
454424
455425int _subprocess_fork_exec (
456426 pid_t * _Nonnull pid ,
@@ -471,32 +441,6 @@ int _subprocess_fork_exec(
471441 close(pipefd[1]); \
472442 _exit(EXIT_FAILURE)
473443
474- int require_pre_fork = _subprocess_is_addchdir_np_available () == 0 ||
475- uid != NULL ||
476- gid != NULL ||
477- process_group_id != NULL ||
478- (number_of_sgroups > 0 && sgroups != NULL ) ||
479- create_session ||
480- configurator != NULL ;
481-
482- #if _POSIX_SPAWN
483- // If posix_spawn is available on this platform and
484- // we do not require prefork, use posix_spawn if possible.
485- //
486- // (Glibc's posix_spawn does not support
487- // `POSIX_SPAWN_SETEXEC` therefore we have to keep
488- // using fork/exec if `require_pre_fork` is true.
489- if (require_pre_fork == 0 ) {
490- return _subprocess_posix_spawn_fallback (
491- pid , exec_path ,
492- working_directory ,
493- file_descriptors ,
494- args , env ,
495- process_group_id
496- );
497- }
498- #endif
499-
500444 // Setup pipe to catch exec failures from child
501445 int pipefd [2 ];
502446 if (pipe (pipefd ) != 0 ) {
@@ -557,8 +501,6 @@ int _subprocess_fork_exec(
557501
558502 if (childPid == 0 ) {
559503 // Child process
560- close (pipefd [0 ]); // Close unused read end
561-
562504 // Reset signal handlers
563505 for (int signo = 1 ; signo < _SUBPROCESS_SIG_MAX ; signo ++ ) {
564506 if (signo == SIGKILL || signo == SIGSTOP ) {
@@ -620,40 +562,46 @@ int _subprocess_fork_exec(
620562 // Bind stdin, stdout, and stderr
621563 if (file_descriptors [0 ] >= 0 ) {
622564 rc = dup2 (file_descriptors [0 ], STDIN_FILENO );
623- if (rc < 0 ) {
624- write_error_and_exit ;
625- }
565+ } else {
566+ rc = close (STDIN_FILENO );
567+ }
568+ if (rc < 0 ) {
569+ write_error_and_exit ;
626570 }
571+
627572 if (file_descriptors [2 ] >= 0 ) {
628573 rc = dup2 (file_descriptors [2 ], STDOUT_FILENO );
629- if (rc < 0 ) {
630- write_error_and_exit ;
631- }
574+ } else {
575+ rc = close (STDOUT_FILENO );
632576 }
577+ if (rc < 0 ) {
578+ write_error_and_exit ;
579+ }
580+
633581 if (file_descriptors [4 ] >= 0 ) {
634582 rc = dup2 (file_descriptors [4 ], STDERR_FILENO );
635- if (rc < 0 ) {
636- int error = errno ;
637- write (pipefd [1 ], & error , sizeof (error ));
638- close (pipefd [1 ]);
639- _exit (EXIT_FAILURE );
640- }
641- }
642- // Close parent side
643- if (file_descriptors [1 ] >= 0 ) {
644- rc = close (file_descriptors [1 ]);
583+ } else {
584+ rc = close (STDERR_FILENO );
645585 }
646- if (file_descriptors [3 ] >= 0 ) {
647- rc = close (file_descriptors [3 ]);
648- }
649- if (file_descriptors [5 ] >= 0 ) {
650- rc = close (file_descriptors [5 ]);
586+ if (rc < 0 ) {
587+ write_error_and_exit ;
651588 }
589+ // Close all other file descriptors
590+ rc = -1 ;
591+ errno = ENOSYS ;
592+ #if __has_include (< linux /close_range .h > )
593+ // We must NOT close pipefd[1] for writing errors
594+ rc = close_range (STDERR_FILENO + 1 , pipefd [1 ] - 1 , 0 );
595+ rc |= close_range (pipefd [1 ] + 1 , ~0U , 0 );
596+ #endif
652597 if (rc != 0 ) {
653- int error = errno ;
654- write (pipefd [1 ], & error , sizeof (error ));
655- close (pipefd [1 ]);
656- _exit (EXIT_FAILURE );
598+ // close_range failed (or doesn't exist), fall back to close()
599+ for (int fd = STDERR_FILENO + 1 ; fd < _highest_possibly_open_fd (); fd + + ) {
600+ // We must NOT close pipefd[1] for writing errors
601+ if (fd != pipefd [1 ]) {
602+ close (fd );
603+ }
604+ }
657605 }
658606 // Run custom configuratior
659607 if (configurator != NULL ) {
0 commit comments