📄 q3process_unix.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.**** This file is part of the Qt3Support module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#include "qplatformdefs.h"// Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED.#if defined(connect)#undef connect#endif#include "q3process.h"#ifndef QT_NO_PROCESS#include "qapplication.h"#include "q3cstring.h"#include "q3ptrqueue.h"#include "q3ptrlist.h"#include "qsocketnotifier.h"#include "qtimer.h"#include "q3cleanuphandler.h"#include "qregexp.h"#include "private/q3membuf_p.h"#include <stdlib.h>#include <errno.h>#include <sys/types.h>#ifdef __MIPSEL__# ifndef SOCK_DGRAM# define SOCK_DGRAM 1# endif# ifndef SOCK_STREAM# define SOCK_STREAM 2# endif#endif//#define QT_Q3PROCESS_DEBUG#ifdef Q_C_CALLBACKSextern "C" {#endif // Q_C_CALLBACKS static QT_SIGNAL_RETTYPE qt_C_sigchldHnd(QT_SIGNAL_ARGS);#ifdef Q_C_CALLBACKS}#endif // Q_C_CALLBACKSclass QProc;class Q3ProcessManager;class Q3ProcessPrivate{public: Q3ProcessPrivate(); ~Q3ProcessPrivate(); void closeOpenSocketsForChild(); void newProc( pid_t pid, Q3Process *process ); Q3Membuf bufStdout; Q3Membuf bufStderr; Q3PtrQueue<QByteArray> stdinBuf; QSocketNotifier *notifierStdin; QSocketNotifier *notifierStdout; QSocketNotifier *notifierStderr; ssize_t stdinBufRead; QProc *proc; bool exitValuesCalculated; bool socketReadCalled; static Q3ProcessManager *procManager;};/*********************************************************************** * * QProc * **********************************************************************//* The class Q3Process does not necessarily map exactly to the running child processes: if the process is finished, the Q3Process class may still be there; furthermore a user can use Q3Process to start more than one process. The helper-class QProc has the semantics that one instance of this class maps directly to a running child process.*/class QProc{public: QProc( pid_t p, Q3Process *proc=0 ) : pid(p), process(proc) {#if defined(QT_Q3PROCESS_DEBUG) qDebug( "QProc: Constructor for pid %d and Q3Process %p", pid, process );#endif socketStdin = 0; socketStdout = 0; socketStderr = 0; } ~QProc() {#if defined(QT_Q3PROCESS_DEBUG) qDebug( "QProc: Destructor for pid %d and Q3Process %p", pid, process );#endif if ( process ) { if ( process->d->notifierStdin ) process->d->notifierStdin->setEnabled( false ); if ( process->d->notifierStdout ) process->d->notifierStdout->setEnabled( false ); if ( process->d->notifierStderr ) process->d->notifierStderr->setEnabled( false ); process->d->proc = 0; } if( socketStdin ) ::close( socketStdin ); if( socketStdout ) ::close( socketStdout ); if( socketStderr ) ::close( socketStderr ); } pid_t pid; int socketStdin; int socketStdout; int socketStderr; Q3Process *process;};/*********************************************************************** * * Q3ProcessManager * **********************************************************************/class Q3ProcessManager : public QObject{ Q_OBJECTpublic: Q3ProcessManager(); ~Q3ProcessManager(); void append( QProc *p ); void remove( QProc *p ); void cleanup();public slots: void removeMe(); void sigchldHnd( int );public: struct sigaction oldactChld; struct sigaction oldactPipe; Q3PtrList<QProc> *procList; int sigchldFd[2];private: QSocketNotifier *sn;};static void q3process_cleanup(){ delete Q3ProcessPrivate::procManager; Q3ProcessPrivate::procManager = 0;}#ifdef Q_OS_QNX6#define BAILOUT close(tmpSocket);close(socketFD[1]);return -1;int qnx6SocketPairReplacement (int socketFD[2]) { int tmpSocket; tmpSocket = socket (AF_INET, SOCK_STREAM, 0); if (tmpSocket == -1) return -1; socketFD[1] = socket(AF_INET, SOCK_STREAM, 0); if (socketFD[1] == -1) { BAILOUT }; sockaddr_in ipAddr; memset(&ipAddr, 0, sizeof(ipAddr)); ipAddr.sin_family = AF_INET; ipAddr.sin_addr.s_addr = INADDR_ANY; int socketOptions = 1; setsockopt(tmpSocket, SOL_SOCKET, SO_REUSEADDR, &socketOptions, sizeof(int)); bool found = false; for (int socketIP = 2000; (socketIP < 2500) && !(found); socketIP++) { ipAddr.sin_port = htons(socketIP); if (bind(tmpSocket, (struct sockaddr *)&ipAddr, sizeof(ipAddr))) found = true; } if (listen(tmpSocket, 5)) { BAILOUT }; // Select non-blocking mode int originalFlags = fcntl(socketFD[1], F_GETFL, 0); fcntl(socketFD[1], F_SETFL, originalFlags | O_NONBLOCK); // Request connection if (connect(socketFD[1], (struct sockaddr*)&ipAddr, sizeof(ipAddr))) if (errno != EINPROGRESS) { BAILOUT }; // Accept connection socketFD[0] = accept(tmpSocket, (struct sockaddr *)NULL, (size_t *)NULL); if(socketFD[0] == -1) { BAILOUT }; // We're done close(tmpSocket); // Restore original flags , ie return to blocking fcntl(socketFD[1], F_SETFL, originalFlags); return 0;}#undef BAILOUT#endifQ3ProcessManager::Q3ProcessManager() : sn(0){ procList = new Q3PtrList<QProc>; procList->setAutoDelete( true ); // The SIGCHLD handler writes to a socket to tell the manager that // something happened. This is done to get the processing in sync with the // event reporting.#ifndef Q_OS_QNX6 if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, sigchldFd ) ) {#else if ( qnx6SocketPairReplacement (sigchldFd) ) {#endif sigchldFd[0] = 0; sigchldFd[1] = 0; } else {#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessManager: install socket notifier (%d)", sigchldFd[1] );#endif sn = new QSocketNotifier( sigchldFd[1], QSocketNotifier::Read, this ); connect( sn, SIGNAL(activated(int)), this, SLOT(sigchldHnd(int)) ); sn->setEnabled( true ); } // install a SIGCHLD handler and ignore SIGPIPE struct sigaction act;#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessManager: install a SIGCHLD handler" );#endif act.sa_handler = qt_C_sigchldHnd; sigemptyset( &(act.sa_mask) ); sigaddset( &(act.sa_mask), SIGCHLD ); act.sa_flags = SA_NOCLDSTOP;#if defined(SA_RESTART) act.sa_flags |= SA_RESTART;#endif if ( sigaction( SIGCHLD, &act, &oldactChld ) != 0 ) qWarning( "Error installing SIGCHLD handler" );#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessManager: install a SIGPIPE handler (SIG_IGN)" );#endif act.sa_handler = QT_SIGNAL_IGNORE; sigemptyset( &(act.sa_mask) ); sigaddset( &(act.sa_mask), SIGPIPE ); act.sa_flags = 0; if ( sigaction( SIGPIPE, &act, &oldactPipe ) != 0 ) qWarning( "Error installing SIGPIPE handler" );}Q3ProcessManager::~Q3ProcessManager(){ delete procList; if ( sigchldFd[0] != 0 ) ::close( sigchldFd[0] ); if ( sigchldFd[1] != 0 ) ::close( sigchldFd[1] ); // restore SIGCHLD handler#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessManager: restore old sigchild handler" );#endif if ( sigaction( SIGCHLD, &oldactChld, 0 ) != 0 ) qWarning( "Error restoring SIGCHLD handler" );#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessManager: restore old sigpipe handler" );#endif if ( sigaction( SIGPIPE, &oldactPipe, 0 ) != 0 ) qWarning( "Error restoring SIGPIPE handler" );}void Q3ProcessManager::append( QProc *p ){ procList->append( p );#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessManager: append process (procList.count(): %d)", procList->count() );#endif}void Q3ProcessManager::remove( QProc *p ){ procList->remove( p );#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessManager: remove process (procList.count(): %d)", procList->count() );#endif cleanup();}void Q3ProcessManager::cleanup(){ if ( procList->count() == 0 ) { QTimer::singleShot( 0, this, SLOT(removeMe()) ); }}void Q3ProcessManager::removeMe(){ if ( procList->count() == 0 ) { qRemovePostRoutine(q3process_cleanup); Q3ProcessPrivate::procManager = 0; delete this; }}void Q3ProcessManager::sigchldHnd( int fd ){ // Disable the socket notifier to make sure that this function is not // called recursively -- this can happen, if you enter the event loop in // the slot connected to the processExited() signal (e.g. by showing a // modal dialog) and there are more than one process which exited in the // meantime. if ( sn ) { if ( !sn->isEnabled() ) return; sn->setEnabled( false ); } char tmp; ::read( fd, &tmp, sizeof(tmp) );#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessManager::sigchldHnd()" );#endif QProc *proc; Q3Process *process; bool removeProc; proc = procList->first(); while ( proc != 0 ) { removeProc = false; process = proc->process; if ( process != 0 ) { if ( !process->isRunning() ) {#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessManager::sigchldHnd() (PID: %d): process exited (Q3Process available)", proc->pid );#endif /* 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; // read pending data if ( proc->socketStdout && ::ioctl(proc->socketStdout, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) {#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessManager::sigchldHnd() (PID: %d): reading %d bytes of pending data on stdout", proc->pid, nbytes );#endif process->socketRead( proc->socketStdout ); } nbytes = 0; if ( proc->socketStderr && ::ioctl(proc->socketStderr, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) {#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessManager::sigchldHnd() (PID: %d): reading %d bytes of pending data on stderr", proc->pid, nbytes );#endif process->socketRead( proc->socketStderr ); } // close filedescriptors if open, and disable the // socket notifiers if ( proc->socketStdout ) { ::close( proc->socketStdout ); proc->socketStdout = 0; if (process->d->notifierStdout) process->d->notifierStdout->setEnabled(false); } if ( proc->socketStderr ) { ::close( proc->socketStderr ); proc->socketStderr = 0; if (process->d->notifierStderr) process->d->notifierStderr->setEnabled(false); } if ( process->notifyOnExit ) emit process->processExited(); removeProc = true; } } else { int status; if ( ::waitpid( proc->pid, &status, WNOHANG ) == proc->pid ) {#if defined(QT_Q3PROCESS_DEBUG) qDebug( "Q3ProcessManager::sigchldHnd() (PID: %d): process exited (Q3Process not available)", proc->pid );#endif removeProc = true; } } if ( removeProc ) { QProc *oldproc = proc; proc = procList->next(); remove( oldproc ); } else { proc = procList->next(); } } if ( sn ) sn->setEnabled( true );}#include "q3process_unix.moc"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -