QtPass 2.0.0
Developer documentation
Loading...
Searching...
No Matches
Executor Class Reference

Runs non-interactive external commands in FIFO order using a single QProcess. More...

#include <executor.h>

Inheritance diagram for Executor:
Collaboration diagram for Executor:

Classes

struct  ExecQueueItem
 Execution queue item for non-interactive ordered execution. More...
struct  WslCommand
 A configured executable that runs a Linux program through WSL. More...

Signals

void drained ()
 Emitted once the last queued item is done and nothing runs or waits any more (isIdle()), after that item's finished() or error().
void finished (int id, int exitCode, const QString &output, const QString &errout)
 Emitted when a queued process exits normally, whatever its exit code; a non-zero code is the receiver's to interpret (Pass does).
void starting ()
 Emitted just before a queued process is started; the main window shows the busy indicator on it.
void dequeuing (int id, bool completes)
 Emitted, synchronously, when the queue's head leaves it: just before it is started, when it cannot be (no executable, a failed start), or when cancelNext() drops it. Lets a caller do per-item work at the moment the item actually runs, not when it was queued.
void error (int id, int exitCode, const QString &output, const QString &errout)
 Emitted instead of finished() when a queued process could not be started, had no executable configured, or crashed.

Public Member Functions

 Executor (QObject *parent=nullptr)
 Construct an Executor with an optional parent QObject.
void execute (int id, const QString &app, const QStringList &args, bool readStdout, bool readStderr=true)
 Queue a command without stdin input.
void execute (int id, const QString &workDir, const QString &app, const QStringList &args, bool readStdout, bool readStderr=true)
 Queue a command with an explicit working directory.
void execute (int id, const QString &app, const QStringList &args, QString input=QString(), bool readStdout=false, bool readStderr=true)
 Queue a command with optional stdin input.
void execute (int id, const QString &workDir, const QString &app, const QStringList &args, QString input=QString(), bool readStdout=false, bool readStderr=true)
 Queue a command with working directory and optional stdin input.
void setEnvironment (const QProcessEnvironment &env)
 Set the environment passed to all child processes.
auto environment () const -> QProcessEnvironment
 Return the environment passed to child processes.
auto cancelNext () -> int
 Cancel the next queued command without executing it.
auto isIdle () const -> bool
 Whether no command is running and nothing is queued.
auto hasQueued (int id) const -> bool
 Whether a command with process id id is queued or running.

Static Public Member Functions

static auto executeBlocking (const QString &app, const QStringList &args, const QString &input=QString(), QString *process_out=nullptr, QString *process_err=nullptr) -> int
 Run a command synchronously and return its exit code.
static auto executeBlocking (QProcess &process, const QString &app, const QStringList &args, const QString &input=QString(), QString *process_out=nullptr, QString *process_err=nullptr, const std::atomic_bool *cancel=nullptr, int timeoutMs=-1) -> int
 Run a command synchronously on a caller-supplied QProcess, with optional cancellation.
static auto executeBlocking (const QString &app, const QStringList &args, QString *process_out, QString *process_err=nullptr) -> int
 Run a command synchronously capturing stdout and stderr.
static auto executeBlocking (const QProcessEnvironment &env, const QString &app, const QStringList &args, QString *process_out=nullptr, QString *process_err=nullptr) -> int
 Run a command synchronously with a custom environment.
static auto resolveExecutable (const QString &app) -> QString
 The one rule for turning a configured executable into what runs.
static void detachFromTerminal (QProcess &process)
 Make process start its child without a controlling terminal.
static void setChildUmask (int mask)
 The umask every child QtPass starts gets, as pass sets its own.
static auto childUmask () -> int
 The umask setChildUmask() set.
static auto parseWslCommand (const QString &app) -> std::optional< WslCommand >
 Parse a configured executable as a WSL command.
static auto wslExecArgs (const QString &command, const QStringList &args) -> QStringList
 Build the wsl.exe argument list that runs command directly.
static auto translatePathForWsl (const QString &path, const QString &exe) -> QString
 Translate a native path for a WSL-routed executable.

Private Slots

void onProcessFinished (int exitCode, QProcess::ExitStatus exitStatus)
 Finish the running item when its process exits.

Private Member Functions

void executeNext ()
 Start the head of the queue unless something runs or the queue is empty.
void postDrainedIfIdle ()
auto collectOutput (const ExecQueueItem &item, int exitCode) -> std::pair< QString, QString >
 Read what a finished process produced, according to the queue item's readStdout/readStderr flags; stderr is always read on a non-zero exit so the caller can report it.

