/****************************************************************************
**
** Copyright (C) 2004-2006 Frank Hemer <frank@hemer.org>
**
*****************************************************************************/

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <limits.h>
#include <termios.h>
#include <fcntl.h>
#include <signal.h>


//#define WRAPPER_DEBUG
#define PIPE_ERR_ONLY

static void sig_pipe(int signr) {
   fprintf(stderr,"ERROR: rsh: received broken pipe signal\n");
   exit(-1);
}

int main(int argc, char *argv[]) {
   int pid;
   #ifndef PIPE_ERR_ONLY
   int pipeStdout[2];
   int pipeStdin[2];
   #endif
   int pipeStderr[2];
   
   int failed = 0;
   
   #ifdef WRAPPER_DEBUG
   FILE * log = fopen("/home/frank/work/projects/LinCVS/contrib/cvswrapper/log.txt","a");
   if (log) {
      int i;
      for (i=0; i<argc; ++i) {
         fprintf(log,"startup arg[%d]: %s\n",i,argv[i]);fflush(log);
      }
   }
   #endif
   
   if (signal(SIGPIPE, sig_pipe) == SIG_ERR) {
      perror("can't sig_pipe\n");
   }
   
   #ifndef PIPE_ERR_ONLY
   if (pipe(pipeStdin)<0) {perror("can't pipeStdin\n");failed=1;}
   if (pipe(pipeStdout)<0) {perror("can't pipeStdout\n");failed=1;}
   #endif
   if (pipe(pipeStderr)<0) {perror("can't pipeStderr\n");failed=1;}
   if (failed) exit(-1);
   
   if ((pid = fork()) == 0) {
      
      #ifdef WRAPPER_DEBUG
      if (log) {
         fprintf(log,"forked child\n");fflush(log);
      }
      #endif
      
      #ifndef PIPE_ERR_ONLY
      close(pipeStdin[1]);
      close(pipeStdout[0]);
      #endif
      close(pipeStderr[0]);
      
      #ifndef PIPE_ERR_ONLY
      if (pipeStdin[0] != STDIN_FILENO) {
         if (dup2(pipeStdin[0],STDIN_FILENO) != STDIN_FILENO) {
            perror("can't dup2 stdin\n");
            failed = 1;
         } else {
            close(pipeStdin[0]);
         }
      }
      if (pipeStdout[1] != STDOUT_FILENO) {
         if (dup2(pipeStdout[1],STDOUT_FILENO) != STDOUT_FILENO) {
            perror("can't dup2 stdout\n");
            failed = 1;
         } else {
            close(pipeStdout[1]);
         }
      }
      #endif
      if (pipeStderr[1] != STDERR_FILENO) {
         if (dup2(pipeStderr[1],STDERR_FILENO) != STDERR_FILENO) {
            perror("can't dup2 stderr\n");
            failed = 1;
         } else {
            close(pipeStderr[1]);
         }
      }
      if (failed) exit(-1);
      
      argv[0] = getenv("WRAPPER_RSH");
      
      #ifdef WRAPPER_DEBUG
      if (log) {
         fprintf(log,"starting cmd ...\n");fflush(log);
         for (i=0; i<argc; ++i) {
            fprintf(log,"execv arg[%d]: %s\n",i,argv[i]);fflush(log);
         }
      }
      #endif
      
      execvp(argv[0],argv);
      fprintf(stderr,"ERROR: Cannot execute rsh: %s\n",argv[0]);
      perror("ERROR: Call to rsh returned");
      fflush(stdout);
      fflush(stderr);
      
      exit (-1);
      
   } else if (pid > 0) {
      fd_set rfdset;
      fd_set rfds;
      int s;
      char buff[PIPE_BUF];
      int wait = 1;
      int retval;
      
      #ifdef WRAPPER_DEBUG
      if (log) {
         fprintf(log,"forked parent\n");fflush(log);
      }
      #endif
      
      #ifndef PIPE_ERR_ONLY
      close(pipeStdin[0]);
      close(pipeStdout[1]);
      #endif
      close(pipeStderr[1]);
      
      FD_ZERO(&rfds);
      FD_ZERO(&rfdset);
      
      #ifndef PIPE_ERR_ONLY
      FD_SET(STDIN_FILENO, &rfdset);
      FD_SET(pipeStdout[0], &rfdset);
      wait += 1;
      #endif
      FD_SET(pipeStderr[0], &rfdset);
      
      do {
         
         rfds = rfdset;
         s = select(pipeStderr[0]+1,&rfds,NULL,NULL,NULL);
         
         #ifndef PIPE_ERR_ONLY
         if ( FD_ISSET(STDIN_FILENO,&rfds) ) {
            int c;
            #ifdef WRAPPER_DEBUG
            if (log) fprintf(log,"select STDIN_FILENO\n");
            #endif
            c = read(STDIN_FILENO,buff,PIPE_BUF);
            if (c <= 0) {
               #ifdef WRAPPER_DEBUG
               if (log) {
                  fprintf(log,"exit stdin\n");fflush(log);
               }
               #endif
               FD_CLR(STDIN_FILENO,&rfdset);
               close(pipeStdin[1]);
               continue;
            }
            write(pipeStdin[1],buff,c);
         }
         
         if ( FD_ISSET(pipeStdout[0],&rfds) ) {
            int c;
            #ifdef WRAPPER_DEBUG
            if (log) fprintf(log,"select STDOUT_FILENO\n");
            #endif
            c = read(pipeStdout[0],buff,PIPE_BUF);
            if (c <= 0) {
               #ifdef WRAPPER_DEBUG
               if (log) {
                  fprintf(log,"exit pipeStdout\n");fflush(log);
               }
               #endif
               --wait;
               FD_CLR(pipeStdout[0],&rfdset);
               continue;
            }
            write(STDOUT_FILENO,buff,c);
         }
         #endif
         
         if ( FD_ISSET(pipeStderr[0],&rfds) ) {
            int c;
            #ifdef WRAPPER_DEBUG
            if (log) fprintf(log,"select STDERR_FILENO\n");
            #endif
            c = read(pipeStderr[0],buff,PIPE_BUF);
            if (c <= 0) {
               #ifdef WRAPPER_DEBUG
               if (log) {
                  fprintf(log,"exit pipeStderr\n");fflush(log);
               }
               #endif
               --wait;
               FD_CLR(pipeStderr[0],&rfdset);
               continue;
            }
            write(STDERR_FILENO,buff,c);
         }
         
         
      } while (s>-1 && wait>0);
      
      #ifdef WRAPPER_DEBUG
      if (log) {
         fprintf(log,"wrapper done\n");fflush(log);
         fflush(log);
         fclose(log);
      }
      #endif
      if (waitpid(pid,&retval,0)<0) {
         perror("ERROR: rsh: waitpid error\n");
      } 
      return !WIFEXITED(retval);
      
   } else {
      perror("ERROR: rsh: can't fork\n");
   }
   
   return (-1); 
}

