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)
@@ -361,91 +367,57 @@ static int _subprocess_addchdir_np(
361367#endif
362368}
363369
364- static int _subprocess_posix_spawn_fallback (
365- pid_t * _Nonnull pid ,
366- const char * _Nonnull exec_path ,
367- const char * _Nullable working_directory ,
368- const int file_descriptors [_Nonnull],
369- char * _Nullable const args [_Nonnull],
370- char * _Nullable const env [_Nullable ],
371- gid_t * _Nullable process_group_id
372- ) {
373- // Setup stdin, stdout, and stderr
374- posix_spawn_file_actions_t file_actions ;
375-
376- int rc = posix_spawn_file_actions_init (& file_actions );
377- if (rc != 0 ) { return rc ; }
378- if (file_descriptors [0 ] >= 0 ) {
379- rc = posix_spawn_file_actions_adddup2 (
380- & file_actions , file_descriptors [0 ], STDIN_FILENO
381- );
382- if (rc != 0 ) { return rc ; }
383- }
384- if (file_descriptors [2 ] >= 0 ) {
385- rc = posix_spawn_file_actions_adddup2 (
386- & file_actions , file_descriptors [2 ], STDOUT_FILENO
387- );
388- if (rc != 0 ) { return rc ; }
389- }
390- if (file_descriptors [4 ] >= 0 ) {
391- rc = posix_spawn_file_actions_adddup2 (
392- & file_actions , file_descriptors [4 ], STDERR_FILENO
393- );
394- if (rc != 0 ) { return rc ; }
395- }
396- // Setup working directory
397- rc = _subprocess_addchdir_np (& file_actions , working_directory );
398- if (rc != 0 ) {
399- return rc ;
400- }
370+ static int _positive_int_parse (const char * str ) {
371+ int out = 0 ;
372+ char c = 0 ;
401373
402- // Close parent side
403- if (file_descriptors [1 ] >= 0 ) {
404- rc = posix_spawn_file_actions_addclose (& file_actions , file_descriptors [1 ]);
405- if (rc != 0 ) { return rc ; }
406- }
407- if (file_descriptors [3 ] >= 0 ) {
408- rc = posix_spawn_file_actions_addclose (& file_actions , file_descriptors [3 ]);
409- if (rc != 0 ) { return rc ; }
374+ while ((c = * str ++ ) != 0 ) {
375+ out *= 10 ;
376+ if (c >= '0' && c <= '9' ) {
377+ out += c - '0' ;
378+ } else {
379+ return -1 ;
380+ }
410381 }
411- if (file_descriptors [5 ] >= 0 ) {
412- rc = posix_spawn_file_actions_addclose (& file_actions , file_descriptors [5 ]);
413- if (rc != 0 ) { return rc ; }
382+ return out ;
383+ }
384+
385+ static int _highest_possibly_open_fd_dir (const char * fd_dir ) {
386+ int highest_fd_so_far = 0 ;
387+ DIR * dir_ptr = opendir (fd_dir );
388+ if (dir_ptr == NULL ) {
389+ return -1 ;
414390 }
415391
416- // Setup spawnattr
417- posix_spawnattr_t spawn_attr ;
418- rc = posix_spawnattr_init (& spawn_attr );
419- if (rc != 0 ) { return rc ; }
420- // Masks
421- sigset_t no_signals ;
422- sigset_t all_signals ;
423- sigemptyset (& no_signals );
424- sigfillset (& all_signals );
425- rc = posix_spawnattr_setsigmask (& spawn_attr , & no_signals );
426- if (rc != 0 ) { return rc ; }
427- rc = posix_spawnattr_setsigdefault (& spawn_attr , & all_signals );
428- if (rc != 0 ) { return rc ; }
429- // Flags
430- short flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF ;
431- if (process_group_id != NULL ) {
432- flags |= POSIX_SPAWN_SETPGROUP ;
433- rc = posix_spawnattr_setpgroup (& spawn_attr , * process_group_id );
434- if (rc != 0 ) { return rc ; }
392+ struct dirent * dir_entry = NULL ;
393+ while ((dir_entry = readdir (dir_ptr )) != NULL ) {
394+ char * entry_name = dir_entry -> d_name ;
395+ int number = _positive_int_parse (entry_name );
396+ if (number > (long )highest_fd_so_far ) {
397+ highest_fd_so_far = number ;
398+ }
435399 }
436- rc = posix_spawnattr_setflags (& spawn_attr , flags );
437400
438- // Spawn!
439- rc = posix_spawn (
440- pid , exec_path ,
441- & file_actions , & spawn_attr ,
442- args , env
443- );
444- posix_spawn_file_actions_destroy (& file_actions );
445- posix_spawnattr_destroy (& spawn_attr );
446- return rc ;
401+ closedir (dir_ptr );
402+ return highest_fd_so_far ;
403+ }
404+
405+ static int _highest_possibly_open_fd (void ) {
406+ #if defined(__APPLE__ )
407+ int hi = _highest_possibly_open_fd_dir ("/dev/fd" );
408+ if (hi < 0 ) {
409+ hi = getdtablesize ();
410+ }
411+ #elif defined(__linux__ )
412+ int hi = _highest_possibly_open_fd_dir ("/proc/self/fd" );
413+ if (hi < 0 ) {
414+ hi = getdtablesize ();
415+ }
416+ #else
417+ int hi = 1024 ;
418+ #endif
419+ return hi ;
447420}
448- #endif // _POSIX_SPAWN
449421
450422int _subprocess_fork_exec (
451423 pid_t * _Nonnull pid ,
@@ -466,32 +438,6 @@ int _subprocess_fork_exec(
466438 close(pipefd[1]); \
467439 _exit(EXIT_FAILURE)
468440
469- int require_pre_fork = _subprocess_is_addchdir_np_available () == 0 ||
470- uid != NULL ||
471- gid != NULL ||
472- process_group_id != NULL ||
473- (number_of_sgroups > 0 && sgroups != NULL ) ||
474- create_session ||
475- configurator != NULL ;
476-
477- #if _POSIX_SPAWN
478- // If posix_spawn is available on this platform and
479- // we do not require prefork, use posix_spawn if possible.
480- //
481- // (Glibc's posix_spawn does not support
482- // `POSIX_SPAWN_SETEXEC` therefore we have to keep
483- // using fork/exec if `require_pre_fork` is true.
484- if (require_pre_fork == 0 ) {
485- return _subprocess_posix_spawn_fallback (
486- pid , exec_path ,
487- working_directory ,
488- file_descriptors ,
489- args , env ,
490- process_group_id
491- );
492- }
493- #endif
494-
495441 // Setup pipe to catch exec failures from child
496442 int pipefd [2 ];
497443 if (pipe (pipefd ) != 0 ) {
@@ -552,8 +498,6 @@ int _subprocess_fork_exec(
552498
553499 if (childPid == 0 ) {
554500 // Child process
555- close (pipefd [0 ]); // Close unused read end
556-
557501 // Reset signal handlers
558502 for (int signo = 1 ; signo < _SUBPROCESS_SIG_MAX ; signo ++ ) {
559503 if (signo == SIGKILL || signo == SIGSTOP ) {
@@ -615,41 +559,48 @@ int _subprocess_fork_exec(
615559 // Bind stdin, stdout, and stderr
616560 if (file_descriptors [0 ] >= 0 ) {
617561 rc = dup2 (file_descriptors [0 ], STDIN_FILENO );
618- if (rc < 0 ) {
619- write_error_and_exit ;
620- }
562+ } else {
563+ rc = close (STDIN_FILENO );
564+ }
565+ if (rc < 0 ) {
566+ write_error_and_exit ;
621567 }
568+
622569 if (file_descriptors [2 ] >= 0 ) {
623570 rc = dup2 (file_descriptors [2 ], STDOUT_FILENO );
624- if (rc < 0 ) {
625- write_error_and_exit ;
626- }
571+ } else {
572+ rc = close (STDOUT_FILENO );
573+ }
574+ if (rc < 0 ) {
575+ write_error_and_exit ;
627576 }
577+
628578 if (file_descriptors [4 ] >= 0 ) {
629579 rc = dup2 (file_descriptors [4 ], STDERR_FILENO );
630- if (rc < 0 ) {
631- int error = errno ;
632- write (pipefd [1 ], & error , sizeof (error ));
633- close (pipefd [1 ]);
634- _exit (EXIT_FAILURE );
635- }
636- }
637- // Close parent side
638- if (file_descriptors [1 ] >= 0 ) {
639- rc = close (file_descriptors [1 ]);
580+ } else {
581+ rc = close (STDERR_FILENO );
640582 }
641- if (file_descriptors [3 ] >= 0 ) {
642- rc = close (file_descriptors [3 ]);
643- }
644- if (file_descriptors [4 ] >= 0 ) {
645- rc = close (file_descriptors [4 ]);
583+ if (rc < 0 ) {
584+ write_error_and_exit ;
646585 }
586+ // Close all other file descriptors
587+ rc = -1 ;
588+ errno = ENOSYS ;
589+ #if __has_include (< linux /close_range .h > )
590+ // We must NOT close pipefd[1] for writing errors
591+ rc = close_range (STDERR_FILENO + 1 , pipefd [1 ] - 1 , 0 );
592+ rc |= close_range (pipefd [1 ] + 1 , ~0U , 0 );
593+ #endif
647594 if (rc != 0 ) {
648- int error = errno ;
649- write (pipefd [1 ], & error , sizeof (error ));
650- close (pipefd [1 ]);
651- _exit (EXIT_FAILURE );
595+ // close_range failed (or doesn't exist), fall back to close()
596+ for (int fd = STDERR_FILENO + 1 ; fd < _highest_possibly_open_fd (); fd + + ) {
597+ // We must NOT close pipefd[1] for writing errors
598+ if (fd != pipefd [1 ]) {
599+ close (fd );
600+ }
601+ }
652602 }
603+
653604 // Run custom configuratior
654605 if (configurator != NULL ) {
655606 configurator ();
0 commit comments