Static Private Member Functions

static void startProcess (QProcess &process, const QString &app, const QStringList &args)
 Start process running app, handling the "wsl " prefix once for both the queued and the blocking path.
static auto runBlocking (QProcess &process, const QString &app, const QStringList &args, const QString &input, QString *process_out, QString *process_err, const std::atomic_bool *cancel=nullptr, int timeoutMs=-1) -> int
 Shared blocking run: start process, optionally feed input on stdin, wait, and capture stdout/stderr. Backs the executeBlocking overloads. The caller configures process (e.g. environment) first.

Private Attributes

QQueue< ExecQueueItem > m_execQueue
 Commands waiting to run, head first. The head stays queued while it runs and is dequeued in onProcessFinished().
QProcess m_process
 The one process object; every queued command reuses it.
bool running {false}
 Whether the head of the queue has been started (or is being dequeued, see dequeuing()) and has not finished yet.
int m_pendingErrors = 0
 error()s of failed starts posted but not delivered yet.
bool m_drainCheckPosted = false
 A drained() check is posted and not run yet.

Detailed Description

Runs non-interactive external commands in FIFO order using a single QProcess.

Queues execution requests and emits per-request completion or error signals; supports optional stdin, configurable stdout/stderr capture, and per-request working directory and environment.

Definition at line 31 of file executor.h.

Constructor & Destructor Documentation

◆ Executor()

Executor::Executor ( QObject * parent = nullptr)
explicit

Construct an Executor with an optional parent QObject.

Parameters
parentParent QObject, or nullptr for no parent.

Definition at line 29 of file executor.cpp.

Here is the call graph for this function:

Member Function Documentation

◆ cancelNext()

auto Executor::cancelNext ( ) -> int

Cancel the next queued command without executing it.

Returns
The id of the cancelled command, or -1 if the queue was empty.

Definition at line 462 of file executor.cpp.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ childUmask()

auto Executor::childUmask ( ) -> int
staticnodiscard

The umask setChildUmask() set.

Returns
The umask, or a negative value when none was set.

Definition at line 67 of file executor.cpp.

Here is the caller graph for this function:

◆ collectOutput()

auto Executor::collectOutput ( const ExecQueueItem & item,
int exitCode ) -> std::pair< QString, QString >
private

Read what a finished process produced, according to the queue item's readStdout/readStderr flags; stderr is always read on a non-zero exit so the caller can report it.

Parameters
itemThe queue item the process ran for.
exitCodeIts exit code.
Returns
stdout and stderr, each empty when not read.

Definition at line 522 of file executor.cpp.

Here is the caller graph for this function:

◆ dequeuing

void Executor::dequeuing ( int id,
bool completes )
signal

Emitted, synchronously, when the queue's head leaves it: just before it is started, when it cannot be (no executable, a failed start), or when cancelNext() drops it. Lets a caller do per-item work at the moment the item actually runs, not when it was queued.

Parameters
idThe item's id.
completesWhether finished() or error() follows for it: for every item but one cancelNext() dropped. Those that complete do so in the order they left the queue.

A slot may queue more with execute(): that waits behind this item. The executor counts as running while the signal is out, also for an item cancelNext() drops, so cancelNext() drops nothing then (-1) and nothing starts until the signal has returned.

Here is the caller graph for this function:

◆ detachFromTerminal()

void Executor::detachFromTerminal ( QProcess & process)
static

Make process start its child without a controlling terminal.

On Unix the child starts in a new session (setsid), so it has no /dev/tty. git and ssh then use an askpass helper when there is one and fail at once otherwise, instead of prompting in the terminal QtPass was started from while the queue waits behind them. Other Unix process flags set on process are kept. Does nothing on Windows. Every child QtPass starts goes through this; startProcess() calls it for the queued and the blocking path.

Parameters
processProcess to configure; call it before start().

Definition at line 34 of file executor.cpp.

Here is the caller graph for this function:

◆ drained

void Executor::drained ( )
signal

Emitted once the last queued item is done and nothing runs or waits any more (isIdle()), after that item's finished() or error().

Here is the caller graph for this function:

◆ environment()

auto Executor::environment ( ) const -> QProcessEnvironment

Return the environment passed to child processes.

Returns
Process environment.

Definition at line 457 of file executor.cpp.

Here is the caller graph for this function:

◆ error

void Executor::error ( int id,
int exitCode,
const QString & output,
const QString & errout )
signal

Emitted instead of finished() when a queued process could not be started, had no executable configured, or crashed.

