Line data Source code
1 : #include "executor.h"
2 : #include "debughelper.h"
3 : #include <QCoreApplication>
4 : #include <QDir>
5 : #include <QTextCodec>
6 :
7 : /**
8 : * @brief Executor::Executor executes external applications
9 : * @param parent
10 : */
11 20 : Executor::Executor(QObject *parent) : QObject(parent), running(false) {
12 8 : connect(&m_process,
13 : static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(
14 : &QProcess::finished),
15 : this,
16 : static_cast<void (Executor::*)(int, QProcess::ExitStatus)>(
17 : &Executor::finished));
18 8 : connect(&m_process, &QProcess::started, this, &Executor::starting);
19 8 : }
20 :
21 : /**
22 : * @brief Executor::executeNext consumes executable tasks from the queue
23 : */
24 0 : void Executor::executeNext() {
25 0 : if (!running) {
26 0 : if (!m_execQueue.isEmpty()) {
27 0 : const execQueueItem &i = m_execQueue.head();
28 0 : running = true;
29 0 : if (!i.workingDir.isEmpty())
30 0 : m_process.setWorkingDirectory(i.workingDir);
31 0 : m_process.start(i.app, i.args);
32 0 : if (!i.input.isEmpty()) {
33 0 : m_process.waitForStarted(-1);
34 0 : QByteArray data = i.input.toUtf8();
35 0 : if (m_process.write(data) != data.length())
36 : dbg() << "Not all data written to process:" << i.id << " " << i.app;
37 0 : }
38 0 : m_process.closeWriteChannel();
39 0 : }
40 : }
41 0 : }
42 :
43 : /**
44 : * @brief Executor::execute execute an app
45 : * @param id
46 : * @param app
47 : * @param args
48 : * @param readStdout
49 : * @param readStderr
50 : */
51 0 : void Executor::execute(int id, const QString &app, const QStringList &args,
52 : bool readStdout, bool readStderr) {
53 0 : execute(id, QString(), app, args, QString(), readStdout, readStderr);
54 0 : }
55 :
56 : /**
57 : * @brief Executor::execute executes an app from a workDir
58 : * @param id
59 : * @param workDir
60 : * @param app
61 : * @param args
62 : * @param readStdout
63 : * @param readStderr
64 : */
65 0 : void Executor::execute(int id, const QString &workDir, const QString &app,
66 : const QStringList &args, bool readStdout,
67 : bool readStderr) {
68 0 : execute(id, workDir, app, args, QString(), readStdout, readStderr);
69 0 : }
70 :
71 : /**
72 : * @brief Executor::execute an app, takes input and presents it as stdin
73 : * @param id
74 : * @param app
75 : * @param args
76 : * @param input
77 : * @param readStdout
78 : * @param readStderr
79 : */
80 0 : void Executor::execute(int id, const QString &app, const QStringList &args,
81 : QString input, bool readStdout, bool readStderr) {
82 0 : execute(id, QString(), app, args, input, readStdout, readStderr);
83 0 : }
84 :
85 : /**
86 : * @brief Executor::execute executes an app from a workDir, takes input and
87 : * presents it as stdin
88 : * @param id
89 : * @param workDir
90 : * @param app
91 : * @param args
92 : * @param input
93 : * @param readStdout
94 : * @param readStderr
95 : */
96 0 : void Executor::execute(int id, const QString &workDir, const QString &app,
97 : const QStringList &args, QString input, bool readStdout,
98 : bool readStderr) {
99 : // Happens a lot if e.g. git binary is not set.
100 : // This will result in bogus "QProcess::FailedToStart" messages,
101 : // also hiding legitimate errors from the gpg commands.
102 0 : if (app.isEmpty()) {
103 : dbg() << "Trying to execute nothing...";
104 : return;
105 : }
106 0 : QString appPath =
107 0 : QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(app);
108 0 : m_execQueue.push_back(
109 0 : {id, appPath, args, input, readStdout, readStderr, workDir});
110 0 : executeNext();
111 0 : }
112 :
113 : /**
114 : * @brief Executor::executeBlocking blocking version of the executor,
115 : * takes input and presents it as stdin
116 : * @param app
117 : * @param args
118 : * @param input
119 : * @param process_out
120 : * @param process_err
121 : * @return
122 : *
123 : * TODO(bezet): it might make sense to throw here, a lot of possible errors
124 : */
125 0 : int Executor::executeBlocking(QString app, const QStringList &args,
126 : QString input, QString *process_out,
127 : QString *process_err) {
128 0 : QProcess internal;
129 0 : internal.start(app, args);
130 0 : if (!input.isEmpty()) {
131 0 : QByteArray data = input.toUtf8();
132 0 : internal.waitForStarted(-1);
133 0 : if (internal.write(data) != data.length()) {
134 : dbg() << "Not all input written:" << app;
135 : }
136 0 : internal.closeWriteChannel();
137 0 : }
138 0 : internal.waitForFinished(-1);
139 0 : if (internal.exitStatus() == QProcess::NormalExit) {
140 0 : QTextCodec *codec = QTextCodec::codecForLocale();
141 0 : QString pout = codec->toUnicode(internal.readAllStandardOutput());
142 0 : QString perr = codec->toUnicode(internal.readAllStandardError());
143 0 : if (process_out != Q_NULLPTR)
144 0 : *process_out = pout;
145 0 : if (process_err != Q_NULLPTR)
146 0 : *process_err = perr;
147 0 : return internal.exitCode();
148 0 : } else {
149 : // TODO(bezet): emit error() ?
150 0 : return -1; // QProcess error code + qDebug error?
151 : }
152 0 : }
153 :
154 : /**
155 : * @brief Executor::executeBlocking blocking version of the executor
156 : * @param app
157 : * @param args
158 : * @param process_out
159 : * @param process_err
160 : * @return
161 : */
162 0 : int Executor::executeBlocking(QString app, const QStringList &args,
163 : QString *process_out, QString *process_err) {
164 0 : return executeBlocking(app, args, QString(), process_out, process_err);
165 0 : }
166 :
167 : /**
168 : * @brief Executor::setEnvironment set environment variables
169 : * for executor processes
170 : * @param env
171 : */
172 0 : void Executor::setEnvironment(const QStringList &env) {
173 0 : m_process.setEnvironment(env);
174 0 : }
175 :
176 : /**
177 : * @brief Executor::cancelNext cancels execution of first process in queue
178 : * if it's not already running
179 : *
180 : * @return id of the cancelled process or -1 on error
181 : */
182 0 : int Executor::cancelNext() {
183 0 : if (running || m_execQueue.isEmpty())
184 0 : return -1; // TODO(bezet): definitely throw here
185 0 : return m_execQueue.dequeue().id;
186 0 : }
187 :
188 : /**
189 : * @brief Executor::finished called when an executed process finishes
190 : * @param exitCode
191 : * @param exitStatus
192 : */
193 0 : void Executor::finished(int exitCode, QProcess::ExitStatus exitStatus) {
194 0 : execQueueItem i = m_execQueue.dequeue();
195 0 : running = false;
196 0 : if (exitStatus == QProcess::NormalExit) {
197 0 : QString output, err;
198 0 : QTextCodec *codec = QTextCodec::codecForLocale();
199 0 : if (i.readStdout)
200 0 : output = codec->toUnicode(m_process.readAllStandardOutput());
201 0 : if (i.readStderr or exitCode != 0) {
202 0 : err = codec->toUnicode(m_process.readAllStandardError());
203 : if (exitCode != 0)
204 : dbg() << exitCode << err;
205 0 : }
206 0 : emit finished(i.id, exitCode, output, err);
207 0 : }
208 : // else: emit crashed with ID, which may give a chance to recover ?
209 0 : executeNext();
210 0 : }
|