final.cc
// final.cc
/**********************************************************************
*
* Solutions to Final Exam Questions
*
* C. Vickery
* CS-701, Spring 2003
*
*********************************************************************/
#include <cstdio>
#include <errno.h>
#include <poll.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
// err_check()
// -------------------------------------------------------------------
/*
* Prints system-defined error message and exits if an error has
* occurred.
*/
void err_check( const char *msg )
{
if ( errno == 0 )
return;
perror( msg );
exit( 1 );
}
// main()
// -------------------------------------------------------------------
/*
* Treats argv[1] through argv[argc-1] as a command line. Executes
* the command, acting as a proxy for the target command's stdin,
* stdout, and stderr.
*/
int
main( int argc, char *argv[], char * envp[] )
{
int num = 0;
int status = 0;
char buf[8192];
// Check command line arguments
if ( argc == 1 )
{
fprintf( stderr, "Usage: final arg ...\n" );
exit( 1 );
}
status = access( argv[1], X_OK );
if ( status != 0 )
{
perror( argv[1] );
exit( 1 );
}
// Create proxy pipes
int fd_in[2], fd_out[2], fd_err[2];
pipe( fd_in );
err_check( "fd_in" );
pipe( fd_out );
err_check( "fd_out" );
pipe( fd_err );
err_check( "fd_err" );
// fork ...
pid_t target = fork();
err_check( "fork" );
if ( target == 0 )
{
// Child: Do redirection
dup2( fd_in[0], 0 );
err_check( "dup in" );
close( fd_in[0] );
close( fd_in[1] );
dup2( fd_out[1], 1 );
err_check( "dup out" );
close( fd_out[0] );
close( fd_out[1] );
dup2( fd_err[1], 2 );
err_check( "dup err" );
close( fd_err[0] );
close( fd_err[1] );
// Execute the command
execve( argv[1], &argv[1], envp );
perror( argv[1] );
exit( 1 );
}
// Parent: Close unneeded fds
close( fd_in[0] );
close( fd_out[1] );
close( fd_err[1] );
// Set up for polling
struct pollfd poll_list[] =
{
{ STDIN_FILENO, POLLIN, 0 },
{ fd_out[0], POLLIN, 0 },
{ fd_err[0], POLLIN, 0 },
};
// Act as proxy for the command
for ( ;; )
{
fprintf( stderr, "Type something: " );
poll( poll_list, 3, -1 );
// Check stdin
if ( poll_list[0].revents != 0 )
{
num = read( STDIN_FILENO, buf, sizeof( buf ) );
err_check( "read stdin" );
write( fd_in[1], buf, num );
}
// Check stdout
if ( (poll_list[1].revents & (POLLERR|POLLHUP|POLLNVAL)) != 0 )
{
// Error: assume child has terminated
fprintf( stderr,
"Out.revents: %04X\n", poll_list[1].revents );
break;
}
if ( (poll_list[1].revents & POLLIN) != 0 )
{
// Read from stdout
num = read( fd_out[0], buf, sizeof( buf ) );
if ( num < 0 )
{
perror( "Reading out" );
break;
}
buf[num] = '\0';
fprintf( stderr, "%s.out: %s", argv[1], buf );
}
// Check stderr
if ( (poll_list[2].revents & (POLLERR|POLLHUP|POLLNVAL)) != 0 )
{
// Error: assume child has terminated
fprintf( stderr,
"Err.revents: %04X\n", poll_list[2].revents );
break;
}
if ( (poll_list[2].revents&POLLIN) !=0 )
{
// Read from stderr
num = read( fd_err[0], buf, sizeof( buf ) );
if ( num < 0 )
{
perror( "Reading err" );
break;
}
buf[num] = '\0';
fprintf( stderr, "%s.err: %s", argv[1], buf );
}
}
// Error from stderr or stdout. Harvest the child.
wait( &status );
if ( WIFEXITED( status ) )
{
printf( "%s has exited with exit code %d\n",
argv[1], WEXITSTATUS( status ) );
}
else
{
printf( "%s has terminated with no exit code\n",
argv[1] );
}
exit( 0 );
}