?
Hier der Source. Sei so nett und beachte OpenSource..
/** @defgroup xlib
/==============================================================================
/
/ File: xexec.c
/
/ Description:
/ ...
/ execute a external programm with given parameters. stdout/stderr will be
/ fetched and a appropiate call-back function is called to give the possibility
/ to scan the output. Also a timeout is included. All returnvalues will be
/ stored in a structure which is returned after execution.
/ ...
/
/ Author:
/ K. Kankowski
/
/ Version:
/ 0.0.1 initial Sa 15.05.2004 - 10:33:50 Karsten
/=============================================================================*/
#include <xlib.h>
extern char **environ;
static int prog_returned = 0;
static int stop_prog(execInfo_t info);
static void prog_exit(int signr);
/**
/==============================================================================
/ @ingroup xlib
/
/ FunctionName xexec
/----------------------------------------------------------------------
/ Will be called by:
/ - divers
/
/ Description
/ execute a given programm and return a filled xexecInfo_t
/ structure with appropiate data of the execution.
/ All needet data must befilled in the xexecInfo_t structure
/
/ \param xeceInfo_t
/ \return xexecInfo_t Ptr otherwise NULL
/=============================================================================/
*/
execInfo_t
xexec(int ac, char **av, char **env, execInfo_t exInfo) {
int sout[2] = { -1, -1 }; // pipe for stdout
int eout[2] = { -1, -1 }; // pipe for stderr
int sin[2] = { -1, -1 }; // pipe for stdin
pid_t prog_pid; // process id of external programm
msg_t msg_dat[3]; // msg structures for better handling
sigfkt old_sigchild_handler = NULL;
if(exInfo == NULL) {
xlog("xexec need a prefilled exInfo structur!");
return(exInfo);
}
// make it more secure
av[ac]=NULL;
exInfo->retval = 0;
if(env == NULL)
env = environ;
// check if we cann access the given programm
if( access(av[0], F_OK )) {
exInfo->retval = -1;
exInfo->error = XEXEC_NOPROG;
exInfo->ex_errno = errno;
return (exInfo);
}
if( access(av[0], R_OK|X_OK )) {
exInfo->retval = -1;
exInfo->error = XEXEC_NOPERM;
exInfo->ex_errno = errno;
return (exInfo);
}
if(pipe(sout)<0) {
exInfo->retval = -1;
exInfo->error = XEXEC_NOPIPE;
exInfo->ex_errno = errno;
} else if(pipe(eout)<0) {
exInfo->retval = -1;
exInfo->error = XEXEC_NOPIPE;
exInfo->ex_errno = errno;
} else if(pipe(sin)<0) {
exInfo->retval = -1;
exInfo->error = XEXEC_NOPIPE;
exInfo->ex_errno = errno;
}
prog_returned = 0;
// install own signal handler for SIGCHLD and save the old one
old_sigchild_handler = signal(SIGCHLD,prog_exit);
if( exInfo->error == 0 ) {
if(( prog_pid = fork()) == 0 ) {
/*
* This is the slave
*/
// close the read side of pipes
close(sout[0]);
close(eout[0]);
// close the write side of pipe
close(sin[1]);
if( dup2(sout[1], 1) < 0 ) {
xlog("dup2() sout failed (%s)",strerror(errno));
exit(1);
} else if( dup2(eout[1], 2) < 0 ) {
xlog("dup2() eout failed (%s)",strerror(errno));
exit(1);
} else if( dup2(sin[0], 0) < 0 ) {
xlog("dup2() sin failed (%s)",strerror(errno));
exit(1);
}
// make the slave the process leader for recursiv kill!
setsid();
if( execve(av[1],av+1,env) < 0 ) {
char msg[PATH_MAX + 256];
int len = 0;
sprintf(msg,"Exec of %s failed",av[1]);
len = strlen(msg);
write(2, msg, len);
exit(1);
}
} else if(prog_pid < 0 ) {
exInfo->retval = -1;
exInfo->error = XEXEC_NOFORK;
exInfo->ex_errno = errno;
xlog("fork() failed (%s)",strerror(errno));
} else {
/*
* Master
*/
pid_t parent = getppid();
int end = 0, in_end = 0, out_end = 0, std_end = 0;
int i = 0;
int highfd = 0;
int status = 0;
int eintr_cnt = 0;
fd_set rfds;
fd_set wfds;
struct timeval tv;
time_t exitTime = time(NULL) + exInfo->timeout;
// close the write side of pipes
close(sout[1]); sout[1]=-1;
close(eout[1]); eout[1]=-1;
// close the read side of pipe
close(sin[0]); sin[0]=-1;
memset(msg_dat,0,sizeof(msg_dat));
// fill the msg_t struct for stdout/stderr
msg_dat[0].msg_fd = sout[0];
msg_dat[1].msg_fd = eout[0];
msg_dat[0].msg_callback = exInfo->stdout_callback;
msg_dat[1].msg_callback = exInfo->stderr_callback;
// fill the msg_t struct for stdin
msg_dat[2].msg_fd = sin[1];
msg_dat[2].msg_buffer = exInfo->stdin_buffer;
msg_dat[2].msg_len = exInfo->stdin_len;
exInfo->start = time(NULL);
exInfo->exec_pid = prog_pid;
highfd = sout[0];
if(highfd < eout[0])
highfd = eout[0];
if(highfd < sin[1])
highfd = sin[1];
for( end = 0; end == 0; ) {
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(sout[0],&rfds);
FD_SET(eout[0],&rfds);
if(in_end == 1 && std_end == 1) {
highfd = sout[0];
if(highfd < eout[0])
highfd = eout[0];
close(sin[1]);
} else {
FD_SET(sin[1],&wfds);
}
status = select(highfd + 1, &rfds, &wfds , NULL, &tv);
// If an error ocurrs, or select was interrupt by a signal.
if(status < 0) {
if( errno == EINTR && eintr_cnt++ < 10 )
continue;
end=1;
exInfo->retval = -1;
exInfo->error = XEXEC_SELECT;
break;
}
// If my parent is no longer alive (paranoia)
if( kill( parent, 0 ) < 0 ) {
exInfo->retval = -1;
exInfo->error = XEXEC_NOPAR;
xlog("Parent process died. Will interrupt the "
"programm now.");
stop_prog(exInfo);
end=1;
break;
}
// If the given timeout is reached
if(exInfo->timeout && (( exitTime - time(NULL)) < 0 )) {
exInfo->timeoutReached = 1;
exInfo->retval = -1;
exInfo->error = XEXEC_TIMEOUT;
xlog("Timeout reached, try to kill (%d)\n",prog_pid);
stop_prog(exInfo);
end = 1;
break;
}
// if stdin pipe write able
if(FD_ISSET(msg_dat[2].msg_fd, &wfds)) {
// should we use our own stdin channel?
if(exInfo->use_stdin == 1) {
fcntl(sin[1], F_SETFL, O_NONBLOCK);
// read from stdin
while(!feof(stdin)) {
int tx = 0;
char ch[2];
if((tx = fread(ch, 1, 1, stdin)) < 0) {
xlog("read from stdin failed (%s)",strerror(errno));
end = 1;
break;
}
if(tx == 0) {
std_end = 1;
break;
}
// write to stdin pipe
if((tx = write(sin[1], ch, 1)) < 0) {
if(errno != EAGAIN) {
xlog("error on write to stdin (%s)\n",
strerror(errno));
end = 1;
} else {
break;
}
}
}
fcntl(sin[1], F_SETFL, ~O_NONBLOCK);
} else {
std_end = 1;
}
// if we have data to write to stdin
if(msg_dat[2].msg_buffer != NULL) {
int tx = 0;
fcntl(msg_dat[2].msg_fd, F_SETFL, O_NONBLOCK);
while(msg_dat[2].msg_wrote < msg_dat[2].msg_len) {
tx = write(sin[1],
msg_dat[2].msg_buffer + msg_dat[2].msg_wrote,
msg_dat[2].msg_len - msg_dat[2].msg_wrote);
if(tx > 0) {
exInfo->bytesToStdin =
msg_dat[2].msg_wrote += tx;
} else {
if(errno != EAGAIN) {
xlog("error on write to stdin (%s)\n",
strerror(errno));
end = 1;
} else {
break;
}
}
}
fcntl(msg_dat[2].msg_fd, F_SETFL, ~O_NONBLOCK);
if(msg_dat[2].msg_wrote == exInfo->stdin_len) {
in_end = 1;
}
} else {
// set in_end to 1 while there are no data to write
in_end = 1;
}
}
// read data from stderr/out and call callback
for(i = 0, end = 0; i < 2 ; i++) {
if(FD_ISSET(msg_dat[i].msg_fd,&rfds)) {
char ch[2];
char r = 0;
fcntl(msg_dat[i].msg_fd,F_SETFL,O_NONBLOCK);
while(!end && (r = read(msg_dat[i].msg_fd,ch,1))>0) {
if(msg_dat[i].msg_len >=
msg_dat[i].msg_buffer_size - 1) {
msg_dat[i].msg_buffer_size += 8192;
msg_dat[i].msg_buffer = (char *)XREALLOC(
msg_dat[i].msg_buffer,
msg_dat[i].msg_buffer_size);
}
if( ch[0] == '\n' && exInfo->call_block == 0 ) {
// insert '\n' in buffer
*(msg_dat[i].msg_buffer +
msg_dat[i].msg_len) = ch[0];
msg_dat[i].msg_len++;
// terminate buffer
*(msg_dat[i].msg_buffer+msg_dat[i].msg_len)=0;
msg_dat[i].msg_len++;
if(msg_dat[i].msg_callback) {
status = msg_dat[i].msg_callback(
prog_pid,
msg_dat[i].msg_buffer);
if(status == TERMINATE_PROG) {
exInfo->retval = -1;
exInfo->error = XEXEC_TERM;
stop_prog(exInfo);
end = 1;
break;
}
}
msg_dat[i].msg_len=0;
} else {
if(i==0) exInfo->bytesOnStdout++;
if(i==1) exInfo->bytesOnStderr++;
*(msg_dat[i].msg_buffer +
msg_dat[i].msg_len) = ch[0];
msg_dat[i].msg_len++;
}
}
if( r == 0 ) // EOF
msg_dat[i].msg_end = 1;
fcntl(msg_dat[i].msg_fd,F_SETFL,~O_NONBLOCK);
}
// set end if stdout/stderr detect EOF
if(msg_dat[0].msg_end == 1 && msg_dat[1].msg_end == 1) {
/*
* If call_line == 1 we call the callback function
* with whole msg_buffer instead after every line
*/
if(exInfo->call_block ) {
for(i = 0; i < 2 ; i++) {
if(msg_dat[i].msg_callback && msg_dat[i].msg_len) {
status = msg_dat[i].msg_callback(
prog_pid,
msg_dat[i].msg_buffer);
if(status == TERMINATE_PROG) {
exInfo->retval = -1;
exInfo->error = XEXEC_TERM;
stop_prog(exInfo);
end = 1;
break;
}
}
}
}
out_end = 1;
break;
}
}
if(in_end == 1 && out_end == 1 && std_end == 1)
end = 1;
}
}
}
// clean up now!
stop_prog(exInfo);
// reinstall orig SIGCHLD handler
signal(SIGCHLD,old_sigchild_handler);
XFREE(msg_dat[0].msg_buffer);
XFREE(msg_dat[1].msg_buffer);
if(sout[0]!=-1) close(sout[0]);
if(sout[1]!=-1) close(sout[1]);
if(eout[0]!=-1) close(eout[0]);
if(eout[1]!=-1) close(eout[1]);
return (exInfo);
}
static int
stop_prog(execInfo_t info) {
#define MAXRETRYS 5
int retrys = 0;
int signr = SIGTERM;
int loop = 0;
pid_t retpid = 0;
if(prog_returned) {
/*
* Programm was returned, so fetch the retval via waitpid
*/
retpid = waitpid(info->exec_pid, &info->retState, WNOHANG);
info->retState = WEXITSTATUS(info->retState);
if( retpid > 0 ) {
if(WIFSIGNALED(info->retState))
xlog("Process don't caught signal!");
}
} else {
/*
* try kill the programm and fetch the retval via waitpid
*/
do {
kill( -info->exec_pid, SIGTERM );
for(loop=0,retrys=0,retpid = 0; loop<2 && retpid == 0;
loop++,retrys=0,signr=SIGKILL) {
kill( -info->exec_pid, signr );
sleep(1);
}
retpid = waitpid(info->exec_pid, &info->retState, WNOHANG);
info->retState = WEXITSTATUS(info->retState);
if( retpid > 0 ) {
if(WIFSIGNALED(info->retState))
xlog("Process don't caught signal!");
break;
}
sleep(1);
} while( !prog_returned && retpid == 0 && ( ++retrys < MAXRETRYS));
}
info->duration = time(NULL) - info->start;
return (info->retState);
}
static void
prog_exit(int signr) {
if(signr == SIGCHLD)
prog_returned = 1;
}