Parameters
idCaller-assigned id the item was queued with.
exitCodeAlways -1; the real code is unknown in these cases.
outputstdout read so far, when the item asked for it.
erroutstderr, or a message saying what went wrong when the process left none.
Here is the caller graph for this function:

◆ execute() [1/4]

void Executor::execute ( int id,
const QString & app,
const QStringList & args,
bool readStdout,
bool readStderr = true )

Queue a command without stdin input.

Parameters
idCaller-assigned process identifier.
appExecutable path.
argsCommand arguments.
readStdoutWhether to capture stdout.
readStderrWhether to capture stderr.

Definition at line 264 of file executor.cpp.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ execute() [2/4]

void Executor::execute ( int id,
const QString & app,
const QStringList & args,
QString input = QString(),
bool readStdout = false,
bool readStderr = true )

Queue a command with optional stdin input.

Parameters
idCaller-assigned process identifier.
appExecutable path.
argsCommand arguments.
inputData to write to stdin.
readStdoutWhether to capture stdout.
readStderrWhether to capture stderr.

Definition at line 275 of file executor.cpp.

Here is the call graph for this function:

◆ execute() [3/4]

void Executor::execute ( int id,
const QString & workDir,
const QString & app,
const QStringList & args,
bool readStdout,
bool readStderr = true )

Queue a command with an explicit working directory.

Parameters
idCaller-assigned process identifier.
workDirWorking directory for the process.
appExecutable path.
argsCommand arguments.
readStdoutWhether to capture stdout.
readStderrWhether to capture stderr.

Definition at line 269 of file executor.cpp.

Here is the call graph for this function:

◆ execute() [4/4]

void Executor::execute ( int id,
const QString & workDir,
const QString & app,
const QStringList & args,
QString input = QString(),
bool readStdout = false,
bool readStderr = true )

Queue a command with working directory and optional stdin input.

Parameters
idCaller-assigned process identifier.
workDirWorking directory for the process.
appExecutable path.
argsCommand arguments.
inputData to write to stdin.
readStdoutWhether to capture stdout.
readStderrWhether to capture stderr.

Definition at line 280 of file executor.cpp.

Here is the call graph for this function:

◆ executeBlocking() [1/4]

auto Executor::executeBlocking ( const QProcessEnvironment & env,
const QString & app,
const QStringList & args,
QString * process_out = nullptr,
QString * process_err = nullptr ) -> int
staticnodiscard

Run a command synchronously with a custom environment.

Parameters
envProcess environment for the child process.
appExecutable path.
argsCommand arguments.
process_outIf non-null, receives stdout output.
process_errIf non-null, receives stderr output.
Returns
Process exit code.

Definition at line 444 of file executor.cpp.

Here is the call graph for this function:

◆ executeBlocking() [2/4]

auto Executor::executeBlocking ( const QString & app,
const QStringList & args,
const QString & input = QString(),
QString * process_out = nullptr,
QString * process_err = nullptr ) -> int
staticnodiscard

Run a command synchronously and return its exit code.

Parameters
appExecutable path.
argsCommand arguments.
inputData to write to stdin.
process_outIf non-null, receives stdout output.
process_errIf non-null, receives stderr output.
Returns
Process exit code.

Definition at line 422 of file executor.cpp.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ executeBlocking() [3/4]

auto Executor::executeBlocking ( const QString & app,
const QStringList & args,
QString * process_out,
QString * process_err = nullptr ) -> int
staticnodiscard

Run a command synchronously capturing stdout and stderr.

Parameters
appExecutable path.
argsCommand arguments.
process_outReceives stdout output.
process_errIf non-null, receives stderr output.
Returns
Process exit code.

Definition at line 438 of file executor.cpp.

Here is the call graph for this function:

◆ executeBlocking() [4/4]

auto Executor::executeBlocking ( QProcess & process,
const QString & app,
const QStringList & args,
const QString & input = QString(),
QString * process_out = nullptr,
QString * process_err = nullptr,
const std::atomic_bool * cancel = nullptr,
int timeoutMs = -1 ) -> int
staticnodiscard

Run a command synchronously on a caller-supplied QProcess, with optional cancellation.

The caller configures process (environment, say) before the call; it must be a fresh, not yet started object. QProcess is not thread-safe, so process belongs to the calling thread for the whole run: no other thread may call terminate(), kill() or anything else on it. To let a run be cancelled, pass cancel: the wait then polls the flag, and once it is set the calling thread itself terminate()s the child, waits a short grace period, kill()s it if it is still running, waits one more grace period at most, and returns -1. Setting the flag is the only thing another thread does; it may do so at any time, before or during the run. A run given timeoutMs ends the same way once that time has passed, start included, so it returns within the timeout plus twice the grace even when a kill()ed child cannot exit yet (uninterruptible sleep on a hung mount). Not timed: QProcess::start() itself, which can block, for example opening a FIFO named by a redirected channel (no caller redirects one), and QProcess's destructor, which still waits for such a child, for up to 30 s.

