Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #include "process.h"
23 :
24 : #include "muse_framework_config.h"
25 :
26 : #ifdef QT_QPROCESS_SUPPORTED
27 : #include <algorithm>
28 : #include <QElapsedTimer>
29 : #include <QProcess>
30 : #endif
31 :
32 : #include "log.h"
33 :
34 : using namespace muse;
35 :
36 : #ifdef QT_QPROCESS_SUPPORTED
37 0 : static QStringList toQList(const std::vector<std::string>& args)
38 : {
39 0 : QStringList list;
40 0 : for (const std::string& a : args) {
41 0 : list << QString::fromStdString(a);
42 : }
43 0 : return list;
44 0 : }
45 :
46 : #endif
47 :
48 0 : int Process::execute(const std::string& program, const std::vector<std::string>& args)
49 : {
50 : #ifdef QT_QPROCESS_SUPPORTED
51 0 : int ret = QProcess::execute(QString::fromStdString(program), toQList(args));
52 0 : return ret;
53 : #else
54 : UNUSED(program);
55 : UNUSED(args);
56 : NOT_SUPPORTED;
57 : return -1;
58 : #endif
59 : }
60 :
61 0 : int Process::execute(const std::string& program, const std::vector<std::string>& args, int timeout_ms)
62 : {
63 0 : return execute(program, args, timeout_ms, {});
64 : }
65 :
66 0 : int Process::execute(const std::string& program, const std::vector<std::string>& args, int timeout_ms,
67 : const std::function<bool()>& shouldCancel)
68 : {
69 : #ifdef QT_QPROCESS_SUPPORTED
70 0 : QProcess process;
71 0 : QElapsedTimer elapsed;
72 0 : elapsed.start();
73 0 : process.start(QString::fromStdString(program), toQList(args));
74 :
75 0 : if (!process.waitForStarted()) {
76 0 : return ExecuteStartFailedCode;
77 : }
78 :
79 0 : auto killProcess = [&](const char* reason, int returnCode) {
80 : UNUSED(reason);
81 0 : if (returnCode == ExecuteTimeoutCode) {
82 0 : LOGW() << "Process timed out, killing: " << program;
83 : }
84 :
85 0 : process.kill();
86 0 : if (!process.waitForFinished(3000)) {
87 0 : if (returnCode == ExecuteTimeoutCode) {
88 0 : LOGW() << "Timed-out process did not finish after kill: " << program;
89 : }
90 : }
91 0 : return returnCode;
92 0 : };
93 :
94 : while (true) {
95 0 : if (shouldCancel && shouldCancel()) {
96 0 : return killProcess("canceled", ExecuteCanceledCode);
97 : }
98 :
99 0 : const qint64 remaining = timeout_ms < 0 ? 50 : std::max<qint64>(0, timeout_ms - elapsed.elapsed());
100 0 : const int waitMs = timeout_ms < 0 ? 50 : static_cast<int>(std::min<qint64>(50, remaining));
101 :
102 0 : if (process.waitForFinished(waitMs)) {
103 0 : break;
104 : }
105 :
106 0 : if (timeout_ms >= 0 && elapsed.elapsed() >= timeout_ms) {
107 0 : return killProcess("timed out", ExecuteTimeoutCode);
108 : }
109 0 : }
110 :
111 : {
112 0 : const bool normalExit = process.exitStatus() == QProcess::NormalExit;
113 0 : const int code = normalExit ? process.exitCode() : ExecuteCrashCode;
114 0 : return code;
115 : }
116 : #else
117 : UNUSED(program);
118 : UNUSED(args);
119 : UNUSED(timeout_ms);
120 : UNUSED(shouldCancel);
121 : NOT_SUPPORTED;
122 : return ExecuteCrashCode;
123 : #endif
124 0 : }
125 :
126 0 : bool Process::startDetached(const std::string& program, const std::vector<std::string>& args)
127 : {
128 : #ifdef QT_QPROCESS_SUPPORTED
129 0 : bool ok = QProcess::startDetached(QString::fromStdString(program), toQList(args));
130 0 : return ok;
131 : #else
132 : UNUSED(program);
133 : UNUSED(args);
134 : NOT_SUPPORTED;
135 : return false;
136 : #endif
137 : }
|