1 #include "tarsnaptask.h"
2 #include "debug.h"
3 #include "utils.h"
4
5 #if defined Q_OS_UNIX
6 #include <signal.h>
7 #endif
8
9 #define DEFAULT_TIMEOUT_MS 5000
10 #define LOG_MAX_LENGTH 3072
11 #define LOG_MAX_SEARCH_NL 1024
12
TarsnapTask()13 TarsnapTask::TarsnapTask()
14 : QObject(),
15 _id(QUuid::createUuid()),
16 _process(nullptr),
17 _truncateLogOutput(false)
18 {
19 }
20
~TarsnapTask()21 TarsnapTask::~TarsnapTask()
22 {
23 }
24
run()25 void TarsnapTask::run()
26 {
27 _process = new QProcess();
28 _process->setProgram(_command);
29 _process->setArguments(_arguments);
30 if(!_stdOutFile.isEmpty())
31 _process->setStandardOutputFile(_stdOutFile);
32
33 LOG << tr("Task %1 started:\n[%2 %3]")
34 .arg(_id.toString())
35 .arg(_process->program())
36 .arg(Utils::quoteCommandLine(_process->arguments()));
37
38 _process->start();
39
40 if(_process->waitForStarted(DEFAULT_TIMEOUT_MS))
41 {
42 emit started(_data);
43 }
44 else
45 {
46 processError();
47 goto cleanup;
48 }
49
50 if(!_stdIn.isEmpty())
51 {
52 QByteArray password(_stdIn.toUtf8());
53 _process->write(password.data(), password.size());
54 _process->closeWriteChannel();
55 }
56
57 if(_process->waitForFinished(-1))
58 {
59 readProcessOutput();
60 processFinished();
61 }
62 else
63 {
64 readProcessOutput();
65 processError();
66 goto cleanup;
67 }
68
69 cleanup:
70 delete _process;
71 _process = nullptr;
72 emit dequeue();
73 }
74
stop(bool kill)75 void TarsnapTask::stop(bool kill)
76 {
77 if(_process->state() == QProcess::Running)
78 {
79 _process->terminate();
80 if(kill && (false == _process->waitForFinished(DEFAULT_TIMEOUT_MS)))
81 _process->kill();
82 }
83 }
84
interrupt()85 void TarsnapTask::interrupt()
86 {
87 #if defined Q_OS_UNIX
88 kill(_process->pid(), SIGQUIT);
89 #endif
90 }
91
cancel()92 void TarsnapTask::cancel()
93 {
94 emit canceled(_data);
95 }
96
waitForTask()97 bool TarsnapTask::waitForTask()
98 {
99 return _process->waitForFinished(-1);
100 }
101
taskStatus()102 QProcess::ProcessState TarsnapTask::taskStatus()
103 {
104 return _process->state();
105 }
106
command() const107 QString TarsnapTask::command() const
108 {
109 return _command;
110 }
111
setCommand(const QString & command)112 void TarsnapTask::setCommand(const QString &command)
113 {
114 _command = command;
115 }
116
arguments() const117 QStringList TarsnapTask::arguments() const
118 {
119 return _arguments;
120 }
121
setArguments(const QStringList & arguments)122 void TarsnapTask::setArguments(const QStringList &arguments)
123 {
124 _arguments = arguments;
125 }
126
setStdIn(const QString & standardIn)127 void TarsnapTask::setStdIn(const QString &standardIn)
128 {
129 _stdIn = standardIn;
130 }
131
setStdOutFile(const QString & fileName)132 void TarsnapTask::setStdOutFile(const QString &fileName)
133 {
134 _stdOutFile = fileName;
135 }
136
data() const137 QVariant TarsnapTask::data() const
138 {
139 return _data;
140 }
141
setData(const QVariant & data)142 void TarsnapTask::setData(const QVariant &data)
143 {
144 _data = data;
145 }
146
truncateLogOutput() const147 bool TarsnapTask::truncateLogOutput() const
148 {
149 return _truncateLogOutput;
150 }
151
setTruncateLogOutput(bool truncateLogOutput)152 void TarsnapTask::setTruncateLogOutput(bool truncateLogOutput)
153 {
154 _truncateLogOutput = truncateLogOutput;
155 }
156
readProcessOutput()157 void TarsnapTask::readProcessOutput()
158 {
159 if(_stdOutFile.isEmpty())
160 _stdOut.append(_process->readAllStandardOutput().trimmed());
161 _stdErr.append(_process->readAllStandardError().trimmed());
162 }
163
processFinished()164 void TarsnapTask::processFinished()
165 {
166 switch(_process->exitStatus())
167 {
168 case QProcess::NormalExit:
169 {
170 emit finished(_data, _process->exitCode(), QString(_stdOut),
171 QString(_stdErr));
172
173 // Truncate LOG output
174 QByteArray stdOut(_stdOut);
175 if(!stdOut.isEmpty() && _truncateLogOutput
176 && (stdOut.size() > LOG_MAX_LENGTH))
177 {
178 int nextNL =
179 stdOut.lastIndexOf(QChar('\n'),
180 LOG_MAX_LENGTH
181 + std::min(stdOut.size() - LOG_MAX_LENGTH,
182 LOG_MAX_SEARCH_NL));
183 stdOut.truncate(std::max(LOG_MAX_LENGTH, nextNL));
184 stdOut.append(
185 tr("\n...\n-- %1 output lines truncated by Tarsnap GUI --\n")
186 .arg(_stdOut.mid(stdOut.size()).count(QChar('\n').toLatin1())));
187 }
188
189 LOG << tr("Task %1 finished with exit code %2:\n[%3 %4]\n%5")
190 .arg(_id.toString())
191 .arg(_process->exitCode())
192 .arg(_command)
193 .arg(Utils::quoteCommandLine(_arguments))
194 .arg(QString(stdOut + _stdErr));
195 break;
196 }
197 case QProcess::CrashExit:
198 {
199 processError();
200 break;
201 }
202 }
203 }
204
processError()205 void TarsnapTask::processError()
206 {
207 LOG << tr("Task %1 finished with error %2 (%3) occured (exit code %4):\n[%5 %6]\n%7")
208 .arg(_id.toString())
209 .arg(_process->error())
210 .arg(_process->errorString())
211 .arg(_process->exitCode())
212 .arg(_command)
213 .arg(Utils::quoteCommandLine(_arguments))
214 .arg(QString(_stdOut + _stdErr).trimmed());
215 cancel();
216 }
217