Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

ExecutableCommPort.cc

Go to the documentation of this file.
00001 #include "ExecutableCommPort.h"
00002 #include <sys/types.h>
00003 #include <sys/socket.h>
00004 #include <sys/wait.h>
00005 #include <unistd.h>
00006 
00007 using namespace std; 
00008 
00009 const std::string ExecutableCommPort::autoRegisterExecutableCommPort = CommPort::getRegistry().registerType<ExecutableCommPort>("ExecutableCommPort");
00010 
00011 ExecutableCommPort::~ExecutableCommPort() {
00012   if(openedCnt>0) {
00013     cerr << "Connection still open in ExecutableCommPort destructor" << endl;
00014     command.removePrimitiveListener(this);
00015     shell.removePrimitiveListener(this);
00016   }
00017   if(!isChildRunning())
00018     return;
00019   cerr << "ExecutableCommPort destructing, but child is still running... waiting 3 seconds" << endl;
00020   if(waitChild(3000,500))
00021     return;
00022   cerr << "ExecutableCommPort child is STILL running... sending interrupt signal" << endl;
00023   if(kill(child,SIGINT)==-1) {
00024     perror("ExecutableCommPort unable to kill child");
00025     return;
00026   }
00027   if(waitChild(2000,500))
00028     return;
00029   cerr << "ExecutableCommPort child is STILL running... sending kill signal and moving on..." << endl;
00030   if(kill(child,SIGKILL)==-1) {
00031     perror("ExecutableCommPort unable to kill child");
00032     return;
00033   }
00034 }
00035 
00036 bool ExecutableCommPort::open() {
00037   if(openedCnt++>0)
00038     return true;
00039   command.addPrimitiveListener(this);
00040   shell.addPrimitiveListener(this);
00041   
00042   int s2c[2];
00043   int c2s[2];
00044   if(pipe(s2c)==-1) {
00045     perror("ERROR: ExecutableCommPort could not open simulator to child pipe");
00046     return false;
00047   }
00048   if(pipe(c2s)==-1) {
00049     perror("ERROR: ExecutableCommPort could not open child to simulator pipe");
00050 		::close(s2c[0]);
00051 		::close(s2c[1]);
00052     return false;
00053   }
00054   
00055   child = fork();
00056   if(child==-1) {
00057     perror("ERROR: ExecutableCommPort could not fork to launch executable");
00058 		::close(s2c[0]);
00059 		::close(s2c[1]);
00060 		::close(c2s[0]);
00061 		::close(c2s[1]);
00062     return false;
00063   }
00064   
00065   if(child==0) {
00066     // child process
00067 		::close(s2c[WRITEPIPE]); // close write of sim to child (not used on our end!)
00068 		::close(c2s[READPIPE]); // close read of child to sim (not used on our end!)
00069 		
00070 		::close(STDIN_FILENO); // theoretically not necessary (dup2 will close it), but we'll be paranoid
00071     if(::dup2(s2c[READPIPE],STDIN_FILENO)==-1)
00072       perror("ERROR: ExecutableCommPort could not dup2 the child's input to stdin");
00073 		::close(s2c[READPIPE]); // now duplicated, close this reference
00074 		
00075 		::close(STDOUT_FILENO); // theoretically not necessary (dup2 will close it), but we'll be paranoid
00076     if(::dup2(c2s[WRITEPIPE],STDOUT_FILENO)==-1)
00077       perror("ERROR: ExecutableCommPort could not dup2 the child's output to stdout");
00078 		::close(c2s[WRITEPIPE]); // now duplicated, close this reference
00079     
00080     // Launch executable!
00081     execlp(shell.c_str(), shell.c_str(), "-c", command.c_str(), NULL);
00082     
00083     // any return from here means error occurred!
00084     perror("ERROR: ExecutableCommPort could not launch the executable!");
00085 		::close(STDIN_FILENO);
00086 		::close(STDOUT_FILENO);
00087     _exit(EXIT_FAILURE); // note _exit() instead of exit(), avoids double-flushing buffers...
00088     
00089   } else {
00090     // parent process
00091 		::close(s2c[READPIPE]); // close read of sim to child (not used on our end!)
00092 		::close(c2s[WRITEPIPE]); // close write of child to sim (not used on our end!)
00093     rbuf.adoptFD(c2s[READPIPE]);
00094     wbuf.adoptFD(s2c[WRITEPIPE]);
00095   }
00096   opened();
00097   return true;
00098 }
00099 
00100 bool ExecutableCommPort::close() {
00101   if(openedCnt==0)
00102     std::cerr << "Warning: ExecutableCommPort close() without open()" << std::endl;
00103   if(--openedCnt>0)
00104     return false;
00105   closing();
00106   command.removePrimitiveListener(this);
00107   shell.removePrimitiveListener(this);
00108   rbuf.close();
00109   wbuf.close();
00110   return true;
00111 }
00112 
00113 void ExecutableCommPort::plistValueChanged(const plist::PrimitiveBase& pl) {
00114   if(&pl==&command || &pl==&shell) {
00115     if(openedCnt>0) {
00116       unsigned int tmp=openedCnt;
00117       openedCnt=1; // fake out close() and open() to make sure they trigger the action
00118       close();
00119       open();
00120       openedCnt=tmp; // reset original reference count
00121     }
00122   } else {
00123     std::cerr << "Unhandled value change in " << getClassName() << ": " << pl.get() << std::endl;
00124   }
00125 }
00126 
00127 bool ExecutableCommPort::waitChild(unsigned int t, unsigned int p) {
00128   if(!isChildRunning())
00129     return true;
00130   for(unsigned int x=0; x<t; x+=p) {
00131     usleep(p*1000);
00132     if(!isChildRunning())
00133       return true;
00134   }
00135   return false;
00136 }
00137 
00138 bool ExecutableCommPort::isChildRunning() const {
00139   if(child==0)
00140     return false;
00141   int status=0;
00142   int ret=waitpid(child,&status,WNOHANG);
00143   if(ret==-1) {
00144     perror("ExecutableCommPort unable to check child status, waitpid");
00145     return false;
00146   }
00147   if(ret==0)
00148     return false;
00149   if(WIFEXITED(status) || WIFSIGNALED(status))
00150     return false;
00151   return true;
00152 }
00153 
00154 /*! @file
00155  * @brief 
00156  * @author Ethan Tira-Thompson (ejt) (Creator)
00157  */

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Mon May 9 05:01:38 2016 by Doxygen 1.6.3