OSDN Git Service

Updated CA certificates file for cURL.
[lamexp/LameXP.git] / src / Tool_Abstract.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2022 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU GENERAL PUBLIC LICENSE as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version; always including the non-optional
9 // LAMEXP GNU GENERAL PUBLIC LICENSE ADDENDUM. See "License.txt" file!
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 //
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
22
23 #include "Tool_Abstract.h"
24
25 //Internal
26 #include "Global.h"
27
28 //MUtils
29 #include <MUtils/Global.h>
30 #include <MUtils/OSSupport.h>
31 #include <MUtils/JobObject.h>
32
33 //Qt
34 #include <QProcess>
35 #include <QMutex>
36 #include <QMutexLocker>
37 #include <QProcessEnvironment>
38 #include <QDir>
39 #include <QElapsedTimer>
40
41 /*
42  * Static Objects
43  */
44 QScopedPointer<MUtils::JobObject> AbstractTool::s_jobObjectInstance;
45 QScopedPointer<QElapsedTimer>     AbstractTool::s_startProcessTimer;
46
47 /*
48  * Synchronization
49  */
50 QMutex AbstractTool::s_startProcessMutex;
51 QMutex AbstractTool::s_createObjectMutex;
52
53 /*
54  * Ref Counter
55  */
56 quint64 AbstractTool::s_referenceCounter = 0ui64;
57
58 /*
59  * Const
60  */
61 static const qint64 START_DELAY = 64i64;        //in milliseconds
62
63 /*
64  * Constructor
65  */
66 AbstractTool::AbstractTool(void)
67 :
68         m_firstLaunch(true)
69
70 {
71         QMutexLocker lock(&s_createObjectMutex);
72
73         if(s_referenceCounter++ == 0)
74         {
75                 s_jobObjectInstance.reset(new MUtils::JobObject());
76                 s_startProcessTimer.reset(new QElapsedTimer());
77                 if(!MUtils::OS::setup_timer_resolution())
78                 {
79                         qWarning("Failed to setup system timer resolution!");
80                 }
81         }
82 }
83
84 /*
85  * Destructor
86  */
87 AbstractTool::~AbstractTool(void)
88 {
89         QMutexLocker lock(&s_createObjectMutex);
90
91         if(--s_referenceCounter == 0)
92         {
93                 s_jobObjectInstance.reset(NULL);
94                 s_startProcessTimer.reset(NULL);
95                 if(!MUtils::OS::reset_timer_resolution())
96                 {
97                         qWarning("Failed to reset system timer resolution!");
98                 }
99         }
100 }
101
102 /*
103  * Initialize and launch process object
104  */
105 bool AbstractTool::startProcess(QProcess &process, const QString &program, const QStringList &args, const QString &workingDir)
106 {
107         QMutexLocker lock(&s_startProcessMutex);
108         
109         if((!s_startProcessTimer.isNull()) && s_startProcessTimer->isValid())
110         {
111                 qint64 elapsed = s_startProcessTimer->elapsed();
112                 while(elapsed < START_DELAY)
113                 {
114                         lock.unlock();
115                         MUtils::OS::sleep_ms((size_t)(START_DELAY - elapsed));
116                         lock.relock();
117                         elapsed = s_startProcessTimer->elapsed();
118                 }
119         }
120
121         emit messageLogged(commandline2string(program, args) + "\n");
122         MUtils::init_process(process, workingDir.isEmpty() ? QFileInfo(program).absolutePath() : workingDir);
123
124         process.start(program, args);
125         
126         if(process.waitForStarted())
127         {
128                 if(!s_jobObjectInstance.isNull())
129                 {
130                         if(!s_jobObjectInstance->addProcessToJob(&process))
131                         {
132                                 qWarning("Failed to assign process to job object!");
133                         }
134                 }
135
136                 MUtils::OS::change_process_priority(&process, -1);
137                 
138                 if(m_firstLaunch)
139                 {
140                         emit statusUpdated(0);
141                         m_firstLaunch = false;
142                 }
143                 
144                 s_startProcessTimer->start();
145                 return true;
146         }
147
148         emit messageLogged("Process creation has failed :-(");
149         QString errorMsg= process.errorString().trimmed();
150         if(!errorMsg.isEmpty()) emit messageLogged(errorMsg);
151
152         process.kill();
153         process.waitForFinished(-1);
154
155         s_startProcessTimer->start();
156         return false;
157 }
158
159 /*
160 * Wait for process to terminate while processing its output
161 */
162 AbstractTool::result_t AbstractTool::awaitProcess(QProcess &process, QAtomicInt &abortFlag, int *const exitCode)
163 {
164         return awaitProcess(process, abortFlag, [](const QString& /*text*/) { return false; }, exitCode);
165 }
166
167 /*
168 * Wait for process to terminate while processing its output
169 */
170 AbstractTool::result_t AbstractTool::awaitProcess(QProcess &process, QAtomicInt &abortFlag, std::function<bool(const QString &text)> &&handler, int *const exitCode)
171 {
172         bool bTimeout = false;
173         bool bAborted = false;
174
175         QString lastText;
176
177         while (process.state() != QProcess::NotRunning)
178         {
179                 if (CHECK_FLAG(abortFlag))
180                 {
181                         process.kill();
182                         bAborted = true;
183                         emit messageLogged("\nABORTED BY USER !!!");
184                         break;
185                 }
186
187                 process.waitForReadyRead(m_processTimeoutInterval);
188                 if (!process.bytesAvailable() && process.state() == QProcess::Running)
189                 {
190                         process.kill();
191                         qWarning("Tool process timed out <-- killing!");
192                         emit messageLogged("\nPROCESS TIMEOUT !!!");
193                         bTimeout = true;
194                         break;
195                 }
196
197                 while (process.bytesAvailable() > 0)
198                 {
199                         QByteArray line = process.readLine();
200                         if (line.size() > 0)
201                         {
202                                 line.replace('\r', char(0x20)).replace('\b', char(0x20)).replace('\t', char(0x20));
203                                 const QString text = QString::fromUtf8(line.constData()).simplified();
204                                 if (!text.isEmpty())
205                                 {
206                                         if (!handler(text))
207                                         {
208                                                 if (text.compare(lastText, Qt::CaseInsensitive) != 0)
209                                                 {
210                                                         emit messageLogged(lastText = text);
211                                                 }
212                                         }
213                                 }
214                         }
215                 }
216         }
217
218         process.waitForFinished();
219         if (process.state() != QProcess::NotRunning)
220         {
221                 process.kill();
222                 process.waitForFinished(-1);
223         }
224
225         if (exitCode)
226         {
227                 *exitCode = process.exitCode();
228         }
229
230         emit messageLogged(QString().sprintf("\nExited with code: 0x%04X", process.exitCode()));
231         if (!(bAborted || bTimeout)) emit statusUpdated(100);
232
233         if (bAborted || bTimeout || (process.exitCode() != EXIT_SUCCESS))
234         {
235                 return bAborted ? RESULT_ABORTED : (bTimeout ? RESULT_TIMEOUT : RESULT_FAILURE);
236         }
237
238         return RESULT_SUCCESS;
239 }
240
241 /*
242  * Convert program arguments to single string
243  */
244 QString AbstractTool::commandline2string(const QString &program, const QStringList &arguments)
245 {
246         QString commandline = (program.contains(' ') ? QString("\"%1\"").arg(program) : program);
247         
248         for(int i = 0; i < arguments.count(); i++)
249         {
250                 commandline += (arguments.at(i).contains(' ') ? QString(" \"%1\"").arg(arguments.at(i)) : QString(" %1").arg(arguments.at(i)));
251         }
252
253         return commandline;
254 }
255
256