///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2021 - Equinor ASA // // ResInsight 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 3 of the License, or // (at your option) any later version. // // ResInsight 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 at // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RimProcess.h" #include "RiaLogging.h" #include "RiaPreferencesOpm.h" #include "RiaQStringFormatter.h" #include "RiaWslTools.h" #include "RimProcessMonitor.h" #include "cafPdmFieldCapability.h" #include #include #include CAF_PDM_SOURCE_INIT( RimProcess, "RimProcess" ); size_t RimProcess::m_nextProcessId = 1; //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimProcess::RimProcess( bool logStdOutErr, std::shared_ptr monitor ) : m_enableLogging( logStdOutErr ) , m_qProcess( nullptr ) { size_t defId = m_nextProcessId++; if ( monitor == nullptr ) m_monitor = std::make_shared( defId, logStdOutErr ); else { m_monitor = monitor; m_monitor->setProcessId( defId ); } CAF_PDM_InitObject( "ResInsight Process", ":/Erase.png" ); CAF_PDM_InitFieldNoDefault( &m_command, "Command", "Command" ); m_command.uiCapability()->setUiReadOnly( true ); CAF_PDM_InitField( &m_workDir, "WorkDir", caf::FilePath(), "Working Directory" ); m_command.uiCapability()->setUiReadOnly( true ); CAF_PDM_InitFieldNoDefault( &m_description, "Description", "Description" ); m_description.uiCapability()->setUiReadOnly( true ); CAF_PDM_InitField( &m_id, "ID", defId, "ID" ); m_id.uiCapability()->setUiReadOnly( true ); CAF_PDM_InitField( &m_useWsl, "UseWSL", false, "Use WSL to launch process" ); m_useWsl.uiCapability()->setUiReadOnly( true ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimProcess::~RimProcess() { } caf::PdmFieldHandle* RimProcess::userDescriptionField() { return &m_description; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimProcess::addParameter( QString paramStr ) { m_arguments << paramStr.trimmed(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimProcess::addParameters( QStringList parameterList ) { for ( int i = 0; i < parameterList.size(); i++ ) { addParameter( parameterList[i] ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimProcess::setCommand( QString cmdStr ) { m_command = cmdStr.trimmed(); QString shell = optionalCommandInterpreter(); if ( shell.isEmpty() ) return; QString preParam = optionalPreParameters(); if ( !preParam.isEmpty() ) m_arguments.append( preParam ); m_arguments.append( cmdStr.trimmed() ); m_command = shell; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimProcess::setDescription( QString desc ) { m_description = desc; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimProcess::description() const { return m_description(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::shared_ptr RimProcess::monitor() const { return m_monitor; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimProcess::command() const { return m_command(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QStringList RimProcess::parameters() const { return m_arguments; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const QStringList& RimProcess::stdOut() const { if ( m_monitor ) return m_monitor->stdOut(); return m_emptyList; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const QStringList& RimProcess::stdErr() const { if ( m_monitor ) return m_monitor->stdErr(); return m_emptyList; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimProcess::appendStdOut( QString line ) { if ( m_monitor ) m_monitor->appendStdOut( line ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimProcess::appendStdErr( QString line ) { if ( m_monitor ) m_monitor->appendStdErr( line ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t RimProcess::ID() const { return m_id(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimProcess::start( bool enableStdOut, bool enableStdErr ) { if ( !m_monitor ) return false; if ( m_qProcess != nullptr ) return false; m_qProcess = new QProcess(); QString cmd = commandLine(); if ( m_enableLogging ) { std::string wslTxt = m_useWsl() ? " using WSL" : ""; RiaLogging::info( std::format( "Start process {}{}: {}", m_id(), wslTxt, cmd ) ); } m_monitor->clearStdOutErr(); QObject::connect( m_qProcess, SIGNAL( finished( int, QProcess::ExitStatus ) ), m_monitor.get(), SLOT( finished( int, QProcess::ExitStatus ) ) ); if ( enableStdOut ) QObject::connect( m_qProcess, SIGNAL( readyReadStandardOutput() ), m_monitor.get(), SLOT( readyReadStandardOutput() ) ); if ( enableStdErr ) QObject::connect( m_qProcess, SIGNAL( readyReadStandardError() ), m_monitor.get(), SLOT( readyReadStandardError() ) ); QObject::connect( m_qProcess, SIGNAL( started() ), m_monitor.get(), SLOT( started() ) ); QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); for ( auto& [key, val] : m_environmentVariables ) { env.insert( key, val ); } m_qProcess->setProcessEnvironment( env ); if ( !m_workDir().path().isEmpty() ) { m_qProcess->setWorkingDirectory( m_workDir().path() ); } if ( m_useWsl() ) { m_arguments.prepend( m_command() ); m_arguments = RiaPreferencesOpm::current()->wslOptions() + m_arguments; m_command = RiaWslTools::wslCommand(); } m_qProcess->start( m_command, m_arguments ); if ( !m_qProcess->waitForStarted( -1 ) ) { if ( m_enableLogging ) { auto error = m_qProcess->errorString(); RiaLogging::error( std::format( "Failed to start process {}. {}.", m_id(), error ) ); } return false; } return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimProcess::writeToStdIn( QStringList text ) { if ( m_qProcess == nullptr ) return; for ( auto& line : text ) { m_qProcess->write( line.toUtf8() + "\n" ); m_qProcess->waitForBytesWritten(); m_qProcess->closeWriteChannel(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimProcess::cleanUpAfterRun() { if ( m_qProcess != nullptr ) { m_qProcess->deleteLater(); m_qProcess = nullptr; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimProcess::terminate() { if ( m_qProcess != nullptr ) { if ( m_qProcess->state() == QProcess::Running ) { m_qProcess->kill(); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimProcess::notifyErrorFinish() { if ( m_monitor ) m_monitor->finished( -1, QProcess::CrashExit ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimProcess::execute( bool enableStdOut, bool enableStdErr ) { if ( !start( enableStdOut, enableStdErr ) ) return false; while ( !m_qProcess->waitForFinished( 250 ) ) { QApplication::processEvents( QEventLoop::ExcludeUserInputEvents ); } bool retval = ( m_qProcess->exitCode() == 0 ); cleanUpAfterRun(); return retval; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimProcess::optionalCommandInterpreter() const { if ( m_command.value().isNull() ) return ""; if ( isWindowsBatchFile() ) { return "cmd.exe"; } if ( m_command.value().endsWith( ".sh", Qt::CaseInsensitive ) ) { return "bash"; } if ( m_command.value().endsWith( ".csh", Qt::CaseInsensitive ) ) { return "csh"; } return ""; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimProcess::optionalPreParameters() const { if ( m_command.value().isNull() ) return ""; if ( isWindowsBatchFile() ) { return "/c"; } return ""; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimProcess::isWindowsBatchFile() const { return ( m_command.value().endsWith( ".cmd", Qt::CaseInsensitive ) || m_command.value().endsWith( ".bat", Qt::CaseInsensitive ) ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimProcess::commandLine() const { QString cmdline = handleSpaces( m_command ); for ( int i = 0; i < m_arguments.size(); i++ ) { cmdline += " "; cmdline += handleSpaces( m_arguments[i] ); } return cmdline; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimProcess::handleSpaces( QString arg ) const { if ( arg.contains( " " ) && !arg.startsWith( "\"" ) ) { return QString( "\"" + arg + "\"" ); } return arg; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimProcess::addEnvironmentVariable( QString name, QString value ) { m_environmentVariables.push_back( std::make_pair( name, value ) ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector>& RimProcess::environmentVariables() const { return m_environmentVariables; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimProcess::setWorkingDirectory( QString workDir ) { m_workDir = workDir; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimProcess::workingDirectory() const { return m_workDir().path(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimProcess::useWsl() const { return m_useWsl(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimProcess::setUseWsl( bool useWsl ) { m_useWsl = useWsl; }