Linux: call a program in C / C++

vfork, exec, zombies and file descriptors

Call a program using vfork to create a child process, call the program by exec, avoid zombie processes, perfom error handling, close file descriptors.


// C++ Header
#include <iostream.h>
#include <cerrno.h>
// C-Header
extern "C" {
   #include <unistd.h> // fork, execv, getpid  
   #include <sys/wait.h> // for signal, waitpid
   #include <errno.h> // fork, execv, getpid  
   #include <dirent.h>// for directory content of /proc/PID/fd/   
}

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

    if(child == 0) // executed by child process: 
    {
        int child_pid = getpid();
			
        // replace the child process with exec*-function
        char *args[2]; // arguments for exec
        args[0] = (char*)program_call; // first argument is program_call
        args[1] = (char*)param;
        args[2] = NULL; // no arguments...
        
        // close file descriptors > 2
        const char* prefix = "/proc/";
        const char* suffix = "/fd/";
        char child_proc_dir[16]; // directory contains file descriptors
        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) // Converted successfully: valid file descriptor
               {
                  if (fd > 2)
                  {
                     close(fd); // close the file descriptor
                  }
               }
            }
            closedir (dir);
        } else {
           cerr<< "can not open directory: " << child_proc_dir << endl;
        }        

        execv(program_call,args);

        // executes if execv failed
        _exit(2);
    }
    else if (child == -1) // fork error: child < 0
    {
    	string fork_note = "fork failed for program: \n" + string(program_call);
    	string fork_error = "";

        if (errno == EAGAIN)
        {
           fork_error = "\n To much processes";
        }
        else if (errno == ENOMEM)
        {
           fork_error = "\n Not enough space available.";
        }
        else 
        {
           fork_error = "\n "\n Unexpected error: " + errno;
        }        
        string message = fork_note + fork_error;
        fl_alert( message.c_str() );
        Fl::run();
    }
    else // this is executed by parent process
    {
      usleep(50); // give some time to get status of child: 50 microseconds

      // get und store useful errors of exec:
      string child_error = "";
      if ( errno == EACCES)
      {
         child_error = "\n Permission denied or process file not executable.";
      }
      else if ( errno == ENOENT)
      {
         child_error = "\n Invalid path or file.";
      }
      else if ( errno == EPERM)
      {
         child_error = "\n Superuser privileges required.";
      }
      else if ( errno == ENOEXEC)
      {
         child_error = "\n Unsupported format of file.";
      }
      else
      {
         child_error = "\n unexpected error:" + errno;
      }      
      int child_status;
      if ( waitpid(child, &child_status, WNOHANG | WUNTRACED) < 0) // waitpid failed
      {
         string waitpid_message = "Error - Execution failed: \n   " + string(program_call);
         string message = waitpid_message  + child_error;
         fl_alert( message.c_str() );
         Fl::run();
      }
      else if ( WIFEXITED( child_status )  &&  WEXITSTATUS( child_status ) != 0) // child process failed although waitpid does not
      {
         string waitpid_message = "Error - Process failed: \n   " + string(program_call);
         string message = waitpid_message  + child_error;
         fl_alert( message.c_str() );
         Fl::run();
      } 
       // prevent zombies: 
       pid_t p;
       // Reap all pending child processes
       do {
          p = waitpid(-1, NULL, WNOHANG);
       } while (p != (pid_t)0 && p != (pid_t)-1);
    }
}