Parameters
processProcess object to run the command on.
appExecutable path.
argsCommand arguments.
inputData to write to stdin.
process_outIf non-null, receives stdout output.
process_errIf non-null, receives stderr output.
cancelIf non-null, a flag that ends the run when set.
timeoutMsTime for the whole run, start included; negative (the default) for none.
Returns
Process exit code, or -1 if it failed to start, crashed, was cancelled or ran out of time.

Definition at line 429 of file executor.cpp.

Here is the call graph for this function:

◆ executeNext()

void Executor::executeNext ( )
private

Start the head of the queue unless something runs or the queue is empty.

Marks the executor running and emits dequeuing() before touching the item, so a slot there cannot invalidate it. An item with no executable is failed with a queued error() (an empty command used to wedge the queue, #1682) and the next one is tried at once. Called after each execute() and from onProcessFinished().

Definition at line 192 of file executor.cpp.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ finished

void Executor::finished ( int id,
int exitCode,
const QString & output,
const QString & errout )
signal

Emitted when a queued process exits normally, whatever its exit code; a non-zero code is the receiver's to interpret (Pass does).

Parameters
idCaller-assigned id the item was queued with.
exitCodeExit code of the process.
outputstdout, when the item asked for it; empty otherwise.
erroutstderr, when the item asked for it or the exit code is non-zero.
Here is the caller graph for this function:

◆ hasQueued()

auto Executor::hasQueued ( int id) const -> bool
inline

Whether a command with process id id is queued or running.

The running command stays at the head of the queue until it finishes, so one scan covers both the queued and the running one. Used to refuse a store change while a Git pull is still in flight, whose conflict a call-time check could not yet see.

Parameters
idThe process id to look for (an Enums::PROCESS value).
Returns
true when the queue holds a command with that id.

Definition at line 437 of file executor.h.

◆ isIdle()

auto Executor::isIdle ( ) const -> bool
inline

Whether no command is running and nothing is queued.

Returns
true when the executor has nothing in flight.

Definition at line 425 of file executor.h.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ onProcessFinished

void Executor::onProcessFinished ( int exitCode,
QProcess::ExitStatus exitStatus )
privateslot

Finish the running item when its process exits.

Dequeues the head, collects its output and emits finished() for a normal exit or error() with exit code -1 for a crash (Qt leaves the code undefined then, and 0 or 1 would be mistaken for success or "no matches"), then starts the next item and posts a drained() check.

Parameters
exitCodeExit code reported by QProcess.
exitStatusWhether the process exited normally or crashed.

Definition at line 478 of file executor.cpp.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parseWslCommand()

auto Executor::parseWslCommand ( const QString & app) -> std::optional< WslCommand >
static

Parse a configured executable as a WSL command.

Accepts wsl or wsl.exe in any case, bare or as a path, followed by wsl.exe options and exactly one program: wsl gpg, WSL.EXE -d Debian /usr/bin/gpg, C:\Windows\System32\wsl.exe -u me gpg. A -e/--exec the user wrote is dropped (argv() adds it), anything after the program (wsl sh -c "gpg") or a wsl with no program is not a WSL command.

Every place that starts, probes or translates paths for a configured executable goes through this, so a form one of them accepts cannot be started differently by another.

Parameters
appExecutable as configured.
Returns
The parsed command, or nothing when app is not one.

Definition at line 114 of file executor.cpp.

Here is the caller graph for this function:

◆ postDrainedIfIdle()

void Executor::postDrainedIfIdle ( )
private

Post one check for drained(), coalesced: it comes after what is being emitted now, and fires only when the executor is idle and no error() of a failed start is still on its way.

Definition at line 503 of file executor.cpp.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ resolveExecutable()

auto Executor::resolveExecutable ( const QString & app) -> QString
static

The one rule for turning a configured executable into what runs.

Absolute paths and "wsl ..." commands are used as given. A bare or relative name is looked up next to the application first (the Windows and macOS bundles ship gpg/git/pass there) and otherwise left for QProcess's PATH lookup. Both the queued and the blocking path go through this, so the same setting can no longer run two different binaries.

Parameters
appExecutable as configured.
Returns
What to hand to QProcess::start().

Definition at line 86 of file executor.cpp.

Here is the caller graph for this function:

◆ runBlocking()

auto Executor::runBlocking ( QProcess & process,
const QString & app,
const QStringList & args,
const QString & input,
QString * process_out,
QString * process_err,
const std::atomic_bool * cancel = nullptr,
int timeoutMs = -1 ) -> int
staticprivate

Shared blocking run: start process, optionally feed input on stdin, wait, and capture stdout/stderr. Backs the executeBlocking overloads. The caller configures process (e.g. environment) first.

With a null cancel the wait is a plain waitForFinished(-1). With a non-null one the wait polls the flag; once it is set this thread terminate()s the child, gives it a short grace period, kill()s it if it is still running, waits one more grace period at most, and returns -1. A flag that is already set skips the start altogether.

Parameters
processPre-configured QProcess to run.
appExecutable path.
argsCommand arguments.
inputData to write to stdin (empty = none).
process_outIf non-null, receives stdout.
process_errIf non-null, receives stderr.
cancelOptional flag another thread may set to end the run.
timeoutMsTime for the whole run, start included; negative for none.
Returns
Process exit code, or -1 on start/crash failure, cancel or timeout.

Definition at line 382 of file executor.cpp.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ setChildUmask()

void Executor::setChildUmask ( int mask)
static

The umask every child QtPass starts gets, as pass sets its own.

pass runs git and gpg under umask 077 (or PASSWORD_STORE_UMASK), so the folders and files they create in the store, .git included, are the user's alone. QtPass gives that umask to the processes it starts (startProcess(), the queue and the blocking calls) and not to itself: a browser or file manager it opens keeps the session's. Unix only.

Parameters
maskThe umask, or a negative value for none (the default).

Definition at line 65 of file executor.cpp.

Here is the caller graph for this function:

◆ setEnvironment()

void Executor::setEnvironment ( const QProcessEnvironment & env)

Set the environment passed to all child processes.

Parameters
envProcess environment for child processes.

Definition at line 453 of file executor.cpp.

Here is the caller graph for this function:

◆ starting

void Executor::starting ( )
signal

Emitted just before a queued process is started; the main window shows the busy indicator on it.

Here is the caller graph for this function:

◆ startProcess()

void Executor::startProcess ( QProcess & process,
const QString & app,
const QStringList & args )
staticprivate

Start process running app, handling the "wsl " prefix once for both the queued and the blocking path.

Also applies detachFromTerminal() and the umask from setChildUmask(), so every child QtPass starts gets both.

Parameters
processProcess object to start; configured by the caller.
appExecutable as configured (see resolveExecutable() and parseWslCommand()).
argsCommand arguments.

Definition at line 69 of file executor.cpp.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ translatePathForWsl()

auto Executor::translatePathForWsl ( const QString & path,
const QString & exe ) -> QString
static

Translate a native path for a WSL-routed executable.

When exe is a WSL command (parseWslCommand()) the path is converted with wslpath in the same distribution (the Linux binary cannot open C:\...); otherwise it is only cleaned.

Parameters
pathNative filesystem path.
exeExecutable the path is meant for.
Returns
Path suitable for exe.

Definition at line 177 of file executor.cpp.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ wslExecArgs()

auto Executor::wslExecArgs ( const QString & command,
const QStringList & args ) -> QStringList
static

Build the wsl.exe argument list that runs command directly.

Parameters
commandLinux command to run (looked up on the WSL PATH).
argsArguments for command, passed through verbatim.
Returns
Arguments for wsl: --exec, command, then args.
See also
WslCommand::argv

Definition at line 172 of file executor.cpp.

Here is the call graph for this function:

Member Data Documentation

◆ m_drainCheckPosted

bool Executor::m_drainCheckPosted = false
private

A drained() check is posted and not run yet.

Definition at line 88 of file executor.h.

◆ m_execQueue

QQueue<ExecQueueItem> Executor::m_execQueue
private

Commands waiting to run, head first. The head stays queued while it runs and is dequeued in onProcessFinished().

Definition at line 62 of file executor.h.

◆ m_pendingErrors

int Executor::m_pendingErrors = 0
private

error()s of failed starts posted but not delivered yet.

Definition at line 86 of file executor.h.

◆ m_process

QProcess Executor::m_process
private

The one process object; every queued command reuses it.

Definition at line 64 of file executor.h.

◆ running

bool Executor::running {false}
private

Whether the head of the queue has been started (or is being dequeued, see dequeuing()) and has not finished yet.

Definition at line 69 of file executor.h.


The documentation for this class was generated from the following files: