[libcalamares] another convenience for running commands

Back targetEnvCommand() with a more general runCommand()
that takes an argument selecting the location to run
the command in. This allows us also to use the same
API for running processes in the host during install,
as we do for running them in the target system.

One reason for this change is wanting to run (user-specified)
commands and independently from the global dontChroot setting,
run those commands in the live system or the target.

This changes the ABI of the DLL, since targetEnvCommand()
is no longer exported. Plugins will need to be recompiled.

 - refactor targetEnvCommand() into more general runCommand().
 - While here, allow host system commands to run even if
   there is no global storage.
 - provide convenience accessors for ProcessResult members
 - Move explanation of process errors out of ProcessJob
   - Move from ProcessJob to ProcessResult, so it can be
     reused outside of ProcessJob (e.g. from ShellProcessJob).
   - Add some convenience functions, too.
This commit is contained in:
Adriaan de Groot 2018-01-12 06:30:52 -05:00
parent 6e01bb0fa4
commit 4ff1a0d5ea
4 changed files with 108 additions and 52 deletions

View File

@ -82,7 +82,7 @@ ProcessJob::exec()
QString(),
m_timeoutSec );
return explainProcess( ec, m_command, output, m_timeoutSec );
return CalamaresUtils::ProcessResult::explainProcess( this, ec, m_command, output, m_timeoutSec );
}
@ -144,41 +144,4 @@ ProcessJob::callOutput( const QString& command,
return process.exitCode();
}
JobResult
ProcessJob::explainProcess( int ec, const QString& command, const QString& output, int timeout )
{
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( command )
.arg( output ) );
if ( ec == -2 )
return JobResult::error( tr( "External command failed to start" ),
tr( "Command %1 failed to start." )
.arg( command ) );
if ( ec == -3 )
return JobResult::error( tr( "Internal error when starting command" ),
tr( "Bad parameters for process job call." ) );
if ( ec == -4 )
return JobResult::error( tr( "External command failed to finish" ),
tr( "Command %1 failed to finish in %2s.\nOutput:\n%3" )
.arg( command )
.arg( timeout )
.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( command )
.arg( ec )
.arg( output ) );
}
} // namespace Calamares

View File

