Why does closing file descriptors after fork affect the child process?

I want to run programs in linux with the click of a button, so I wrote a function execute:

void execute(const char* program_call, const char* param )
{
    pid_t child = vfork();

    if(child == 0) // child process
    {
        int child_pid = getpid();

        char *args[2]; // arguments for exec
        args[0] = (char*)program_call; // first argument is program_call
        args[1] = (char*)param;

        // close all opened file descriptors:
        const char* prefix = "/proc/";
        const char* suffix = "/fd/";
        char child_proc_dir[16]; 
        sprintf(child_proc_dir,"%s%d%s",prefix,child_pid, suffix);

        DIR *dir;
        struct dirent *ent;

        if ((dir = opendir (child_proc_dir)) != NULL) {
            // get files and directories within directory
            while ((ent = readdir (dir)) != NULL) {
                // convert file name to int
                char* end;
                int fd = strtol(ent->d_name, &end, 32);
                if (!*end) // valid file descriptor
                {
                    close(fd); // close file descriptor
                    // or set the flag FD_CLOEXEC
                    //fcntl( fd, F_SETFD, FD_CLOEXEC );
                }
            }
            closedir (dir);
        } 
        else 
        {
            cerr<< "can not open directory: " << child_proc_dir <<endl;
        }
        // replace the child process with exec*-function
            execv(program_call,args);
            _exit(2);
        }
    else if (child == -1) // fork error
    {
        if (errno == EAGAIN)
        {
            cerr<<"To much processes"<<endl;
        }
        else if (errno == ENOMEM)
        {
            cerr<<"Not enough space available."<<endl;
        }
    }
    else // parent process
    {
        usleep(50); // give some time 
        if ( errno == EACCES)
        {
            cerr<<"Permission denied or process file not executable."<<endl;
        }
        else if ( errno == ENOENT)
        {
            cerr<<"\n Invalid path or file."<<endl;
        }
        int child_status;
        if ( waitpid(child, &child_status, WNOHANG | WUNTRACED) < 0) // waitpid failed
        {
            cerr<<"Error - Execution failed"<<endl;
        }
        else if ( WIFEXITED( child_status ) &&  WEXITSTATUS( child_status ) != 0)   
        {
            cerr<<"Child process error - Execution failed"<<endl;
        }
    }
}

There are two problems:

  • Closing file descriptors causes some problems, for example, Thunderbird or VLC crashes without sound. More precisely: closing stdout(1)also stderr(2)causes these problems. As far as I understand, closing the file descriptor before exec only prevents their duplication (there is no need to send information from the child process to the parent process). Why does this affect the child process? Replacing close()the flag FD_CLOEXECdoes not change anything. Also setting a flag FD_CLOEXECbefore fork does not solve the problem. Is there a better way to prevent file descriptor inheritance?

  • waitpid 0, , , () . usleep(50) , , .

vfork, fork.

+4
2

: , , FD_CLOEXEC, this

: The return value of waitpid is often 0, sepecfied WNOHANG waitpid.

waitpid(): on success, returns the process ID of the child whose state has changed; 
if WNOHANG was specified  and  one  or  more  child(ren) specified by pid exist, 
but have not yet changed state, then 0 is returned.  On error, -1 is returned.
+4

, 2014 , vfork, fork (2). ( vfork (2) POSIX 2001 POSIX 2008).

-

for (int fd=3; fd<256; fd++) (void) close(fd);

(: a fd , close(fd) , , 3, 0 == stdin, 1 == stdout, 2 = = stderr, close ).

( ).

, , - , stdin, stdout, stderr program_call ( ), .

FD_CLOEXEC .

, .

, daemon (3) ( vality) posix_spawn.

STDIN_FILENO ( 0) STDOUT_FILENO (.. 1) STDERR_FILENO (.. 2), open("/dev/null",... dup2 - exec, .

+2

All Articles