Changeset 168 for trunk


Ignore:
Timestamp:
Jan 6, 2007, 7:38:01 PM (19 years ago)
Author:
dmik
Message:

Kernel/QProcess: Use spawnvpe() instead of DosExecPgm() in order to make it possible for a child process to correctly inherit LIBC file descriptors. Note though that inheritance doesn't always work correctly by now (at least for socket descriptors, see http://svn.netlabs.org/qtapps/ticket/6). QT_QPROCESS_USE_DOSEXECPGM can be defined to restore the old behavior.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kernel/qprocess_pm.cpp

    r159 r168  
    4848#include "qintdict.h"
    4949#include "qmutex.h"
     50#include "qfile.h"
    5051#include "private/qinternal_p.h"
    5152#include "qt_os2.h"
    5253
     54// When QT_QPROCESS_USE_DOSEXECPGM is not defined, we use spawnvpe() instead of
     55// DosExecPgm() to let LIBC file (pipe, socket) descriptors be properly
     56// inherited if the child uses the same LIBC version.
     57//#define QT_QPROCESS_USE_DOSEXECPGM
     58
    5359#include <string.h>
     60
     61#if !defined(QT_QPROCESS_USE_DOSEXECPGM)
     62#include <process.h>
     63#include <sys/wait.h>
     64#endif
    5465
    5566//#define QT_QPROCESS_DEBUG
     
    754765    if ( _arguments.isEmpty() )
    755766        return FALSE;
     767
     768#if defined(QT_QPROCESS_USE_DOSEXECPGM)
    756769
    757770    // construct the arguments for DosExecPgm()
     
    979992            appNameFull.data(), buf.data(), buf.data() + strlen( buf.data() ) + 1 );
    980993#endif
    981    
     994
     995#else // QT_QPROCESS_USE_DOSEXECPGM
     996
     997    // construct the arguments for spawnvpe()
     998
     999    QCString appName;
     1000    QStrList appArgs;
     1001
     1002    for ( QStringList::ConstIterator it = _arguments.begin();
     1003          it != _arguments.end(); ++ it
     1004    ) {
     1005        if ( it == _arguments.begin() ) {
     1006            appName = QFile::encodeName( *it );
     1007        } else {
     1008            appArgs.append( QFile::encodeName( *it ) );
     1009        }
     1010    }
     1011
     1012#if defined(QT_QPROCESS_DEBUG)
     1013    qDebug( "QProcess::start(): [%s]",
     1014            QFile::encodeName( _arguments.join( "] [" ) ).data() );
     1015#endif
     1016
     1017    // prepare the environment
     1018
     1019    QStrList envList;
     1020    if ( env != 0 ) {
     1021        // add the user environment
     1022        bool seenPATH = FALSE;
     1023        bool seenCOMSPEC = FALSE;
     1024        for ( QStringList::ConstIterator it = env->begin();
     1025              it != env->end(); ++ it
     1026        ) {
     1027            envList.append( QFile::encodeName( *it ) );
     1028            if ( !seenPATH )
     1029                seenPATH = !strncmp( envList.current(), "PATH=", 4 );
     1030            if ( !seenCOMSPEC )
     1031                seenCOMSPEC = !strncmp( envList.current(), "COMSPEC=", 8 );
     1032        }
     1033        if ( !seenPATH ) {
     1034            // inherit PATH if missing (for convenience)
     1035            // (note that BEGINLIBPATH and ENDLIBPATH, if any, are automatically
     1036            // inherited, while LIBPATH is always a global setting)
     1037            const char *path = getenv( "PATH" );
     1038            QCString str( 5 /* PATH= */ + strlen( path ) + 1 /* \0 */ );
     1039            strcpy( str.data(), "PATH=" );
     1040            strcat( str.data() + 5, path );
     1041            envList.append( str );
     1042        }
     1043        // inherit COMSPEC if missing (to let the child start .cmd and .bat)
     1044        if ( !seenCOMSPEC ) {
     1045            const char *comspec = getenv( "COMSPEC" );
     1046            QCString str( 8 /* COMSPEC= */ + strlen( comspec ) + 1 /* \0 */ );
     1047            strcpy( str.data(), "COMSPEC=" );
     1048            strcat( str.data() + 5, comspec );
     1049            envList.append( str );
     1050        }
     1051    }
     1052
     1053    APIRET rc = 0;
     1054    char pathBuf[ CCHMAXPATH ];
     1055
     1056#endif // QT_QPROCESS_USE_DOSEXECPGM
     1057
    9821058    // create STDIN/OUT/ERR redirection pipes
    9831059   
     
    12061282#endif
    12071283   
     1284#if defined(QT_QPROCESS_USE_DOSEXECPGM)
     1285
    12081286    // DosExecPgm()
    12091287   
     
    12131291                     buf.data(), envlist.data(), &resc, appNameFull.data() );
    12141292
     1293#else // defined(QT_QPROCESS_USE_DOSEXECPGM)
     1294   
     1295    // start the application
     1296   
     1297    int i = 0;
     1298
     1299    char **argv = new char *[ appArgs.count() + 2 ];
     1300    argv[ i++ ] = appName.data();
     1301    for ( appArgs.first(); appArgs.current(); appArgs.next() )
     1302        argv[ i++ ] = appArgs.current();
     1303    argv[ i ] = NULL;
     1304
     1305    char **envv = NULL;
     1306    if ( envList.count() ) {
     1307        i = 0;
     1308        envv = new char *[ envList.count() + 1 ];
     1309        for ( envList.first(); envList.current(); envList.next() )
     1310            envv[ i++ ] = envList.current();
     1311        envv[ i ] = NULL;
     1312    } else {
     1313        envv = environ;
     1314    }
     1315
     1316    int pid = spawnvpe( P_NOWAIT | P_DEFAULT, appName.data(), argv, envv );
     1317   
     1318    // if spawnvpe() couldn't find the executable, try .CMD and .BAT
     1319    // extensions (in order of CMD.EXE precedence); spawnvpe() knows how to
     1320    // locate and run them (i.e. using CMD.EXE /c)
     1321    if ( pid == -1 && errno == ENOENT ) {
     1322        appName += ".cmd";
     1323        pid = spawnvpe( P_NOWAIT | P_DEFAULT, appName.data(), argv, envv );
     1324        if ( pid == -1 && errno == ENOENT ) {
     1325            strcpy( appName.data() + appName.length() - 4, ".bat" );
     1326            pid = spawnvpe( P_NOWAIT | P_DEFAULT, appName.data(), argv, envv );
     1327        }
     1328    }
     1329   
     1330    if ( envv != environ )
     1331        delete[] envv;
     1332    delete[] argv;
     1333   
     1334#endif // defined(QT_QPROCESS_USE_DOSEXECPGM)
     1335   
    12151336    // restore the current directory
    12161337    QDir::setCurrent( curDir );
     
    12321353    UninstallQtMsgHandler();
    12331354   
     1355#if defined(QT_QPROCESS_USE_DOSEXECPGM)
     1356
    12341357    if ( rc != NO_ERROR ) {
    12351358#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
     
    12481371    // memporize PID of the started process
    12491372    d->pid = resc.codeTerminate;
     1373   
     1374#else // defined(QT_QPROCESS_USE_DOSEXECPGM)
     1375
     1376    if ( pid == -1 ) {
     1377#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
     1378        qWarning( "Failed to start a new process [%s]\n"
     1379                  "errno=%d (%s)",
     1380                  QFile::encodeName( _arguments.join( "] [" ) ).data(),
     1381                  errno, strerror( errno ) );
     1382#endif
     1383        processMonitor->removeProcess( d );
     1384        d->closeHandles();
     1385        return FALSE;
     1386    }
     1387
     1388    // memporize PID of the started process
     1389    d->pid = pid;
     1390
     1391#endif // defined(QT_QPROCESS_USE_DOSEXECPGM)
     1392
     1393#if defined(QT_QPROCESS_DEBUG)
     1394    qDebug( "QProcess::start(): started PID %lu (0x%08lX)", d->pid, d->pid );
     1395#endif
    12501396   
    12511397    // timer is not necessary for ioRedirection (we use the monitor thread)
     
    12971443        return FALSE;
    12981444
     1445#if defined(QT_QPROCESS_USE_DOSEXECPGM)
     1446   
    12991447    PID pidEnded = PID_NULL;
    13001448    RESULTCODES resc = { 0 };
     
    13051453        return TRUE;
    13061454
     1455#else // defined(QT_QPROCESS_USE_DOSEXECPGM)
     1456
     1457    int stat = 0;
     1458    int pid = waitpid( d->pid, &stat, WNOHANG );
     1459    if ( (pid == 0) ||
     1460         ((PID) pid == d->pid &&
     1461          !WIFEXITED( stat ) && !WIFSIGNALED( stat ) && !WIFSTOPPED( stat ) ) )
     1462        return TRUE;
     1463
     1464    if ( pid == -1 ) {
     1465#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
     1466        qWarning( "Failed to wait for a child process\n"
     1467                  "errno=%d (%s)", errno, strerror( errno ) );
     1468#endif
     1469    }
     1470
     1471#endif // defined(QT_QPROCESS_USE_DOSEXECPGM)
     1472   
    13071473    QProcess *that = (QProcess *) this;
    13081474
     
    13181484    // compute the exit values
    13191485    if ( !d->exitValuesCalculated ) {
     1486#if defined(QT_QPROCESS_USE_DOSEXECPGM)
    13201487        that->exitNormal = resc.codeTerminate == TC_EXIT;
    13211488        that->exitStat = resc.codeResult;
     1489#else // defined(QT_QPROCESS_USE_DOSEXECPGM)
     1490        that->exitNormal = pid != -1 && WIFEXITED( stat );
     1491        that->exitStat = WEXITSTATUS( stat );
     1492#endif // defined(QT_QPROCESS_USE_DOSEXECPGM)
    13221493        d->exitValuesCalculated = TRUE;
    13231494    }
Note: See TracChangeset for help on using the changeset viewer.