@ -39,10 +39,6 @@ public:
QString prettyStatusMessage() const override;
JobResult exec() override;
protected:
/** @brief Explain a typical external process failure. */
static JobResult explainProcess( int errorCode, const QString& command, const QString& output, int timeout );
private:
int callOutput( const QString& command,
QString& output,

View File

@ -1,7 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org>
* Copyright 2017-2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -93,7 +93,8 @@ System::mount( const QString& devicePath,
}
ProcessResult
System::targetEnvCommand(
System::runCommand(
System::RunLocation location,
const QStringList& args,
const QString& workingPath,
const QString& stdInput,
@ -105,8 +106,8 @@ System::targetEnvCommand(
return -3;
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
if ( !gs ||
( m_doChroot && !gs->contains( "rootMountPoint" ) ) )
if ( ( location == System::RunLocation::RunInTarget ) &&
( !gs || !gs->contains( "rootMountPoint" ) ) )
{
cLog() << "No rootMountPoint in global storage";
return -3;
@ -116,7 +117,7 @@ System::targetEnvCommand(
QString program;
QStringList arguments;
if ( m_doChroot )
if ( location == System::RunLocation::RunInTarget )
{
QString destDir = gs->value( "rootMountPoint" ).toString();
if ( !QDir( destDir ).exists() )
@ -249,4 +250,42 @@ System::doChroot() const
return m_doChroot;
}
Calamares::JobResult
ProcessResult::explainProcess( const QObject* parent, int ec, const QString& command, const QString& output, int timeout )
{
using Calamares::JobResult;
if ( ec == 0 )
return JobResult::ok();
if ( ec == -1 ) //Crash!
return JobResult::error( parent->tr( "External command crashed" ),
parent->tr( "Command %1 crashed.\nOutput:\n%2" )
.arg( command )
.arg( output ) );
if ( ec == -2 )
return JobResult::error( parent->tr( "External command failed to start" ),
parent->tr( "Command %1 failed to start." )
.arg( command ) );
if ( ec == -3 )
return JobResult::error( parent->tr( "Internal error when starting command" ),
parent->tr( "Bad parameters for process job call." ) );
if ( ec == -4 )
return JobResult::error( parent->tr( "External command failed to finish" ),
parent->tr( "Command %1 failed to finish in %2s.\nOutput:\n%3" )
.arg( command )
.arg( timeout )
.arg( output ) );
//Any other exit code
return JobResult::error( parent->tr( "External command finished with errors" ),
parent->tr( "Command %1 finished with exit code %2.\nOutput:\n%3" )
.arg( command )
.arg( ec )
.arg( output ) );
}
} // namespace

View File

@ -1,7 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org>
* Copyright 2017-2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,6 +21,8 @@
#include "DllMacro.h"
#include "Job.h"
#include <QObject>
#include <QPair>
#include <QString>
@ -33,6 +35,37 @@ public:
/** @brief Implicit one-argument constructor has no output, only a return code */
ProcessResult( int r ) : QPair< int, QString >( r, QString() ) {}
ProcessResult( int r, QString s ) : QPair< int, QString >( r, s ) {}
int getExitCode() const { return first; }
QString getOutput() const { return second; }
/** @brief Explain a typical external process failure.
*
* @param parent Used as context for translation calls.
* @param errorCode Return code from runCommand() or similar
* (negative values get special explanation). The member
* function uses the exit code stored in the ProcessResult
* @param output (error) output from the command, used when there is
* an error to report (exit code > 0). The member
* function uses the output stored in the ProcessResult.
* @param command String or split-up string of the command
* that was invoked.
* @param timeout Timeout passed to the process runner, for explaining
* error code -4 (timeout).
*/
static Calamares::JobResult explainProcess( const QObject* parent, int errorCode, const QString& command, const QString& output, int timeout );
/// @brief Convenience wrapper for explainProcess()
inline Calamares::JobResult explainProcess( const QObject* parent, const QString& command, int timeout ) const
{
return explainProcess( parent, getExitCode(), command, getOutput(), timeout );
}
/// @brief Convenience wrapper for explainProcess()
inline Calamares::JobResult explainProcess( const QObject* parent, const QStringList& command, int timeout ) const
{
return explainProcess( parent, getExitCode(), command.join( ' ' ), getOutput(), timeout );
}
} ;
/**
@ -71,15 +104,20 @@ public:
const QString& options = QString() );
/** (Typed) Boolean describing where a particular command should be run,
* whether in the host (live) system or in the (chroot) target system.
*/
enum class RunLocation { RunInHost, RunInTarget };
/**
* Runs the specified command in the chroot of the target system.
* @param args the command with arguments, as a string list.
* @param workingPath the current working directory for the QProcess
* call (optional).
* call (optional).
* @param stdInput the input string to send to the running process as
* standard input (optional).
* standard input (optional).
* @param timeoutSec the timeout after which the process will be
* killed (optional, default is 0 i.e. no timeout).
* killed (optional, default is 0 i.e. no timeout).
*
* @returns the program's exit code and its output (if any). Special
* exit codes (which will never have any output) are:
@ -88,12 +126,32 @@ public:
* -3 = bad arguments
* -4 = QProcess timeout
*/
DLLEXPORT ProcessResult targetEnvCommand(
static DLLEXPORT ProcessResult runCommand(
RunLocation location,
const QStringList &args,
const QString& workingPath = QString(),
const QString& stdInput = QString(),
int timeoutSec = 0 );
/** @brief Convenience wrapper for runCommand().
* Runs the command in the location specified through the boolean
* doChroot(), which is what you usually want for running commands
* during installation.
*/
inline ProcessResult targetEnvCommand(
const QStringList &args,
const QString& workingPath = QString(),
const QString& stdInput = QString(),
int timeoutSec = 0 )
{
return runCommand(
m_doChroot ? RunLocation::RunInTarget : RunLocation::RunInHost,
args,
workingPath,
stdInput,
timeoutSec );
}
/** @brief Convenience wrapper for targetEnvCommand() which returns only the exit code */
inline int targetEnvCall( const QStringList& args,
const QString& workingPath = QString(),