QtPass 1.5.1
Multi-platform GUI for pass, the standard unix password manager.
Loading...
Searching...
No Matches
executor.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2016 Anne Jan Brouwer
2// SPDX-License-Identifier: GPL-3.0-or-later
3#include "executor.h"
4#include <QCoreApplication>
5#include <QDir>
6#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
7#include <QTextCodec>
8#endif
9#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
10#include <QStringDecoder>
11#include <utility>
12#endif
13
14#ifdef QT_DEBUG
15#include "debughelper.h"
16#endif
17
22Executor::Executor(QObject *parent) : QObject(parent), running(false) {
23 connect(&m_process,
24 static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(
25 &QProcess::finished),
26 this,
27 static_cast<void (Executor::*)(int, QProcess::ExitStatus)>(
28 &Executor::finished));
29 connect(&m_process, &QProcess::started, this, &Executor::starting);
30}
31
35void Executor::executeNext() {
36 if (!running) {
37 if (!m_execQueue.isEmpty()) {
38 const execQueueItem &i = m_execQueue.head();
39 running = true;
40 if (!i.workingDir.isEmpty()) {
41 m_process.setWorkingDirectory(i.workingDir);
42 }
43 if (i.app.startsWith("wsl ")) {
44 QStringList tmp = i.args;
45 QString app = i.app;
46 tmp.prepend(app.remove(0, 4));
47 m_process.start("wsl", tmp);
48 } else {
49 m_process.start(i.app, i.args);
50 }
51 if (!i.input.isEmpty()) {
52 m_process.waitForStarted(-1);
53 QByteArray data = i.input.toUtf8();
54 if (m_process.write(data) != data.length()) {
55#ifdef QT_DEBUG
56 dbg() << "Not all data written to process:" << i.id << " " << i.app;
57#endif
58 }
59 }
60 m_process.closeWriteChannel();
61 }
62 }
63}
64
73void Executor::execute(int id, const QString &app, const QStringList &args,
74 bool readStdout, bool readStderr) {
75 execute(id, QString(), app, args, QString(), readStdout, readStderr);
76}
77
87void Executor::execute(int id, const QString &workDir, const QString &app,
88 const QStringList &args, bool readStdout,
89 bool readStderr) {
90 execute(id, workDir, app, args, QString(), readStdout, readStderr);
91}
92
102void Executor::execute(int id, const QString &app, const QStringList &args,
103 QString input, bool readStdout, bool readStderr) {
104 execute(id, QString(), app, args, std::move(input), readStdout, readStderr);
105}
106
118void Executor::execute(int id, const QString &workDir, const QString &app,
119 const QStringList &args, QString input, bool readStdout,
120 bool readStderr) {
121 // Happens a lot if e.g. git binary is not set.
122 // This will result in bogus "QProcess::FailedToStart" messages,
123 // also hiding legitimate errors from the gpg commands.
124 if (app.isEmpty()) {
125#ifdef QT_DEBUG
126 dbg() << "Trying to execute nothing...";
127#endif
128 return;
129 }
130 QString appPath = app;
131 if (!appPath.startsWith("wsl ")) {
132 appPath =
133 QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(app);
134 }
135 m_execQueue.push_back(
136 {id, appPath, args, std::move(input), readStdout, readStderr, workDir});
137 executeNext();
138}
139
150static auto decodeAssumingUtf8(const QByteArray &in) -> QString {
151#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
152 QTextCodec *codec = QTextCodec::codecForName("UTF-8");
153 QTextCodec::ConverterState state;
154 QString out = codec->toUnicode(in.constData(), in.size(), &state);
155 if (!state.invalidChars)
156 return out;
157 codec = QTextCodec::codecForUtfText(in);
158 return codec->toUnicode(in);
159#else
160 auto converter = QStringDecoder(QStringDecoder::Utf8);
161 return converter(in);
162#endif
163}
164
177auto Executor::executeBlocking(QString app, const QStringList &args,
178 const QString &input, QString *process_out,
179 QString *process_err) -> int {
180 QProcess internal;
181 if (app.startsWith("wsl ")) {
182 QStringList tmp = args;
183 tmp.prepend(app.remove(0, 4));
184 internal.start("wsl", tmp);
185 } else {
186 internal.start(app, args);
187 }
188 if (!input.isEmpty()) {
189 QByteArray data = input.toUtf8();
190 internal.waitForStarted(-1);
191 if (internal.write(data) != data.length()) {
192#ifdef QT_DEBUG
193 dbg() << "Not all input written:" << app;
194#endif
195 }
196 internal.closeWriteChannel();
197 }
198 internal.waitForFinished(-1);
199 if (internal.exitStatus() == QProcess::NormalExit) {
200 QString pout = decodeAssumingUtf8(internal.readAllStandardOutput());
201 QString perr = decodeAssumingUtf8(internal.readAllStandardError());
202 if (process_out != nullptr) {
203 *process_out = pout;
204 }
205 if (process_err != nullptr) {
206 *process_err = perr;
207 }
208 return internal.exitCode();
209 }
210 // TODO(bezet): emit error() ?
211 return -1; // QProcess error code + qDebug error?
212}
213
222auto Executor::executeBlocking(QString app, const QStringList &args,
223 QString *process_out, QString *process_err)
224 -> int {
225 return executeBlocking(std::move(app), args, QString(), process_out,
226 process_err);
227}
228
234void Executor::setEnvironment(const QStringList &env) {
235 m_process.setEnvironment(env);
236}
237
244auto Executor::cancelNext() -> int {
245 if (running || m_execQueue.isEmpty()) {
246 return -1; // TODO(bezet): definitely throw here
247 }
248 return m_execQueue.dequeue().id;
249}
250
256void Executor::finished(int exitCode, QProcess::ExitStatus exitStatus) {
257 execQueueItem i = m_execQueue.dequeue();
258 running = false;
259 if (exitStatus == QProcess::NormalExit) {
260 QString output;
261 QString err;
262 if (i.readStdout) {
263 output = decodeAssumingUtf8(m_process.readAllStandardOutput());
264 }
265 if (i.readStderr || exitCode != 0) {
266 err = decodeAssumingUtf8(m_process.readAllStandardError());
267 if (exitCode != 0) {
268#ifdef QT_DEBUG
269 dbg() << exitCode << err;
270#endif
271 }
272 }
273 emit finished(i.id, exitCode, output, err);
274 }
275 // else: emit crashed with ID, which may give a chance to recover ?
276 executeNext();
277}
Executes external commands for handleing password, git and other data.
Definition executor.h:14
Executor(QObject *parent=0)
Executor::Executor executes external applications.
Definition executor.cpp:22
static auto executeBlocking(QString app, const QStringList &args, const QString &input=QString(), QString *process_out=nullptr, QString *process_err=nullptr) -> int
Executor::executeBlocking blocking version of the executor, takes input and presents it as stdin.
Definition executor.cpp:177
void execute(int id, const QString &app, const QStringList &args, bool readStdout, bool readStderr=true)
Executor::execute execute an app.
Definition executor.cpp:73
void setEnvironment(const QStringList &env)
Executor::setEnvironment set environment variables for executor processes.
Definition executor.cpp:234
void starting()
starting signal that is emited when process starts
auto cancelNext() -> int
Executor::cancelNext cancels execution of first process in queue if it's not already running.
Definition executor.cpp:244
#define dbg()
Definition debughelper.h:9