📄 qprocess_unix.cpp
字号:
/************************************************************************ Copyright (C) 2000-2005 Trolltech AS. All rights reserved.**** This file is part of the Qtopia Environment.** ** This program is free software; you can redistribute it and/or modify it** under the terms of the GNU General Public License as published by the** Free Software Foundation; either version 2 of the License, or (at your** option) any later version.** ** A copy of the GNU GPL license version 2 is included in this package as ** LICENSE.GPL.**** This program is distributed in the hope that it will be useful, but** WITHOUT ANY WARRANTY; without even the implied warranty of** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details.**** In addition, as a special exception Trolltech gives permission to link** the code of this program with Qtopia applications copyrighted, developed** and distributed by Trolltech under the terms of the Qtopia Personal Use** License Agreement. You must comply with the GNU General Public License** in all respects for all of the code used other than the applications** licensed under the Qtopia Personal Use License Agreement. If you modify** this file, you may extend this exception to your version of the file,** but you are not obligated to do so. If you do not wish to do so, delete** this exception statement from your version.** ** See http://www.trolltech.com/gpl/ for GPL licensing information.**** Contact info@trolltech.com if any conditions of this licensing are** not clear to you.************************************************************************///#include "qplatformdefs.h"// Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED.#if defined(connect)#undef connect#endif#include "qprocess.h"#ifndef QT_NO_PROCESS#include "qapplication.h"#include "qqueue.h"#include "qlist.h"#include "qsocketnotifier.h"#include "qtimer.h"#include "qregexp.h"#include "qcleanuphandler_p.h"#include <stdlib.h>// ### FOR Qt 2.3 compat#include <unistd.h>#include <signal.h>#include <sys/socket.h>#include <sys/ioctl.h>#include <sys/wait.h>#include <sys/fcntl.h>#include <errno.h>#include <sys/resource.h>#ifdef __MIPSEL__# ifndef SOCK_DGRAM# define SOCK_DGRAM 1# endif# ifndef SOCK_STREAM# define SOCK_STREAM 2# endif#endif//#define QT_QPROCESS_DEBUG#ifdef Q_C_CALLBACKSextern "C" {#endif // Q_C_CALLBACKS#define QT_SIGNAL_RETTYPE void#define QT_SIGNAL_ARGS int#define QT_SIGNAL_IGNORE SIG_IGN QT_SIGNAL_RETTYPE qt_C_sigchldHnd(QT_SIGNAL_ARGS); QT_SIGNAL_RETTYPE qt_C_sigpipeHnd(QT_SIGNAL_ARGS);#ifdef Q_C_CALLBACKS}#endif // Q_C_CALLBACKSclass QProc;class QProcessManager;class QProcessPrivate{public: QProcessPrivate(); ~QProcessPrivate(); void closeOpenSocketsForChild(); void newProc( pid_t pid, QProcess *process ); QByteArray bufStdout; QByteArray bufStderr; QQueue<QByteArray> stdinBuf; QSocketNotifier *notifierStdin; QSocketNotifier *notifierStdout; QSocketNotifier *notifierStderr; ssize_t stdinBufRead; QProc *proc; bool exitValuesCalculated; bool socketReadCalled; static QProcessManager *procManager;};/*********************************************************************** * * QProc * **********************************************************************//* The class QProcess does not necessarily map exactly to the running child processes: if the process is finished, the QProcess class may still be there; furthermore a user can use QProcess 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, QProcess *proc=0 ) : pid(p), process(proc) {#if defined(QT_QPROCESS_DEBUG) qDebug( "QProc: Constructor for pid %d and QProcess %p", pid, process );#endif socketStdin = 0; socketStdout = 0; socketStderr = 0; } ~QProc() {#if defined(QT_QPROCESS_DEBUG) qDebug( "QProc: Destructor for pid %d and QProcess %p", pid, process );#endif if ( process != 0 ) { 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 != 0 ) ::close( socketStdin ); // ### close these sockets even on parent exit or is it better only on // sigchld (but what do I have to do with them on exit then)? if( socketStdout != 0 ) ::close( socketStdout ); if( socketStderr != 0 ) ::close( socketStderr ); } pid_t pid; int socketStdin; int socketStdout; int socketStderr; QProcess *process;};/*********************************************************************** * * QProcessManager * **********************************************************************/class QProcessManager : public QObject{ Q_OBJECTpublic: QProcessManager(); ~QProcessManager(); void append( QProc *p ); void remove( QProc *p ); void cleanup();public slots: void removeMe(); void sigchldHnd( int );public: struct sigaction oldactChld; struct sigaction oldactPipe; QList<QProc> *procList; int sigchldFd[2];};QCleanupHandler<QProcessManager> qprocess_cleanup_procmanager;QProcessManager::QProcessManager(){ procList = new QList<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. if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, sigchldFd ) ) { sigchldFd[0] = 0; sigchldFd[1] = 0; } else {#if defined(QT_QPROCESS_DEBUG) qDebug( "QProcessManager: install socket notifier (%d)", sigchldFd[1] );#endif QSocketNotifier *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_QPROCESS_DEBUG) qDebug( "QProcessManager: 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_QPROCESS_DEBUG) qDebug( "QProcessManager: install a SIGPIPE handler (SIG_IGN)" );#endif /* Using qt_C_sigpipeHnd rather than SIG_IGN is a workaround for a strange problem where GNU tar (called by backuprestore) would hang on filesystem-full. Strangely, the qt_C_sigpipeHnd is never even called, yet this avoids the hang. */ act.sa_handler = qt_C_sigpipeHnd; sigemptyset( &(act.sa_mask) ); sigaddset( &(act.sa_mask), SIGPIPE ); act.sa_flags = 0; if ( sigaction( SIGPIPE, &act, &oldactPipe ) != 0 ) qWarning( "Error installing SIGPIPE handler" );}QProcessManager::~QProcessManager(){ delete procList; if ( sigchldFd[0] != 0 ) ::close( sigchldFd[0] ); if ( sigchldFd[1] != 0 ) ::close( sigchldFd[1] ); // restore SIGCHLD handler#if defined(QT_QPROCESS_DEBUG) qDebug( "QProcessManager: restore old sigchild handler" );#endif if ( sigaction( SIGCHLD, &oldactChld, 0 ) != 0 ) qWarning( "Error restoring SIGCHLD handler" );#if defined(QT_QPROCESS_DEBUG) qDebug( "QProcessManager: restore old sigpipe handler" );#endif if ( sigaction( SIGPIPE, &oldactPipe, 0 ) != 0 ) qWarning( "Error restoring SIGPIPE handler" );}void QProcessManager::append( QProc *p ){ procList->append( p );#if defined(QT_QPROCESS_DEBUG) qDebug( "QProcessManager: append process (procList.count(): %d)", procList->count() );#endif}void QProcessManager::remove( QProc *p ){ procList->remove( p );#if defined(QT_QPROCESS_DEBUG) qDebug( "QProcessManager: remove process (procList.count(): %d)", procList->count() );#endif cleanup();}void QProcessManager::cleanup(){ if ( procList->count() == 0 ) { QTimer::singleShot( 0, this, SLOT(removeMe()) ); }}void QProcessManager::removeMe(){ if ( procList->count() == 0 ) { qprocess_cleanup_procmanager.remove( &QProcessPrivate::procManager ); QProcessPrivate::procManager = 0; delete this; }}void QProcessManager::sigchldHnd( int fd ){ char tmp; ::read( fd, &tmp, sizeof(tmp) );#if defined(QT_QPROCESS_DEBUG) qDebug( "QProcessManager::sigchldHnd()" );#endif QProc *proc; QProcess *process; bool removeProc; proc = procList->first(); while ( proc != 0 ) { removeProc = FALSE; process = proc->process; QProcess *process_exit_notify=0; if ( process != 0 ) { if ( !process->isRunning() ) {#if defined(QT_QPROCESS_DEBUG) qDebug( "QProcessManager::sigchldHnd() (PID: %d): process exited (QProcess available)", proc->pid );#endif // read pending data int nbytes = 0; if ( ::ioctl(proc->socketStdout, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) {#if defined(QT_QPROCESS_DEBUG) qDebug( "QProcessManager::sigchldHnd() (PID: %d): reading %d bytes of pending data on stdout", proc->pid, nbytes );#endif process->socketRead( proc->socketStdout ); } nbytes = 0; if ( ::ioctl(proc->socketStderr, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) {#if defined(QT_QPROCESS_DEBUG) qDebug( "QProcessManager::sigchldHnd() (PID: %d): reading %d bytes of pending data on stderr", proc->pid, nbytes );#endif process->socketRead( proc->socketStderr ); } if ( process->notifyOnExit ) process_exit_notify = process; removeProc = TRUE; } } else { int status; if ( ::waitpid( proc->pid, &status, WNOHANG ) == proc->pid ) {#if defined(QT_QPROCESS_DEBUG) qDebug( "QProcessManager::sigchldHnd() (PID: %d): process exited (QProcess not available)", proc->pid );#endif removeProc = TRUE; } } if ( removeProc ) { QProc *oldproc = proc; proc = procList->next(); remove( oldproc ); } else { proc = procList->next(); } if ( process_exit_notify ) emit process_exit_notify->processExited(); }}#include "qprocess_unix.moc"/*********************************************************************** * * QProcessPrivate * **********************************************************************/QProcessManager *QProcessPrivate::procManager = 0;QProcessPrivate::QProcessPrivate(){#if defined(QT_QPROCESS_DEBUG) qDebug( "QProcessPrivate: Constructor" );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -