QtPass  1.3.3
Multi-platform GUI for pass, the standard unix password manager.
executor.cpp
Go to the documentation of this file.
1 #include "executor.h"
2 #include <QCoreApplication>
3 #include <QDir>
4 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5 #include <QTextCodec>
6 #endif
7 
8 #ifdef QT_DEBUG
9 #include "debughelper.h"
10 #endif
11 
16 Executor::Executor(QObject *parent) : QObject(parent), running(false) {
17  connect(&m_process,
18  static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(
19  &QProcess::finished),
20  this,
21  static_cast<void (Executor::*)(int, QProcess::ExitStatus)>(
22  &Executor::finished));
23  connect(&m_process, &QProcess::started, this, &Executor::starting);
24 }
25 
29 void Executor::executeNext() {
30  if (!running) {
31  if (!m_execQueue.isEmpty()) {
32  const execQueueItem &i = m_execQueue.head();
33  running = true;
34  if (!i.workingDir.isEmpty())
35  m_process.setWorkingDirectory(i.workingDir);
36  if (i.app.startsWith("wsl ")) {
37  QStringList tmp = i.args;
38  QString app = i.app;
39  tmp.prepend(app.remove(0, 4));
40  m_process.start("wsl", tmp);
41  } else
42  m_process.start(i.app, i.args);
43  if (!i.input.isEmpty()) {
44  m_process.waitForStarted(-1);
45  QByteArray data = i.input.toUtf8();
46  if (m_process.write(data) != data.length()) {
47 #ifdef QT_DEBUG
48  dbg() << "Not all data written to process:" << i.id << " " << i.app;
49 #endif
50  }
51  }
52  m_process.closeWriteChannel();
53  }
54  }
55 }
56 
65 void Executor::execute(int id, const QString &app, const QStringList &args,
66  bool readStdout, bool readStderr) {
67  execute(id, QString(), app, args, QString(), readStdout, readStderr);
68 }
69 
79 void Executor::execute(int id, const QString &workDir, const QString &app,
80  const QStringList &args, bool readStdout,
81  bool readStderr) {
82  execute(id, workDir, app, args, QString(), readStdout, readStderr);
83 }
84 
94 void Executor::execute(int id, const QString &app, const QStringList &args,
95  QString input, bool readStdout, bool readStderr) {
96  execute(id, QString(), app, args, input, readStdout, readStderr);
97 }
98 
110 void Executor::execute(int id, const QString &workDir, const QString &app,
111  const QStringList &args, QString input, bool readStdout,
112  bool readStderr) {
113  // Happens a lot if e.g. git binary is not set.
114  // This will result in bogus "QProcess::FailedToStart" messages,
115  // also hiding legitimate errors from the gpg commands.
116  if (app.isEmpty()) {
117 #ifdef QT_DEBUG
118  dbg() << "Trying to execute nothing...";
119 #endif
120  return;
121  }
122  QString appPath = app;
123  if (!appPath.startsWith("wsl "))
124  appPath =
125  QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(app);
126  m_execQueue.push_back(
127  {id, appPath, args, input, readStdout, readStderr, workDir});
128  executeNext();
129 }
130 
141 static QString decodeAssumingUtf8(QByteArray in) {
142 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
143  QTextCodec *codec = QTextCodec::codecForName("UTF-8");
144  QTextCodec::ConverterState state;
145  QString out = codec->toUnicode(in.constData(), in.size(), &state);
146  if (!state.invalidChars)
147  return out;
148  codec = QTextCodec::codecForUtfText(in);
149  return codec->toUnicode(in);
150 #else
151  auto converter = QStringDecoder(QStringDecoder::Utf8);
152  return converter(in);
153 #endif
154 }
155 
168 int Executor::executeBlocking(QString app, const QStringList &args,
169  QString input, QString *process_out,
170  QString *process_err) {
171  QProcess internal;
172  if (app.startsWith("wsl ")) {
173  QStringList tmp = args;
174  tmp.prepend(app.remove(0, 4));
175  internal.start("wsl", tmp);
176  } else
177  internal.start(app, args);
178  if (!input.isEmpty()) {
179  QByteArray data = input.toUtf8();
180  internal.waitForStarted(-1);
181  if (internal.write(data) != data.length()) {
182 #ifdef QT_DEBUG
183  dbg() << "Not all input written:" << app;
184 #endif
185  }
186  internal.closeWriteChannel();
187  }
188  internal.waitForFinished(-1);
189  if (internal.exitStatus() == QProcess::NormalExit) {
190  QString pout = decodeAssumingUtf8(internal.readAllStandardOutput());
191  QString perr = decodeAssumingUtf8(internal.readAllStandardError());
192  if (process_out != Q_NULLPTR)
193  *process_out = pout;
194  if (process_err != Q_NULLPTR)
195  *process_err = perr;
196  return internal.exitCode();
197  }
198  // TODO(bezet): emit error() ?
199  return -1; // QProcess error code + qDebug error?
200 }
201 
210 int Executor::executeBlocking(QString app, const QStringList &args,
211  QString *process_out, QString *process_err) {
212  return executeBlocking(app, args, QString(), process_out, process_err);
213 }
214 
220 void Executor::setEnvironment(const QStringList &env) {
221  m_process.setEnvironment(env);
222 }
223 
231  if (running || m_execQueue.isEmpty())
232  return -1; // TODO(bezet): definitely throw here
233  return m_execQueue.dequeue().id;
234 }
235 
241 void Executor::finished(int exitCode, QProcess::ExitStatus exitStatus) {
242  execQueueItem i = m_execQueue.dequeue();
243  running = false;
244  if (exitStatus == QProcess::NormalExit) {
245  QString output, err;
246  if (i.readStdout)
247  output = decodeAssumingUtf8(m_process.readAllStandardOutput());
248  if (i.readStderr || exitCode != 0) {
249  err = decodeAssumingUtf8(m_process.readAllStandardError());
250  if (exitCode != 0) {
251 #ifdef QT_DEBUG
252  dbg() << exitCode << err;
253 #endif
254  }
255  }
256  emit finished(i.id, exitCode, output, err);
257  }
258  // else: emit crashed with ID, which may give a chance to recover ?
259  executeNext();
260 }
Executor
Executes external commands for handleing password, git and other data.
Definition: executor.h:12
Executor::execute
void execute(int id, const QString &app, const QStringList &args, bool readStdout, bool readStderr=true)
Executor::execute execute an app.
Definition: executor.cpp:65
Executor::executeBlocking
static int executeBlocking(QString app, const QStringList &args, QString input=QString(), QString *process_out=Q_NULLPTR, QString *process_err=Q_NULLPTR)
Executor::executeBlocking blocking version of the executor, takes input and presents it as stdin.
Definition: executor.cpp:168
debughelper.h
Executor::setEnvironment
void setEnvironment(const QStringList &env)
Executor::setEnvironment set environment variables for executor processes.
Definition: executor.cpp:220
executor.h
Executor::Executor
Executor(QObject *parent=0)
Executor::Executor executes external applications.
Definition: executor.cpp:16
Executor::cancelNext
int cancelNext()
Executor::cancelNext cancels execution of first process in queue if it's not already running.
Definition: executor.cpp:230
dbg
#define dbg()
Definition: debughelper.h:7
Executor::starting
void starting()
starting signal that is emited when process starts