Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

ProcessInvoker.cpp

Go to the documentation of this file.
00001 #include "ProcessInvoker.h"
00002 
00003 #ifndef PIPE_BUF // uClibc or similar
00004 #include <linux/limits.h>
00005 #endif
00006 
00007 static ProcessInvoker* g_this;
00008 /* ------------------------------------------------------------------------ */
00009 /*      static functions                                                                                                                */
00010 /* ------------------------------------------------------------------------ */
00011 
00012 static Sigfunc* setSignalHandler(int signo, Sigfunc* handler)
00013 {
00014         struct sigaction act,oact;
00015 
00016         act.sa_handler = handler;
00017         ::sigemptyset(&act.sa_mask);
00018         act.sa_flags = 0;
00019 #ifdef  SA_RESTART
00020         act.sa_flags |= SA_RESTART;
00021 #endif
00022         if(::sigaction(signo, &act, &oact) < 0){
00023                 return(NULL);
00024         }
00025         return(oact.sa_handler);
00026 }
00027 
00028 static void childHandler(int /*signo*/)
00029 {
00030         pid_t pid;
00031         int status;
00032         while((pid = ::waitpid(-1, &status, WNOHANG)) > 0){
00033                 if(pid == g_this->m_child){
00034                         g_this->notifyFinish(status);
00035                 }
00036         }
00037 }
00038 
00039 /* ------------------------------------------------------------------------ */
00040 /*      ProcessInvoker Class : parent process                                                                   */
00041 /* ------------------------------------------------------------------------ */
00042 ProcessInvoker::ProcessInvoker()
00043 {
00044         g_this = this;
00045         m_isRunning = false;
00046         m_child = 0;
00047         m_defChildHandler = SIG_DFL;
00048         m_pTimer = new QTimer(this);
00049         m_stdfd[0] = m_stdfd[1] = -1;
00050         m_errfd[0] = m_errfd[1] = -1;
00051         connect(m_pTimer, SIGNAL(timeout()),
00052                 this, SLOT(readOutputs()));
00053 }
00054 
00055 ProcessInvoker::~ProcessInvoker()
00056 {
00057         qDebug("ProcessInvoker::~ProcessInvoker()");
00058 }
00059 
00060 bool ProcessInvoker::openPipe()
00061 {
00062         if(m_stdfd[0] >= 0) closePipe(m_stdfd, 0);
00063         if(m_stdfd[1] >= 0) closePipe(m_stdfd, 1);
00064         if(::pipe(m_stdfd) < 0){
00065                 return(false);
00066         }
00067         if(m_errfd[0] >= 0) closePipe(m_errfd, 0);
00068         if(m_errfd[1] >= 0) closePipe(m_errfd, 1);
00069         if(::pipe(m_errfd) < 0){
00070                 closePipe(m_stdfd);
00071                 return(false);
00072         }
00073         m_maxfdp1 = m_stdfd[0];
00074         if(m_errfd[0] > m_maxfdp1){
00075                 m_maxfdp1 = m_errfd[0];
00076         }
00077         m_maxfdp1++;
00078         return(true);
00079 }
00080 
00081 void ProcessInvoker::closePipe(int fd[], int n)
00082 {
00083         if(fd == NULL){
00084                 closePipe(m_stdfd, n);
00085                 closePipe(m_errfd, n);
00086         } else {
00087                 if(n != 1 && fd[0] >= 0){
00088                         ::close(fd[0]);
00089                         fd[0] = -1;
00090                 }
00091                 if(n != 0 && fd[1] >= 0){
00092                         ::close(fd[1]);
00093                         fd[1] = -1;
00094                 }
00095         }
00096 }
00097 
00098 void ProcessInvoker::setRunning(int pid)
00099 {
00100         m_child = pid;
00101         m_isRunning = true;
00102 }
00103 
00104 bool ProcessInvoker::run(const QString& args)
00105 {
00106         //setArguments(KHUtil::parseArgs(args));
00107         setArguments(StringParser::split(' ', args));
00108         return(run());
00109 }
00110 
00111 bool ProcessInvoker::run()
00112 {
00113         if(m_isRunning){
00114                 return(false);
00115         }
00116         m_isRunning = true;
00117         if(m_arguments.isEmpty()){
00118                 m_isRunning = false;
00119                 return(false);
00120         }
00121         if(m_arguments[0][0] != '/'){
00122                 m_isRunning = false;
00123                 return(false);
00124         }
00125 
00126         for(QStringList::Iterator it=m_arguments.begin();
00127                         it!=m_arguments.end(); ++it){
00128                 qDebug("arguments[%s]", (*it).ascii());
00129         }
00130 
00131         /* open pipe */
00132         if(openPipe() == false){
00133                 m_isRunning = false;
00134                 return(false);
00135         }
00136 
00137         /* signal handler reset */
00138         m_defChildHandler = setSignalHandler(SIGCHLD, SIG_DFL);
00139 
00140         m_child = ::fork();
00141         if(m_child < 0){
00142                 /* fork error */
00143                 closePipe();
00144                 setSignalHandler(SIGCHLD, m_defChildHandler);
00145                 m_isRunning = false;
00146                 return(false);
00147         } else if(m_child == 0){
00148                 /* child process */
00149                 qDebug("child process[%d]", ::getpid());
00150                 m_child = ::getpid();
00151                 //setSignalHandler(SIGCHLD, SIG_DFL);
00152                 workerProc();
00153                 /* no return */
00154         }
00155         /* close pipe(write) */
00156         closePipe(NULL, 1);
00157 #if 0
00158         m_pTimer = new QTimer(this);
00159         connect(m_pTimer, SIGNAL(timeout()),
00160                 this, SLOT(readOutputs()));
00161 #endif
00162         m_pTimer->start(500);
00163         {
00164                 emit start(m_child, m_arguments);
00165                 QCopEnvelope e(SC_CHANNEL, "start(int,QStringList)");
00166                 e << m_child << m_arguments;
00167                 if(m_isNotify){
00168                         int idx = m_arguments[0].findRev('/');
00169                         notifyStatus(m_arguments[0].mid(idx+1), m_child);
00170                 }
00171         }
00172         int status;
00173         if(::waitpid(-1, &status, WNOHANG) > 0){
00174                 qDebug("finish");
00175                 notifyFinish(status);
00176         } else {
00177                 /* signal handler set */
00178                 setSignalHandler(SIGCHLD, childHandler);
00179         }
00180         return(true);
00181 }
00182 
00183 void ProcessInvoker::terminate()
00184 {
00185         if(m_isRunning && m_child > 0){
00186                 terminate(m_child);
00187         }
00188 }
00189 
00190 void ProcessInvoker::terminate(pid_t pid)
00191 {
00192 	::kill(pid, SIGTERM);
00193 }
00194 
00195 void ProcessInvoker::kill()
00196 {
00197         if(m_isRunning && m_child > 0){
00198                 kill(m_child);
00199         }
00200 }
00201 
00202 void ProcessInvoker::kill(pid_t pid)
00203 {
00204 	::kill(pid, SIGKILL);
00205 }
00206 
00207 #if 0
00208 const QStringList ProcessInvoker::parseArgs(const QString& arguments)
00209 {
00210         QString str;
00211         QStringList args;
00212         char quote = 0;
00213         char c;
00214         for(unsigned int i=0; i<arguments.length(); i++){
00215                 c = arguments[i];
00216                 switch(c){
00217                 case '\"':
00218                         if(quote == 0){
00219                                 quote = c;
00220                         } else if(quote == '\"'){
00221                                 if(str.length() > 0){
00222                                         args.append(str);
00223                                 }
00224                                 str = "";
00225                                 quote = 0;
00226                         } else {
00227                                 str += c;
00228                         }
00229                         break;
00230                 case '\'':
00231                         if(quote == 0){
00232                                 quote = c;
00233                         } else if(quote == '\''){
00234                                 if(str.length() > 0){
00235                                         args.append(str);
00236                                 }
00237                                 str = "";
00238                                 quote = 0;
00239                         } else {
00240                                 str += c;
00241                         }
00242                         break;
00243                 case ' ':
00244                         if(quote == 0){
00245                                 if(str.length() > 0){
00246                                         args.append(str);
00247                                         str = "";
00248                                 }
00249                         } else {
00250                                 str += c;
00251                         }
00252                         break;
00253                 default:
00254                         str += c;
00255                         break;
00256                 }
00257         }
00258         if(str.length() > 0){
00259                 args.append(str);
00260         }
00261         return(args);
00262 }
00263 #endif
00264 
00265 void ProcessInvoker::readOutputs()
00266 {
00267         struct timeval tmval;
00268         tmval.tv_sec = 0;
00269         tmval.tv_usec = 0;
00270         fd_set rset;
00271 
00272         QByteArray stdBuf, errBuf, resBuf;
00273         QDataStream stdStream(stdBuf, IO_WriteOnly);
00274         QDataStream errStream(errBuf, IO_WriteOnly);
00275 
00276         int iRet;
00277         bool running;
00278         char buf[PIPE_BUF+1];
00279         while(true){
00280                 running = false;
00281                 FD_ZERO(&rset);
00282                 if(m_stdfd[0] >= 0){
00283                         FD_SET(m_stdfd[0], &rset);
00284                         running = true;
00285                 }
00286                 if(m_errfd[0] >= 0){
00287                         FD_SET(m_errfd[0], &rset);
00288                         running = true;
00289                 }
00290                 if(running == false){
00291                         m_pTimer->stop();
00292                         //delete m_pTimer;
00293                         break;
00294                 }
00295 
00296                 if((iRet = ::select(m_maxfdp1, &rset, NULL, NULL, &tmval)) <= 0){
00297                         qDebug("select[%d]", iRet);
00298                         break;
00299                 }
00300 
00301                 if(m_stdfd[0] >= 0 && FD_ISSET(m_stdfd[0], &rset)){
00302                         int n = ::read(m_stdfd[0], buf, PIPE_BUF);
00303                         if(n > 0){
00304                                 stdStream.writeRawBytes(buf, n);
00305                         } else {
00306                                 qDebug("stdout close");
00307                                 closePipe(m_stdfd, 0);
00308                         }
00309                 }
00310                 if(m_errfd[0] >= 0 && FD_ISSET(m_errfd[0], &rset)){
00311                         int n = ::read(m_errfd[0], buf, PIPE_BUF);
00312                         if(n > 0){
00313                                 errStream.writeRawBytes(buf, n);
00314                         } else {
00315                                 qDebug("stderr close");
00316                                 closePipe(m_errfd, 0);
00317                         }
00318                 }
00319         }
00320 
00321         if(stdBuf.size() > 0){
00322                 QCopEnvelope e(SC_CHANNEL, "stdout(int,QByteArray)");
00323                 e << m_child << stdBuf;
00324         }
00325         if(errBuf.size() > 0){
00326                 QCopEnvelope e(SC_CHANNEL, "stderr(int,QByteArray)");
00327                 e << m_child << errBuf;
00328         }
00329         if(running == false){
00330                 QCopEnvelope e(SC_CHANNEL, "close(int)");
00331                 e << m_child;
00332         }
00333 }
00334 
00335 #if 0
00336 void ProcessInvoker::waitFinish()
00337 {
00338         int status;
00339         if(::waitpid(m_child, &status, 0) > 0){
00340                 notifyFinish(status);
00341         } else {
00342                 notifyFinish(0, false);
00343         }
00344 }
00345 #endif
00346 
00347 void ProcessInvoker::notifyFinish(int status, bool success)
00348 {
00349         bool stopped = false;
00350         QString result;
00351         int code;
00352         if(success){
00353                 if(WIFEXITED(status)){
00354                         code = WEXITSTATUS(status);
00355                         if(code == 127){
00356                                 result = "error";
00357                         } else {
00358                                 result = "exit";
00359                         }
00360                 } else if(WIFSIGNALED(status)){
00361                         result = "terminated";
00362                         code = WTERMSIG(status);
00363                 } else if(WIFSTOPPED(status)){
00364                         result = "stopped";
00365                         code = WSTOPSIG(status);
00366                         stopped = true;
00367                 } else {
00368                         /* これは無いはず? */
00369                         result = "error";
00370                         code = -2;
00371                         qWarning("ProcessInvoker: unknown status");
00372                 }               
00373         } else {
00374                 result = "error";
00375                 code = -1;
00376                 qWarning("ProcessInvoker: wait error");
00377         }
00378         emit finish(result, code);
00379         QCopEnvelope e(SC_CHANNEL, "finish(int,QString,int)");
00380         e << m_child << result << code;
00381         if(m_isNotify){
00382                 notifyStatus(result, code);
00383                 setNotify(false);
00384         }
00385         if(stopped == false){
00386                 setSignalHandler(SIGCHLD, m_defChildHandler);
00387                 m_isRunning = false;
00388         }
00389 }
00390 
00391 void ProcessInvoker::notifyStatus(const QString& result, int code)
00392 {
00393         QString message = QString::number(code);
00394         message.append(":");
00395         message.append(result);
00396         Global::statusMessage(message);
00397 }
00398 
00399 /* ------------------------------------------------------------------------ */
00400 /*      ProcessInvoker Class : child process                                                                    */
00401 /* ------------------------------------------------------------------------ */
00402 void ProcessInvoker::workerProc()
00403 {
00404         closePipe(m_stdfd, 0);
00405         closePipe(m_errfd, 0);
00406         if(m_stdfd[1] != STDOUT_FILENO){
00407                 ::dup2(m_stdfd[1], STDOUT_FILENO);
00408                 closePipe(m_stdfd, 1);
00409         }
00410         if(m_errfd[1] != STDERR_FILENO){
00411                 ::dup2(m_errfd[1], STDERR_FILENO);
00412                 closePipe(m_errfd, 1);
00413         }
00414 
00415         QCString* arglist = new QCString[m_arguments.count()+1];
00416         const char** argv = new const char*[m_arguments.count()+1];
00417         unsigned int i;
00418         for(i=0; i<m_arguments.count(); i++){
00419                 //arglist[i] = m_arguments[i].local8Bit();
00420                 arglist[i] = m_arguments[i];
00421                 argv[i] = arglist[i];
00422         }
00423         argv[i] = 0;
00424         ::execv(argv[0], (char*const*)argv);
00425         delete[] arglist;
00426         delete[] argv;
00427         ::_exit(127);
00428 }

Generated on Sat Nov 5 16:16:42 2005 for OPIE by  doxygen 1.4.2