📄 q3process_unix.cpp
字号:
/*********************************************************************** * * Q3ProcessPrivate * **********************************************************************/Q3ProcessManager *Q3ProcessPrivate::procManager = 0;Q3ProcessPrivate::Q3ProcessPrivate(){#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessPrivate: Constructor" );#endif stdinBufRead = 0; notifierStdin = 0; notifierStdout = 0; notifierStderr = 0; exitValuesCalculated = false; socketReadCalled = false; proc = 0;}Q3ProcessPrivate::~Q3ProcessPrivate(){#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessPrivate: Destructor" );#endif if ( proc != 0 ) { if ( proc->socketStdin != 0 ) { ::close( proc->socketStdin ); proc->socketStdin = 0; } proc->process = 0; } while ( !stdinBuf.isEmpty() ) { delete stdinBuf.dequeue(); } delete notifierStdin; delete notifierStdout; delete notifierStderr;}/* Closes all open sockets in the child process that are not needed by the child process. Otherwise one child may have an open socket on standard input, etc. of another child.*/void Q3ProcessPrivate::closeOpenSocketsForChild(){ if ( procManager != 0 ) { if ( procManager->sigchldFd[0] != 0 ) ::close( procManager->sigchldFd[0] ); if ( procManager->sigchldFd[1] != 0 ) ::close( procManager->sigchldFd[1] ); // close also the sockets from other Q3Process instances for ( QProc *p=procManager->procList->first(); p!=0; p=procManager->procList->next() ) { ::close( p->socketStdin ); ::close( p->socketStdout ); ::close( p->socketStderr ); } }}void Q3ProcessPrivate::newProc( pid_t pid, Q3Process *process ){ proc = new QProc( pid, process ); if ( procManager == 0 ) { procManager = new Q3ProcessManager; qAddPostRoutine(q3process_cleanup); } // the Q3ProcessManager takes care of deleting the QProc instances procManager->append( proc );}/*********************************************************************** * * sigchld handler callback * **********************************************************************/static QT_SIGNAL_RETTYPE qt_C_sigchldHnd(QT_SIGNAL_ARGS){ if ( Q3ProcessPrivate::procManager == 0 ) return; if ( Q3ProcessPrivate::procManager->sigchldFd[0] == 0 ) return; char a = 1; ::write( Q3ProcessPrivate::procManager->sigchldFd[0], &a, sizeof(a) );}/*********************************************************************** * * Q3Process * **********************************************************************//* This private class does basic initialization.*/void Q3Process::init(){ d = new Q3ProcessPrivate(); exitStat = 0; exitNormal = false;}/* This private class resets the process variables, etc. so that it can be used for another process to start.*/void Q3Process::reset(){ delete d; d = new Q3ProcessPrivate(); exitStat = 0; exitNormal = false; d->bufStdout.clear(); d->bufStderr.clear();}Q3Membuf* Q3Process::membufStdout(){ if ( d->proc && d->proc->socketStdout ) { /* Apparently, there is not consistency among different operating systems on how to use FIONREAD. FreeBSD, Linux and Solaris all expect the 3rd argument to ioctl() to be an int, which is normally 32-bit even on 64-bit machines. IRIX, on the other hand, expects a size_t, which is 64-bit on 64-bit machines. So, the solution is to use size_t initialized to zero to make sure all bits are set to zero, preventing underflow with the FreeBSD/Linux/Solaris ioctls. */ size_t nbytes = 0; if ( ::ioctl(d->proc->socketStdout, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) socketRead( d->proc->socketStdout ); } return &d->bufStdout;}Q3Membuf* Q3Process::membufStderr(){ if ( d->proc && d->proc->socketStderr ) { /* Apparently, there is not consistency among different operating systems on how to use FIONREAD. FreeBSD, Linux and Solaris all expect the 3rd argument to ioctl() to be an int, which is normally 32-bit even on 64-bit machines. IRIX, on the other hand, expects a size_t, which is 64-bit on 64-bit machines. So, the solution is to use size_t initialized to zero to make sure all bits are set to zero, preventing underflow with the FreeBSD/Linux/Solaris ioctls. */ size_t nbytes = 0; if ( ::ioctl(d->proc->socketStderr, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) socketRead( d->proc->socketStderr ); } return &d->bufStderr;}/*! Destroys the instance. If the process is running, it is <b>not</b> terminated! The standard input, standard output and standard error of the process are closed. You can connect the destroyed() signal to the kill() slot, if you want the process to be terminated automatically when the instance is destroyed. \sa tryTerminate() kill()*/Q3Process::~Q3Process(){ delete d;}/*! Tries to run a process for the command and arguments that were specified with setArguments(), addArgument() or that were specified in the constructor. The command is searched for in the path for executable programs; you can also use an absolute path in the command itself. If \a env is null, then the process is started with the same environment as the starting process. If \a env is non-null, then the values in the stringlist are interpreted as environment setttings of the form \c {key=value} and the process is started in these environment settings. For convenience, there is a small exception to this rule: under Unix, if \a env does not contain any settings for the environment variable \c LD_LIBRARY_PATH, then this variable is inherited from the starting process; under Windows the same applies for the environment variable \c PATH. Returns true if the process could be started; otherwise returns false. You can write data to the process's standard input with writeToStdin(). You can close standard input with closeStdin() and you can terminate the process with tryTerminate(), or with kill(). You can call this function even if you've used this instance to create a another process which is still running. In such cases, Q3Process closes the old process's standard input and deletes pending data, i.e., you lose all control over the old process, but the old process is not terminated. This applies also if the process could not be started. (On operating systems that have zombie processes, Qt will also wait() on the old process.) \sa launch() closeStdin()*/bool Q3Process::start( QStringList *env ){#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3Process::start()" );#endif reset(); int sStdin[2]; int sStdout[2]; int sStderr[2]; // open sockets for piping#ifndef Q_OS_QNX6 if ( (comms & Stdin) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdin ) == -1 ) {#else if ( (comms & Stdin) && qnx6SocketPairReplacement(sStdin) == -1 ) {#endif return false; }#ifndef Q_OS_QNX6 if ( (comms & Stderr) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStderr ) == -1 ) {#else if ( (comms & Stderr) && qnx6SocketPairReplacement(sStderr) == -1 ) {#endif if ( comms & Stdin ) { ::close( sStdin[0] ); ::close( sStdin[1] ); } return false; }#ifndef Q_OS_QNX6 if ( (comms & Stdout) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdout ) == -1 ) {#else if ( (comms & Stdout) && qnx6SocketPairReplacement(sStdout) == -1 ) {#endif if ( comms & Stdin ) { ::close( sStdin[0] ); ::close( sStdin[1] ); } if ( comms & Stderr ) { ::close( sStderr[0] ); ::close( sStderr[1] ); } return false; } // the following pipe is only used to determine if the process could be // started int fd[2]; if ( pipe( fd ) < 0 ) { // non critical error, go on fd[0] = 0; fd[1] = 0; } // construct the arguments for exec Q3CString *arglistQ = new Q3CString[ _arguments.count() + 1 ]; const char** arglist = new const char*[ _arguments.count() + 1 ]; int i = 0; for ( QStringList::Iterator it = _arguments.begin(); it != _arguments.end(); ++it ) { arglistQ[i] = (*it).local8Bit(); arglist[i] = arglistQ[i];#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3Process::start(): arg %d = %s", i, arglist[i] );#endif i++; }#ifdef Q_OS_MACX if(i) { Q3CString arg_bundle = arglistQ[0]; QFileInfo fi(QString::fromUtf8(arg_bundle.constData())); if(fi.exists() && fi.isDir() && arg_bundle.right(4) == ".app") { Q3CString exe = arg_bundle; int lslash = exe.findRev('/'); if(lslash != -1) exe = exe.mid(lslash+1); exe = Q3CString(arg_bundle + "/Contents/MacOS/" + exe); exe = exe.left(exe.length() - 4); //chop off the .app if(QFile::exists(QString::fromLatin1(exe.constData()))) { arglistQ[0] = exe; arglist[0] = arglistQ[0]; } } }#endif arglist[i] = 0; // Must make sure signal handlers are installed before exec'ing // in case the process exits quickly. if ( d->procManager == 0 ) { d->procManager = new Q3ProcessManager; qAddPostRoutine(q3process_cleanup); } // fork and exec QApplication::flushX(); pid_t pid = fork(); if ( pid == 0 ) { // child d->closeOpenSocketsForChild(); if ( comms & Stdin ) { ::close( sStdin[1] ); ::dup2( sStdin[0], STDIN_FILENO ); } if ( comms & Stdout ) { ::close( sStdout[0] ); ::dup2( sStdout[1], STDOUT_FILENO ); } if ( comms & Stderr ) { ::close( sStderr[0] ); ::dup2( sStderr[1], STDERR_FILENO ); } if ( comms & DupStderr ) { ::dup2( STDOUT_FILENO, STDERR_FILENO ); }#ifndef QT_NO_DIR ::chdir( workingDir.absPath().latin1() );#endif if ( fd[0] ) ::close( fd[0] ); if ( fd[1] ) ::fcntl( fd[1], F_SETFD, FD_CLOEXEC ); // close on exec shows success if ( env == 0 ) { // inherit environment and start process#ifndef Q_OS_QNX4 ::execvp( arglist[0], (char*const*)arglist ); // ### cast not nice#else ::execvp( arglist[0], (char const*const*)arglist ); // ### cast not nice#endif } else { // start process with environment settins as specified in env // construct the environment for exec int numEntries = env->count();#if defined(Q_OS_MACX) QString ld_library_path(QLatin1String("DYLD_LIBRARY_PATH"));#else QString ld_library_path(QLatin1String("LD_LIBRARY_PATH"));#endif bool setLibraryPath = env->grep( QRegExp( QLatin1Char('^') + ld_library_path + QLatin1Char('=') ) ).empty() && getenv( ld_library_path.local8Bit() ) != 0; if ( setLibraryPath ) numEntries++; Q3CString *envlistQ = new Q3CString[ numEntries + 1 ]; const char** envlist = new const char*[ numEntries + 1 ]; int i = 0; if ( setLibraryPath ) { envlistQ[i] = QString( ld_library_path + QLatin1String("=%1") ).arg( QString::fromLocal8Bit(getenv( ld_library_path.local8Bit() )) ).local8Bit(); envlist[i] = envlistQ[i]; i++; } for ( QStringList::Iterator it = env->begin(); it != env->end(); ++it ) { envlistQ[i] = (*it).local8Bit(); envlist[i] = envlistQ[i]; i++; } envlist[i] = 0; // look for the executable in the search path if ( _arguments.count()>0 && getenv("PATH")!=0 ) { QString command = _arguments[0]; if ( !command.contains( QLatin1Char('/') ) ) { QStringList pathList = QStringList::split( QLatin1Char(':'), QString::fromLocal8Bit(getenv( "PATH" )) ); for (QStringList::Iterator it = pathList.begin(); it != pathList.end(); ++it ) { QString dir = *it;#if defined(Q_OS_MACX) //look in a bundle if(!QFile::exists(dir + QLatin1Char('/') + command) && QFile::exists(dir + QLatin1Char('/') + command + QLatin1String(".app"))) dir += QLatin1Char('/') + command + QLatin1String(".app/Contents/MacOS");#endif#ifndef QT_NO_DIR QFileInfo fileInfo( dir, command );#else QFileInfo fileInfo( dir + "/" + command );#endif if ( fileInfo.isExecutable() ) {#if defined(Q_OS_MACX) arglistQ[0] = fileInfo.absFilePath().local8Bit();#else arglistQ[0] = fileInfo.filePath().local8Bit();#endif arglist[0] = arglistQ[0]; break; } } } }#ifndef Q_OS_QNX4 ::execve( arglist[0], (char*const*)arglist, (char*const*)envlist ); // ### casts not nice#else ::execve( arglist[0], (char const*const*)arglist,(char const*const*)envlist ); // ### casts not nice#endif } if ( fd[1] ) { char buf = 0; ::write( fd[1], &buf, 1 ); ::close( fd[1] ); } ::_exit( -1 ); } else if ( pid == -1 ) { // error forking goto error; } // test if exec was successful if ( fd[1] ) ::close( fd[1] ); if ( fd[0] ) { char buf; for ( ;; ) { int n = ::read( fd[0], &buf, 1 ); if ( n==1 ) { // socket was not closed => error if ( ::waitpid( pid, 0, WNOHANG ) != pid ) { // The wait did not succeed yet, so try again when we get // the sigchild (to avoid zombies). d->newProc( pid, 0 ); } d->proc = 0; goto error; } else if ( n==-1 ) { if ( errno==EAGAIN || errno==EINTR ) // try it again continue; } break; } ::close( fd[0] ); } d->newProc( pid, this ); if ( comms & Stdin ) { ::close( sStdin[0] ); d->proc->socketStdin = sStdin[1]; // Select non-blocking mode int originalFlags = fcntl(d->proc->socketStdin, F_GETFL, 0); fcntl(d->proc->socketStdin, F_SETFL, originalFlags | O_NONBLOCK); d->notifierStdin = new QSocketNotifier( sStdin[1], QSocketNotifier::Write ); connect( d->notifierStdin, SIGNAL(activated(int)),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -