Changeset View
Changeset View
Standalone View
Standalone View
src/util/externalcommandhelper.cpp
1 | /************************************************************************* | 1 | /************************************************************************* | ||
---|---|---|---|---|---|
2 | * Copyright (C) 2017-2018 by Andrius Štikonas <andrius@stikonas.eu> * | 2 | * Copyright (C) 2017-2018 by Andrius Štikonas <andrius@stikonas.eu> * | ||
3 | * Copyright (C) 2019 by Shubham <aryan100jangid@gmail.com> * | ||||
3 | * * | 4 | * * | ||
4 | * This program is free software; you can redistribute it and/or * | 5 | * This program is free software; you can redistribute it and/or * | ||
5 | * modify it under the terms of the GNU General Public License as * | 6 | * modify it under the terms of the GNU General Public License as * | ||
6 | * published by the Free Software Foundation; either version 3 of * | 7 | * published by the Free Software Foundation; either version 3 of * | ||
7 | * the License, or (at your option) any later version. * | 8 | * the License, or (at your option) any later version. * | ||
8 | * * | 9 | * * | ||
9 | * This program is distributed in the hope that it will be useful, * | 10 | * This program is distributed in the hope that it will be useful, * | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | ||
12 | * GNU General Public License for more details. * | 13 | * GNU General Public License for more details. * | ||
13 | * * | 14 | * * | ||
14 | * You should have received a copy of the GNU General Public License * | 15 | * You should have received a copy of the GNU General Public License * | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>.* | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>.* | ||
16 | *************************************************************************/ | 17 | *************************************************************************/ | ||
17 | 18 | | |||
18 | #include "externalcommandhelper.h" | 19 | #include "externalcommandhelper.h" | ||
20 | | ||||
21 | #include "externalcommand_interface.h" | ||||
19 | #include "externalcommand_whitelist.h" | 22 | #include "externalcommand_whitelist.h" | ||
20 | 23 | | |||
21 | #include <QtDBus> | | |||
22 | #include <QCoreApplication> | 24 | #include <QCoreApplication> | ||
25 | #include <QtDBus> | ||||
23 | #include <QDebug> | 26 | #include <QDebug> | ||
27 | #include <QElapsedTimer> | ||||
24 | #include <QFile> | 28 | #include <QFile> | ||
25 | #include <QString> | 29 | #include <QString> | ||
26 | #include <QTime> | | |||
27 | #include <QVariant> | 30 | #include <QVariant> | ||
28 | 31 | | |||
29 | #include <KLocalizedString> | 32 | #include <KLocalizedString> | ||
30 | 33 | | |||
34 | #define HELPER_MAIN() \ | ||||
35 | int main(int argc, char **argv) { ExternalCommandHelper helper; return helper.helperMain(argc, argv); } | ||||
36 | | ||||
31 | /** Initialize ExternalCommandHelper Daemon and prepare DBus interface | 37 | /** Initialize ExternalCommandHelper Daemon and prepare DBus interface | ||
32 | * | 38 | * | ||
33 | * KAuth helper runs in the background until application exits. | 39 | * Helper runs in the background until application exits. | ||
34 | * To avoid forever running helper in case of application crash | 40 | * To avoid forever running helper in case of application crash | ||
35 | * ExternalCommand class opens a DBus service that we monitor for changes. | 41 | * ExternalCommand class opens a DBus service that we monitor for changes. | ||
36 | * If helper is not busy then it exits when the client services gets | 42 | * If helper is not busy then it exits when the client services gets | ||
37 | * unregistered. Otherwise, | 43 | * unregistered. Otherwise, | ||
38 | * we wait for the current job to finish before exiting, so even in case | 44 | * we wait for the current job to finish before exiting, so even in case | ||
39 | * of main application crash, we do not leave partially moved data. | 45 | * of main application crash, we do not leave partially moved data. | ||
40 | * | 46 | * | ||
41 | * This helper also starts another DBus interface where it listens to | 47 | * This helper also starts another DBus interface where it listens to | ||
42 | * command execution requests from the application that started the helper. | 48 | * command execution requests from the application that started the helper. | ||
43 | * | 49 | * | ||
44 | */ | 50 | */ | ||
45 | ActionReply ExternalCommandHelper::init(const QVariantMap& args) | | |||
46 | { | | |||
47 | Q_UNUSED(args) | | |||
48 | 51 | | |||
49 | ActionReply reply; | 52 | /** Reads the given number of bytes from the sourceDevice into the given buffer. | ||
53 | @param argc argument count | ||||
54 | @param argv argument vector | ||||
55 | @return zero on success, non-zero on failure | ||||
56 | */ | ||||
57 | int ExternalCommandHelper::helperMain(int argc, char **argv) | ||||
58 | { | ||||
59 | QCoreApplication app(argc, argv); | ||||
50 | 60 | | |||
51 | if (!QDBusConnection::systemBus().isConnected() || !QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.helperinterface")) || | 61 | if (!QDBusConnection::systemBus().isConnected() || | ||
52 | !QDBusConnection::systemBus().registerObject(QStringLiteral("/Helper"), this, QDBusConnection::ExportAllSlots)) { | 62 | !QDBusConnection::systemBus().registerObject(QStringLiteral("/Helper"), this, QDBusConnection::ExportAllSlots) || | ||
broulik: One should register objects before registering the service as the service might become visible… | |||||
63 | !QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.helperinterface")) || | ||||
What happens when a service/object is registered and the other one is not? cjlcarvalho: What happens when a service/object is registered and the other one is not? | |||||
64 | !QDBusConnection::systemBus().registerObject(QStringLiteral("/Application"), this, QDBusConnection::ExportAllSlots) || | ||||
65 | !QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.applicationinterface"))) { | ||||
66 | qDebug() << "Failed to initialize the Helper"; | ||||
53 | qWarning() << QDBusConnection::systemBus().lastError().message(); | 67 | qWarning() << QDBusConnection::systemBus().lastError().message(); | ||
54 | reply.addData(QStringLiteral("success"), false); | | |||
55 | 68 | | |||
56 | // Also end the application loop started by KAuth's main() code. Our loop | 69 | // We have no reason to live when Main GUI app has expired | ||
57 | // exits when our client disappears. Without client we have no reason to | | |||
58 | // live. | | |||
59 | qApp->quit(); | 70 | qApp->quit(); | ||
60 | 71 | | |||
61 | return reply; | 72 | return app.exec(); | ||
62 | } | 73 | } | ||
63 | 74 | | |||
64 | m_loop = std::make_unique<QEventLoop>(); | 75 | m_loop = std::make_unique<QEventLoop>(); | ||
65 | HelperSupport::progressStep(QVariantMap()); | | |||
66 | 76 | | |||
67 | // End the loop and return only once the client is done using us. | 77 | // We send zero percent new data on initial start-up | ||
68 | auto serviceWatcher = | 78 | sendProgress(0); | ||
69 | new QDBusServiceWatcher(QStringLiteral("org.kde.kpmcore.applicationinterface"), | 79 | | ||
80 | // QDBus Service watcher which keeps an eye on the client (Main GUI app) | ||||
81 | // End the loop and return only once the client has unregistered over the QDBus. | ||||
82 | auto serviceWatcher = new QDBusServiceWatcher(QStringLiteral("org.kde.kpmcore.applicationinterface"), | ||||
70 | QDBusConnection::systemBus(), | 83 | QDBusConnection::systemBus(), | ||
71 | QDBusServiceWatcher::WatchForUnregistration, | 84 | QDBusServiceWatcher::WatchForUnregistration, | ||
72 | this); | 85 | this); | ||
86 | | ||||
73 | connect(serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, | 87 | connect(serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, | ||
74 | [this]() { | 88 | [this]() { | ||
75 | m_loop->exit(); | 89 | m_loop->exit(); | ||
76 | }); | 90 | }); | ||
77 | 91 | | |||
78 | m_loop->exec(); | 92 | m_loop->exec(); | ||
79 | reply.addData(QStringLiteral("success"), true); | | |||
80 | 93 | | |||
81 | // Also end the application loop started by KAuth's main() code. Our loop | | |||
82 | // exits when our client disappears. Without client we have no reason to | | |||
83 | // live. | | |||
84 | qApp->quit(); | 94 | qApp->quit(); | ||
85 | 95 | | |||
86 | return reply; | 96 | return app.exec(); | ||
87 | } | 97 | } | ||
88 | 98 | | |||
89 | | ||||
90 | /** Reads the given number of bytes from the sourceDevice into the given buffer. | 99 | /** Reads the given number of bytes from the sourceDevice into the given buffer. | ||
91 | @param sourceDevice device or file to read from | 100 | @param sourceDevice device or file to read from | ||
92 | @param buffer buffer to store the bytes read in | 101 | @param buffer buffer to store the bytes read in | ||
93 | @param offset offset where to begin reading | 102 | @param offset offset where to begin reading | ||
94 | @param size the number of bytes to read | 103 | @param size the number of bytes to read | ||
95 | @return true on success | 104 | @return true on success | ||
96 | */ | 105 | */ | ||
97 | bool ExternalCommandHelper::readData(const QString& sourceDevice, QByteArray& buffer, const qint64 offset, const qint64 size) | 106 | bool ExternalCommandHelper::readData(const QString& sourceDevice, QByteArray& buffer, const qint64 offset, const qint64 size) | ||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Line(s) | 137 | { | |||
141 | if (device.write(buffer) != buffer.size()) { | 150 | if (device.write(buffer) != buffer.size()) { | ||
142 | qCritical() << xi18n("Could not write to device <filename>%1</filename>.", targetDevice); | 151 | qCritical() << xi18n("Could not write to device <filename>%1</filename>.", targetDevice); | ||
143 | return false; | 152 | return false; | ||
144 | } | 153 | } | ||
145 | 154 | | |||
146 | return true; | 155 | return true; | ||
147 | } | 156 | } | ||
148 | 157 | | |||
158 | /** Sends progress to the main application in terms of percentage. | ||||
broulik: This is a lie. | |||||
159 | @param percent Percent of job completed. | ||||
160 | */ | ||||
161 | void ExternalCommandHelper::sendProgress(int percent) | ||||
162 | { | ||||
163 | auto *interface = new org::kde::kpmcore::applicationinterface(QStringLiteral("org.kde.kpmcore.applicationinterface"), | ||||
QDBusInterface constructor blocks introspecting DBus. You probably want to use an interface generated from an XML file to avoid this. broulik: `QDBusInterface` constructor blocks introspecting DBus. You probably want to use an interface… | |||||
164 | QStringLiteral("/Application"), QDBusConnection::systemBus(), this); | ||||
165 | interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days | ||||
166 | | ||||
167 | QDBusPendingCall pcall = interface->emitNewData(percent); | ||||
168 | | ||||
169 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); | ||||
170 | QEventLoop loop; | ||||
171 | | ||||
172 | auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { | ||||
173 | loop.exit(); | ||||
174 | if (watcher->isError()) | ||||
broulik: What's the point of the event loop? | |||||
175 | qWarning() << watcher->error(); | ||||
176 | }; | ||||
177 | | ||||
178 | connect(watcher, &QDBusPendingCallWatcher::finished, [this, exitLoop, watcher](){ | ||||
179 | watcher->deleteLater(); | ||||
180 | } | ||||
181 | ); | ||||
182 | | ||||
183 | loop.exec(); | ||||
184 | } | ||||
185 | | ||||
186 | /** Sends progress to the main application in terms of string message. | ||||
187 | @param message Message to send to the main application. | ||||
188 | */ | ||||
189 | void ExternalCommandHelper::sendProgress(QString message) | ||||
190 | { | ||||
191 | auto *interface = new org::kde::kpmcore::applicationinterface(QStringLiteral("org.kde.kpmcore.applicationinterface"), | ||||
192 | QStringLiteral("/Application"), QDBusConnection::systemBus(), this); | ||||
193 | interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days | ||||
194 | | ||||
195 | QDBusPendingCall pcall = interface->emitNewData(message); | ||||
196 | | ||||
197 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); | ||||
198 | QEventLoop loop; | ||||
199 | | ||||
200 | auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { | ||||
201 | loop.exit(); | ||||
202 | if (watcher->isError()) | ||||
You're leaking the watcher, call watcher->deleteLater() in the finished handler broulik: You're leaking the watcher, call `watcher->deleteLater()` in the `finished` handler | |||||
203 | qWarning() << watcher->error(); | ||||
204 | }; | ||||
205 | | ||||
206 | connect(watcher, &QDBusPendingCallWatcher::finished, [this, exitLoop, watcher](){ | ||||
207 | watcher->deleteLater(); | ||||
208 | } | ||||
209 | ); | ||||
210 | | ||||
211 | loop.exec(); | ||||
212 | } | ||||
213 | | ||||
149 | // If targetDevice is empty then return QByteArray with data that was read from disk. | 214 | // If targetDevice is empty then return QByteArray with data that was read from disk. | ||
150 | QVariantMap ExternalCommandHelper::copyblocks(const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize) | 215 | QVariantMap ExternalCommandHelper::copyblocks(const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize) | ||
151 | { | 216 | { | ||
152 | QVariantMap reply; | 217 | QVariantMap reply; | ||
153 | reply[QStringLiteral("success")] = true; | 218 | reply[QStringLiteral("success")] = true; | ||
154 | 219 | | |||
155 | const qint64 blocksToCopy = sourceLength / blockSize; | 220 | const qint64 blocksToCopy = sourceLength / blockSize; | ||
156 | qint64 readOffset = sourceFirstByte; | 221 | qint64 readOffset = sourceFirstByte; | ||
157 | qint64 writeOffset = targetFirstByte; | 222 | qint64 writeOffset = targetFirstByte; | ||
158 | qint32 copyDirection = 1; | 223 | qint32 copyDirection = 1; | ||
159 | 224 | | |||
160 | if (targetFirstByte > sourceFirstByte) { | 225 | if (targetFirstByte > sourceFirstByte) { | ||
161 | readOffset = sourceFirstByte + sourceLength - blockSize; | 226 | readOffset = sourceFirstByte + sourceLength - blockSize; | ||
162 | writeOffset = targetFirstByte + sourceLength - blockSize; | 227 | writeOffset = targetFirstByte + sourceLength - blockSize; | ||
163 | copyDirection = -1; | 228 | copyDirection = -1; | ||
164 | } | 229 | } | ||
165 | 230 | | |||
166 | const qint64 lastBlock = sourceLength % blockSize; | 231 | const qint64 lastBlock = sourceLength % blockSize; | ||
167 | 232 | | |||
168 | qint64 bytesWritten = 0; | 233 | qint64 bytesWritten = 0; | ||
169 | qint64 blocksCopied = 0; | 234 | qint64 blocksCopied = 0; | ||
170 | 235 | | |||
171 | QByteArray buffer; | 236 | QByteArray buffer; | ||
172 | int percent = 0; | 237 | int percent = 0; | ||
173 | QTime t; | | |||
174 | 238 | | |||
239 | QElapsedTimer t; | ||||
broulik: This probably wants to be a `QElapsedTimer` | |||||
175 | t.start(); | 240 | t.start(); | ||
176 | 241 | | |||
177 | QVariantMap report; | 242 | sendProgress(xi18nc("@info:progress", "Copying %1 blocks (%2 bytes) from %3 to %4, direction: %5.", blocksToCopy, | ||
178 | | ||||
179 | report[QStringLiteral("report")] = xi18nc("@info:progress", "Copying %1 blocks (%2 bytes) from %3 to %4, direction: %5.", blocksToCopy, | | |||
180 | sourceLength, readOffset, writeOffset, copyDirection == 1 ? i18nc("direction: left", "left") | 243 | sourceLength, readOffset, writeOffset, copyDirection == 1 ? i18nc("direction: left", "left") | ||
181 | : i18nc("direction: right", "right")); | 244 | : i18nc("direction: right", "right"))); | ||
182 | | ||||
183 | HelperSupport::progressStep(report); | | |||
184 | 245 | | |||
185 | bool rval = true; | 246 | bool rval = true; | ||
186 | 247 | | |||
187 | while (blocksCopied < blocksToCopy && !targetDevice.isEmpty()) { | 248 | while (blocksCopied < blocksToCopy && !targetDevice.isEmpty()) { | ||
188 | if (!(rval = readData(sourceDevice, buffer, readOffset + blockSize * blocksCopied * copyDirection, blockSize))) | 249 | if (!(rval = readData(sourceDevice, buffer, readOffset + blockSize * blocksCopied * copyDirection, blockSize))) | ||
189 | break; | 250 | break; | ||
190 | 251 | | |||
191 | if (!(rval = writeData(targetDevice, buffer, writeOffset + blockSize * blocksCopied * copyDirection))) | 252 | if (!(rval = writeData(targetDevice, buffer, writeOffset + blockSize * blocksCopied * copyDirection))) | ||
192 | break; | 253 | break; | ||
193 | 254 | | |||
194 | bytesWritten += buffer.size(); | 255 | bytesWritten += buffer.size(); | ||
195 | 256 | | |||
196 | if (++blocksCopied * 100 / blocksToCopy != percent) { | 257 | if (++blocksCopied * 100 / blocksToCopy != percent) { | ||
197 | percent = blocksCopied * 100 / blocksToCopy; | 258 | percent = blocksCopied * 100 / blocksToCopy; | ||
198 | 259 | | |||
199 | if (percent % 5 == 0 && t.elapsed() > 1000) { | 260 | if (percent % 5 == 0 && t.elapsed() > 1000) { | ||
200 | const qint64 mibsPerSec = (blocksCopied * blockSize / 1024 / 1024) / (t.elapsed() / 1000); | 261 | const qint64 mibsPerSec = (blocksCopied * blockSize / 1024 / 1024) / (t.elapsed() / 1000); | ||
201 | const qint64 estSecsLeft = (100 - percent) * t.elapsed() / percent / 1000; | 262 | const qint64 estSecsLeft = (100 - percent) * t.elapsed() / percent / 1000; | ||
202 | report[QStringLiteral("report")]= xi18nc("@info:progress", "Copying %1 MiB/second, estimated time left: %2", mibsPerSec, QTime(0, 0).addSecs(estSecsLeft).toString()); | 263 | | ||
203 | HelperSupport::progressStep(report); | 264 | sendProgress(xi18nc("@info:progress", "Copying %1 MiB/second, estimated time left: %2", mibsPerSec, QTime(0, 0).addSecs(estSecsLeft).toString())); | ||
265 | | ||||
204 | } | 266 | } | ||
205 | HelperSupport::progressStep(percent); | 267 | sendProgress(percent); | ||
206 | } | 268 | } | ||
207 | } | 269 | } | ||
208 | 270 | | |||
209 | // copy the remainder | 271 | // copy the remainder | ||
210 | if (rval && lastBlock > 0) { | 272 | if (rval && lastBlock > 0) { | ||
211 | Q_ASSERT(lastBlock < blockSize); | 273 | Q_ASSERT(lastBlock < blockSize); | ||
212 | 274 | | |||
213 | const qint64 lastBlockReadOffset = copyDirection > 0 ? readOffset + blockSize * blocksCopied : sourceFirstByte; | 275 | const qint64 lastBlockReadOffset = copyDirection > 0 ? readOffset + blockSize * blocksCopied : sourceFirstByte; | ||
214 | const qint64 lastBlockWriteOffset = copyDirection > 0 ? writeOffset + blockSize * blocksCopied : targetFirstByte; | 276 | const qint64 lastBlockWriteOffset = copyDirection > 0 ? writeOffset + blockSize * blocksCopied : targetFirstByte; | ||
215 | report[QStringLiteral("report")]= xi18nc("@info:progress", "Copying remainder of block size %1 from %2 to %3.", lastBlock, lastBlockReadOffset, lastBlockWriteOffset); | 277 | | ||
216 | HelperSupport::progressStep(report); | 278 | sendProgress(xi18nc("@info:progress", "Copying remainder of block size %1 from %2 to %3.", lastBlock, lastBlockReadOffset, lastBlockWriteOffset)); | ||
279 | | ||||
217 | rval = readData(sourceDevice, buffer, lastBlockReadOffset, lastBlock); | 280 | rval = readData(sourceDevice, buffer, lastBlockReadOffset, lastBlock); | ||
218 | 281 | | |||
219 | if (rval) { | 282 | if (rval) { | ||
220 | if (targetDevice.isEmpty()) | 283 | if (targetDevice.isEmpty()) | ||
221 | reply[QStringLiteral("targetByteArray")] = buffer; | 284 | reply[QStringLiteral("targetByteArray")] = buffer; | ||
222 | else | 285 | else | ||
223 | rval = writeData(targetDevice, buffer, lastBlockWriteOffset); | 286 | rval = writeData(targetDevice, buffer, lastBlockWriteOffset); | ||
224 | } | 287 | } | ||
225 | 288 | | |||
Most of this code (except for asyncCall is the same in all cases). It would make sense to factor it out into a separate function which might e.g. take an argument that is passed to emitNewData. If you do just this you might still need two function (one for int overload that passed progress percents, one for the other call with QStrings). It might be possible to unify it more but at leat do that... stikonas: Most of this code (except for asyncCall is the same in all cases).
It would make sense to… | |||||
226 | if (rval) { | 289 | if (rval) { | ||
227 | HelperSupport::progressStep(100); | 290 | sendProgress(100); | ||
228 | bytesWritten += buffer.size(); | 291 | bytesWritten += buffer.size(); | ||
229 | } | 292 | } | ||
230 | } | 293 | } | ||
231 | 294 | | |||
232 | report[QStringLiteral("report")] = xi18ncp("@info:progress argument 2 is a string such as 7 bytes (localized accordingly)", "Copying 1 block (%2) finished.", "Copying %1 blocks (%2) finished.", blocksCopied, i18np("1 byte", "%1 bytes", bytesWritten)); | 295 | sendProgress(xi18ncp("@info:progress argument 2 is a string such as 7 bytes (localized accordingly)", "Copying 1 block (%2) finished.", "Copying %1 blocks (%2) finished.", blocksCopied, i18np("1 byte", "%1 bytes", bytesWritten))); | ||
233 | HelperSupport::progressStep(report); | | |||
234 | 296 | | |||
235 | reply[QStringLiteral("success")] = rval; | 297 | reply[QStringLiteral("success")] = rval; | ||
236 | return reply; | 298 | return reply; | ||
237 | } | 299 | } | ||
238 | 300 | | |||
239 | bool ExternalCommandHelper::writeData(const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte) | 301 | bool ExternalCommandHelper::writeData(const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte) | ||
240 | { | 302 | { | ||
241 | // Do not allow using this helper for writing to arbitrary location | 303 | // Do not allow using this helper for writing to arbitrary location | ||
Show All 13 Lines | 312 | { | |||
255 | if (command.isEmpty()) { | 317 | if (command.isEmpty()) { | ||
256 | reply[QStringLiteral("success")] = false; | 318 | reply[QStringLiteral("success")] = false; | ||
257 | return reply; | 319 | return reply; | ||
258 | } | 320 | } | ||
259 | 321 | | |||
260 | // Compare with command whitelist | 322 | // Compare with command whitelist | ||
261 | QString basename = command.mid(command.lastIndexOf(QLatin1Char('/')) + 1); | 323 | QString basename = command.mid(command.lastIndexOf(QLatin1Char('/')) + 1); | ||
262 | if (std::find(std::begin(allowedCommands), std::end(allowedCommands), basename) == std::end(allowedCommands)) { | 324 | if (std::find(std::begin(allowedCommands), std::end(allowedCommands), basename) == std::end(allowedCommands)) { | ||
263 | qInfo() << command <<" command is not one of the whitelisted command"; | 325 | qInfo() << command <<"Command is not one of the whitelisted command"; | ||
264 | m_loop->exit(); | 326 | m_loop->exit(); | ||
265 | reply[QStringLiteral("success")] = false; | 327 | reply[QStringLiteral("success")] = false; | ||
266 | return reply; | 328 | return reply; | ||
267 | } | 329 | } | ||
268 | 330 | | |||
269 | // connect(&cmd, &QProcess::readyReadStandardOutput, this, &ExternalCommandHelper::onReadOutput); | 331 | // connect(&cmd, &QProcess::readyReadStandardOutput, this, &ExternalCommandHelper::onReadOutput); | ||
270 | 332 | | |||
271 | m_cmd.setEnvironment( { QStringLiteral("LVM_SUPPRESS_FD_WARNINGS=1") } ); | 333 | m_cmd.setEnvironment( { QStringLiteral("LVM_SUPPRESS_FD_WARNINGS=1") } ); | ||
272 | m_cmd.setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(processChannelMode)); | 334 | m_cmd.setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(processChannelMode)); | ||
273 | m_cmd.start(command, arguments); | 335 | m_cmd.start(command, arguments); | ||
274 | m_cmd.write(input); | 336 | m_cmd.write(input); | ||
275 | m_cmd.closeWriteChannel(); | 337 | m_cmd.closeWriteChannel(); | ||
276 | m_cmd.waitForFinished(-1); | 338 | m_cmd.waitForFinished(-1); | ||
339 | | ||||
277 | QByteArray output = m_cmd.readAllStandardOutput(); | 340 | QByteArray output = m_cmd.readAllStandardOutput(); | ||
341 | | ||||
278 | reply[QStringLiteral("output")] = output; | 342 | reply[QStringLiteral("output")] = output; | ||
279 | reply[QStringLiteral("exitCode")] = m_cmd.exitCode(); | 343 | reply[QStringLiteral("exitCode")] = m_cmd.exitCode(); | ||
280 | 344 | | |||
281 | return reply; | 345 | return reply; | ||
282 | } | 346 | } | ||
283 | 347 | | |||
284 | void ExternalCommandHelper::exit() | 348 | void ExternalCommandHelper::exit() | ||
285 | { | 349 | { | ||
286 | m_loop->exit(); | 350 | m_loop->exit(); | ||
287 | 351 | | |||
288 | QDBusConnection::systemBus().unregisterObject(QStringLiteral("/Helper")); | 352 | QDBusConnection::systemBus().unregisterObject(QStringLiteral("/Helper")); | ||
289 | QDBusConnection::systemBus().unregisterService(QStringLiteral("org.kde.kpmcore.helperinterface")); | 353 | QDBusConnection::systemBus().unregisterService(QStringLiteral("org.kde.kpmcore.helperinterface")); | ||
290 | } | 354 | } | ||
291 | 355 | | |||
292 | void ExternalCommandHelper::onReadOutput() | 356 | /*void ExternalCommandHelper::onReadOutput() | ||
293 | { | 357 | { | ||
294 | /* const QByteArray s = cmd.readAllStandardOutput(); | 358 | const QByteArray s = cmd.readAllStandardOutput(); | ||
295 | 359 | | |||
296 | if(output.length() > 10*1024*1024) { // prevent memory overflow for badly corrupted file systems | 360 | if(output.length() > 10*1024*1024) { // prevent memory overflow for badly corrupted file systems | ||
297 | if (report()) | 361 | if (report()) | ||
298 | report()->line() << xi18nc("@info:status", "(Command is printing too much output)"); | 362 | report()->line() << xi18nc("@info:status", "(Command is printing too much output)"); | ||
299 | return; | 363 | return; | ||
300 | } | 364 | } | ||
301 | 365 | | |||
302 | output += s; | 366 | output += s; | ||
303 | 367 | | |||
304 | if (report()) | 368 | if (report()) | ||
305 | *report() << QString::fromLocal8Bit(s);*/ | 369 | *report() << QString::fromLocal8Bit(s); | ||
306 | } | 370 | }*/ | ||
307 | 371 | | |||
308 | KAUTH_HELPER_MAIN("org.kde.kpmcore.externalcommand", ExternalCommandHelper) | 372 | HELPER_MAIN() |
One should register objects before registering the service as the service might become visible to the bus in an inconsistent state (i.e. show up before the objects are registered)