From 7a3ce363b3ee147e7ba1067f16f0ee6312dd0d9c Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Tue, 12 Aug 2014 14:26:10 +0200 Subject: [PATCH] Add option to run process jobmodules in chroot. --- src/libcalamares/ProcessJob.cpp | 131 ++++++++++++++---- src/libcalamares/ProcessJob.h | 7 + src/libcalamares/PythonJobApi.cpp | 4 + .../utils/CalamaresUtilsSystem.cpp | 17 ++- src/libcalamares/utils/CalamaresUtilsSystem.h | 4 + .../modulesystem/ProcessJobModule.cpp | 7 + .../modulesystem/ProcessJobModule.h | 1 + src/modules/dummyprocess/module.desc | 1 + 8 files changed, 142 insertions(+), 30 deletions(-) diff --git a/src/libcalamares/ProcessJob.cpp b/src/libcalamares/ProcessJob.cpp index 8a432c6ff..a63af76bb 100644 --- a/src/libcalamares/ProcessJob.cpp +++ b/src/libcalamares/ProcessJob.cpp @@ -18,6 +18,10 @@ #include "ProcessJob.h" +#include "utils/CalamaresUtilsSystem.h" +#include "utils/Logger.h" + +#include #include namespace Calamares { @@ -25,11 +29,13 @@ namespace Calamares { ProcessJob::ProcessJob( const QString& command, const QString& workingPath, + bool runInChroot, int secondsTimeout, QObject* parent ) : Job( parent ) , m_command( command ) , m_workingPath( workingPath ) + , m_runInChroot( runInChroot ) , m_timeoutSec( secondsTimeout ) {} @@ -49,42 +55,109 @@ ProcessJob::prettyName() const JobResult ProcessJob::exec() { - QProcess p; - p.setProcessChannelMode( QProcess::MergedChannels ); - p.setWorkingDirectory( m_workingPath ); - p.start( m_command ); + int ec = 0; + QString output; + if ( m_runInChroot ) + ec = CalamaresUtils::chrootOutput( m_command, + output, + m_workingPath, + QString(), + m_timeoutSec ); + else + ec = callOutput( m_command, + output, + m_workingPath, + QString(), + m_timeoutSec ); - // It's ok to block here because JobQueue runs this method in a separate thread. - if ( !p.waitForStarted() ) + if ( ec == 0 ) + return JobResult::ok(); + + if ( ec == -1 ) //Crash! + return JobResult::error( tr( "External command crashed" ), + tr( "Command %1 crashed.\nOutput:\n%2" ) + .arg( m_command ) + .arg( output ) ); + + if ( ec == -2 ) return JobResult::error( tr( "External command failed to start" ), tr( "Command %1 failed to start." ) .arg( m_command ) ); - if ( !p.waitForFinished( m_timeoutSec * 1000/*msec*/ ) ) - return JobResult::error( tr( "External command failed to finish" ), - tr( "Command %1 failed to finish in %2s." ) - .arg( m_command ) - .arg( m_timeoutSec ) ); + if ( ec == -3 ) + return JobResult::error( tr( "Internal error when starting command" ), + tr( "Bad parameters for process job call." ) ); - if ( p.exitStatus() == QProcess::NormalExit && p.exitCode() == 0 ) - { - return JobResult::ok(); - } - else if ( p.exitStatus() == QProcess::CrashExit ) - { - return JobResult::error( tr( "External command crashed" ), - tr( "Command %1 crashed.\nOutput:\n%2" ) + if ( ec == -4 ) + return JobResult::error( tr( "External command failed to finish" ), + tr( "Command %1 failed to finish in %2s.\nOutput:\n%3" ) .arg( m_command ) - .arg( QString::fromLocal8Bit( p.readAll() ) ) ); - } - else //NormalExit with non-zero exit code - { - return JobResult::error( tr( "External command finished with errors" ), - tr( "Command %1 finished with exit code %2.\nOutput:\n%3" ) - .arg( m_command ) - .arg( p.exitCode() ) - .arg( QString::fromLocal8Bit( p.readAll() ) ) ); - } + .arg( m_timeoutSec ) + .arg( output ) ); + + //Any other exit code + return JobResult::error( tr( "External command finished with errors" ), + tr( "Command %1 finished with exit code %2.\nOutput:\n%3" ) + .arg( m_command ) + .arg( ec ) + .arg( output ) ); } + +int +ProcessJob::callOutput( const QString& command, + QString& output, + const QString& workingPath, + const QString& stdInput, + int timeoutSec ) +{ + output.clear(); + + QProcess process; + process.setProgram( command ); + process.setProcessChannelMode( QProcess::MergedChannels ); + + if ( !workingPath.isEmpty() ) + { + if ( QDir( workingPath ).exists() ) + process.setWorkingDirectory( QDir( workingPath ).absolutePath() ); + else + cLog() << "Invalid working directory:" << workingPath; + return -3; + } + + cLog() << "Running" << command; + process.start(); + if ( !process.waitForStarted() ) + { + cLog() << "Process failed to start" << process.error(); + return -2; + } + + if ( !stdInput.isEmpty() ) + { + process.write( stdInput.toLocal8Bit() ); + process.closeWriteChannel(); + } + + if ( !process.waitForFinished( timeoutSec ? ( timeoutSec * 1000 ) : -1 ) ) + { + cLog() << "Timed out. output so far:"; + cLog() << process.readAllStandardOutput(); + return -4; + } + + output.append( QString::fromLocal8Bit( process.readAllStandardOutput() ).trimmed() ); + + if ( process.exitStatus() == QProcess::CrashExit ) + { + cLog() << "Process crashed"; + return -1; + } + + cLog() << "Finished. Exit code:" << process.exitCode(); + return process.exitCode(); +} + + } // namespace Calamares diff --git a/src/libcalamares/ProcessJob.h b/src/libcalamares/ProcessJob.h index 808b65b9c..d3b51557f 100644 --- a/src/libcalamares/ProcessJob.h +++ b/src/libcalamares/ProcessJob.h @@ -29,6 +29,7 @@ class ProcessJob : public Job public: explicit ProcessJob( const QString& command, const QString& workingPath, + bool runInChroot = false, int secondsTimeout = 30, QObject* parent = nullptr ); virtual ~ProcessJob(); @@ -37,8 +38,14 @@ public: JobResult exec() override; private: + int callOutput( const QString& command, + QString& output, + const QString& workingPath = QString(), + const QString& stdInput = QString(), + int timeoutSec = 0 ); QString m_command; QString m_workingPath; + bool m_runInChroot; int m_timeoutSec; }; diff --git a/src/libcalamares/PythonJobApi.cpp b/src/libcalamares/PythonJobApi.cpp index beab1513a..26fdaaf7f 100644 --- a/src/libcalamares/PythonJobApi.cpp +++ b/src/libcalamares/PythonJobApi.cpp @@ -51,6 +51,7 @@ chroot_call( const std::string& command, int timeout ) { return CalamaresUtils::chrootCall( QString::fromStdString( command ), + QString(), QString::fromStdString( stdin ), timeout ); } @@ -69,6 +70,7 @@ chroot_call( const bp::list& args, } return CalamaresUtils::chrootCall( list, + QString(), QString::fromStdString( stdin ), timeout ); } @@ -112,6 +114,7 @@ check_chroot_output( const std::string& command, QString output; int ec = CalamaresUtils::chrootOutput( QString::fromStdString( command ), output, + QString(), QString::fromStdString( stdin ), timeout ); _handle_check_chroot_call_error( ec, QString::fromStdString( command ) ); @@ -134,6 +137,7 @@ check_chroot_output( const bp::list& args, int ec = CalamaresUtils::chrootOutput( list, output, + QString(), QString::fromStdString( stdin ), timeout ); _handle_check_chroot_call_error( ec, list.join( ' ' ) ); diff --git a/src/libcalamares/utils/CalamaresUtilsSystem.cpp b/src/libcalamares/utils/CalamaresUtilsSystem.cpp index ce958a3a8..35f3647cb 100644 --- a/src/libcalamares/utils/CalamaresUtilsSystem.cpp +++ b/src/libcalamares/utils/CalamaresUtilsSystem.cpp @@ -59,12 +59,14 @@ mount( const QString& devicePath, int chrootCall( const QStringList& args, + const QString& workingPath, const QString& stdInput, int timeoutSec ) { QString discard; return chrootOutput( args, discard, + workingPath, stdInput, timeoutSec ); } @@ -72,10 +74,12 @@ chrootCall( const QStringList& args, int chrootCall( const QString& command, + const QString& workingPath, const QString& stdInput, int timeoutSec ) { return chrootCall( QStringList() = { command }, + workingPath, stdInput, timeoutSec ); } @@ -84,6 +88,7 @@ chrootCall( const QString& command, int chrootOutput( const QStringList& args, QString& output, + const QString& workingPath, const QString& stdInput, int timeoutSec ) { @@ -115,6 +120,15 @@ chrootOutput( const QStringList& args, process.setArguments( arguments ); process.setProcessChannelMode( QProcess::MergedChannels ); + if ( !workingPath.isEmpty() ) + { + if ( QDir( workingPath ).exists() ) + process.setWorkingDirectory( QDir( workingPath ).absolutePath() ); + else + cLog() << "Invalid working directory:" << workingPath; + return -3; + } + cLog() << "Running" << program << arguments; process.start(); if ( !process.waitForStarted() ) @@ -152,15 +166,16 @@ chrootOutput( const QStringList& args, int chrootOutput( const QString& command, QString& output, + const QString& workingPath, const QString& stdInput, int timeoutSec ) { return chrootOutput( QStringList() = { command }, output, + workingPath, stdInput, timeoutSec ); } - } diff --git a/src/libcalamares/utils/CalamaresUtilsSystem.h b/src/libcalamares/utils/CalamaresUtilsSystem.h index 7b8c551e9..1d3884541 100644 --- a/src/libcalamares/utils/CalamaresUtilsSystem.h +++ b/src/libcalamares/utils/CalamaresUtilsSystem.h @@ -45,20 +45,24 @@ DLLEXPORT int mount( const QString& devicePath, * -4 = QProcess timeout */ DLLEXPORT int chrootCall( const QStringList& args, + const QString& workingPath = QString(), const QString& stdInput = QString(), int timeoutSec = 0 ); DLLEXPORT int chrootCall( const QString& command, + const QString& workingPath = QString(), const QString& stdInput = QString(), int timeoutSec = 0 ); DLLEXPORT int chrootOutput( const QStringList& args, QString& output, + const QString& workingPath = QString(), const QString& stdInput = QString(), int timeoutSec = 0 ); DLLEXPORT int chrootOutput( const QString& command, QString& output, + const QString& workingPath = QString(), const QString& stdInput = QString(), int timeoutSec = 0 ); } diff --git a/src/libcalamaresui/modulesystem/ProcessJobModule.cpp b/src/libcalamaresui/modulesystem/ProcessJobModule.cpp index 902f15d82..9d80c0fc5 100644 --- a/src/libcalamaresui/modulesystem/ProcessJobModule.cpp +++ b/src/libcalamaresui/modulesystem/ProcessJobModule.cpp @@ -49,6 +49,7 @@ ProcessJobModule::loadSelf() m_job = Calamares::job_ptr( new ProcessJob( m_command, m_workingPath, + m_runInChroot, m_secondsTimeout ) ); m_loaded = true; } @@ -78,6 +79,12 @@ ProcessJobModule::initFrom( const YAML::Node& node ) { m_secondsTimeout = node[ "timeout" ].as< int >(); } + + m_runInChroot = false; + if ( node[ "chroot" ] ) + { + m_runInChroot = node[ "chroot" ].as< bool >(); + } } diff --git a/src/libcalamaresui/modulesystem/ProcessJobModule.h b/src/libcalamaresui/modulesystem/ProcessJobModule.h index e8b1851c9..692fceb61 100644 --- a/src/libcalamaresui/modulesystem/ProcessJobModule.h +++ b/src/libcalamaresui/modulesystem/ProcessJobModule.h @@ -45,6 +45,7 @@ private: QString m_command; QString m_workingPath; int m_secondsTimeout; + bool m_runInChroot; job_ptr m_job; }; diff --git a/src/modules/dummyprocess/module.desc b/src/modules/dummyprocess/module.desc index 9c534a30f..6463e7a43 100644 --- a/src/modules/dummyprocess/module.desc +++ b/src/modules/dummyprocess/module.desc @@ -4,5 +4,6 @@ type: "job" name: "dummyprocess" interface: "process" +chroot: false command: "/bin/sh -c \"touch ~/calamares-dummyprocess\"" timeout: 5