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);
}
}