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
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 )
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
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
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
00132 if(openPipe() == false){
00133 m_isRunning = false;
00134 return(false);
00135 }
00136
00137
00138 m_defChildHandler = setSignalHandler(SIGCHLD, SIG_DFL);
00139
00140 m_child = ::fork();
00141 if(m_child < 0){
00142
00143 closePipe();
00144 setSignalHandler(SIGCHLD, m_defChildHandler);
00145 m_isRunning = false;
00146 return(false);
00147 } else if(m_child == 0){
00148
00149 qDebug("child process[%d]", ::getpid());
00150 m_child = ::getpid();
00151
00152 workerProc();
00153
00154 }
00155
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
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
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
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
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 }