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 <shubham.jangra@kdemail.net> * | ||||
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" | ||
19 | #include "externalcommand_whitelist.h" | 20 | #include "externalcommand_whitelist.h" | ||
20 | 21 | | |||
21 | #include <QtDBus> | | |||
22 | #include <QCoreApplication> | 22 | #include <QCoreApplication> | ||
23 | #include <QtDBus> | ||||
23 | #include <QDebug> | 24 | #include <QDebug> | ||
24 | #include <QFile> | 25 | #include <QFile> | ||
25 | #include <QString> | 26 | #include <QString> | ||
26 | #include <QTime> | 27 | #include <QTime> | ||
27 | #include <QVariant> | 28 | #include <QVariant> | ||
28 | 29 | | |||
30 | #include <KAuth> | ||||
29 | #include <KLocalizedString> | 31 | #include <KLocalizedString> | ||
30 | 32 | | |||
31 | /** Initialize ExternalCommandHelper Daemon and prepare DBus interface | 33 | /** Initialize ExternalCommandHelper Daemon and prepare DBus interface | ||
32 | * | 34 | * | ||
33 | * KAuth helper runs in the background until application exits. | 35 | * Helper runs in the background until application exits. | ||
34 | * To avoid forever running helper in case of application crash | 36 | * To avoid forever running helper in case of application crash | ||
35 | * ExternalCommand class opens a DBus service that we monitor for changes. | 37 | * 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 | 38 | * If helper is not busy then it exits when the client services gets | ||
37 | * unregistered. Otherwise, | 39 | * unregistered. Otherwise, | ||
38 | * we wait for the current job to finish before exiting, so even in case | 40 | * 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. | 41 | * of main application crash, we do not leave partially moved data. | ||
40 | * | 42 | * | ||
41 | * This helper also starts another DBus interface where it listens to | 43 | * This helper also starts another DBus interface where it listens to | ||
42 | * command execution requests from the application that started the helper. | 44 | * command execution requests from the application that started the helper. | ||
43 | * | 45 | * | ||
44 | */ | 46 | */ | ||
45 | ActionReply ExternalCommandHelper::init(const QVariantMap& args) | | |||
46 | { | | |||
47 | Q_UNUSED(args) | | |||
48 | | ||||
49 | ActionReply reply; | | |||
50 | 47 | | |||
48 | QVariantMap ExternalCommandHelper::init(QVariantMap reply) | ||||
49 | { | ||||
51 | if (!QDBusConnection::systemBus().isConnected() || !QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.helperinterface")) || | 50 | if (!QDBusConnection::systemBus().isConnected() || !QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.helperinterface")) || | ||
52 | !QDBusConnection::systemBus().registerObject(QStringLiteral("/Helper"), this, QDBusConnection::ExportAllSlots)) { | 51 | !QDBusConnection::systemBus().registerObject(QStringLiteral("/Helper"), this, QDBusConnection::ExportAllSlots)) { | ||
53 | qWarning() << QDBusConnection::systemBus().lastError().message(); | 52 | qWarning() << QDBusConnection::systemBus().lastError().message(); | ||
54 | reply.addData(QStringLiteral("success"), false); | 53 | reply[QStringLiteral("success")] = false; | ||
55 | 54 | | |||
56 | // Also end the application loop started by KAuth's main() code. Our loop | 55 | // Also end the application loop started by KAuth's main() code. Our loop | ||
57 | // exits when our client disappears. Without client we have no reason to | 56 | // exits when our client disappears. Without client we have no reason to | ||
58 | // live. | 57 | // live. | ||
59 | qApp->quit(); | 58 | qApp->quit(); | ||
60 | 59 | | |||
61 | return reply; | 60 | return reply; | ||
62 | } | 61 | } | ||
63 | 62 | | |||
64 | m_loop = std::make_unique<QEventLoop>(); | 63 | m_loop = std::make_unique<QEventLoop>(); | ||
65 | HelperSupport::progressStep(QVariantMap()); | 64 | emit reportProgress(QVariantMap()); | ||
66 | 65 | | |||
67 | // End the loop and return only once the client is done using us. | 66 | // QDBus Srvice watcher which keeps an eye on the client(Main GUI app) | ||
68 | auto serviceWatcher = | 67 | // End the loop and return only once the client has unregistered over the QDBus. | ||
69 | new QDBusServiceWatcher(QStringLiteral("org.kde.kpmcore.applicationinterface"), | 68 | auto serviceWatcher = new QDBusServiceWatcher(QStringLiteral("org.kde.kpmcore.applicationinterface"), | ||
70 | QDBusConnection::systemBus(), | 69 | QDBusConnection::systemBus(), | ||
71 | QDBusServiceWatcher::WatchForUnregistration, | 70 | QDBusServiceWatcher::WatchForUnregistration, | ||
72 | this); | 71 | this); | ||
72 | | ||||
73 | connect(serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, | 73 | connect(serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, | ||
74 | [this]() { | 74 | [this]() { | ||
75 | m_loop->exit(); | 75 | m_loop->exit(); | ||
76 | }); | 76 | }); | ||
77 | 77 | | |||
78 | m_loop->exec(); | 78 | m_loop->exec(); | ||
79 | reply.addData(QStringLiteral("success"), true); | 79 | reply[QStringLiteral("success")] = true; | ||
80 | 80 | | |||
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(); | 81 | qApp->quit(); | ||
85 | 82 | | |||
86 | return reply; | 83 | return reply; | ||
87 | } | 84 | } | ||
88 | 85 | | |||
89 | 86 | | |||
90 | /** Reads the given number of bytes from the sourceDevice into the given buffer. | 87 | /** Reads the given number of bytes from the sourceDevice into the given buffer. | ||
91 | @param sourceDevice device or file to read from | 88 | @param sourceDevice device or file to read from | ||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Line(s) | 148 | { | |||
175 | t.start(); | 172 | t.start(); | ||
176 | 173 | | |||
177 | QVariantMap report; | 174 | QVariantMap report; | ||
178 | 175 | | |||
179 | report[QStringLiteral("report")] = xi18nc("@info:progress", "Copying %1 blocks (%2 bytes) from %3 to %4, direction: %5.", blocksToCopy, | 176 | 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") | 177 | sourceLength, readOffset, writeOffset, copyDirection == 1 ? i18nc("direction: left", "left") | ||
181 | : i18nc("direction: right", "right")); | 178 | : i18nc("direction: right", "right")); | ||
182 | 179 | | |||
183 | HelperSupport::progressStep(report); | 180 | //HelperSupport::progressStep(report); | ||
181 | //emit reportProgress(report); | ||||
184 | 182 | | |||
185 | bool rval = true; | 183 | bool rval = true; | ||
186 | 184 | | |||
187 | while (blocksCopied < blocksToCopy && !targetDevice.isEmpty()) { | 185 | while (blocksCopied < blocksToCopy && !targetDevice.isEmpty()) { | ||
188 | if (!(rval = readData(sourceDevice, buffer, readOffset + blockSize * blocksCopied * copyDirection, blockSize))) | 186 | if (!(rval = readData(sourceDevice, buffer, readOffset + blockSize * blocksCopied * copyDirection, blockSize))) | ||
189 | break; | 187 | break; | ||
190 | 188 | | |||
191 | if (!(rval = writeData(targetDevice, buffer, writeOffset + blockSize * blocksCopied * copyDirection))) | 189 | if (!(rval = writeData(targetDevice, buffer, writeOffset + blockSize * blocksCopied * copyDirection))) | ||
192 | break; | 190 | break; | ||
193 | 191 | | |||
194 | bytesWritten += buffer.size(); | 192 | bytesWritten += buffer.size(); | ||
195 | 193 | | |||
196 | if (++blocksCopied * 100 / blocksToCopy != percent) { | 194 | if (++blocksCopied * 100 / blocksToCopy != percent) { | ||
197 | percent = blocksCopied * 100 / blocksToCopy; | 195 | percent = blocksCopied * 100 / blocksToCopy; | ||
198 | 196 | | |||
199 | if (percent % 5 == 0 && t.elapsed() > 1000) { | 197 | if (percent % 5 == 0 && t.elapsed() > 1000) { | ||
200 | const qint64 mibsPerSec = (blocksCopied * blockSize / 1024 / 1024) / (t.elapsed() / 1000); | 198 | const qint64 mibsPerSec = (blocksCopied * blockSize / 1024 / 1024) / (t.elapsed() / 1000); | ||
201 | const qint64 estSecsLeft = (100 - percent) * t.elapsed() / percent / 1000; | 199 | 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()); | 200 | report[QStringLiteral("report")]= xi18nc("@info:progress", "Copying %1 MiB/second, estimated time left: %2", mibsPerSec, QTime(0, 0).addSecs(estSecsLeft).toString()); | ||
203 | HelperSupport::progressStep(report); | 201 | emit reportProgress(report); | ||
204 | } | 202 | } | ||
205 | HelperSupport::progressStep(percent); | 203 | emit progress(percent); | ||
206 | } | 204 | } | ||
207 | } | 205 | } | ||
208 | 206 | | |||
209 | // copy the remainder | 207 | // copy the remainder | ||
210 | if (rval && lastBlock > 0) { | 208 | if (rval && lastBlock > 0) { | ||
211 | Q_ASSERT(lastBlock < blockSize); | 209 | Q_ASSERT(lastBlock < blockSize); | ||
212 | 210 | | |||
213 | const qint64 lastBlockReadOffset = copyDirection > 0 ? readOffset + blockSize * blocksCopied : sourceFirstByte; | 211 | const qint64 lastBlockReadOffset = copyDirection > 0 ? readOffset + blockSize * blocksCopied : sourceFirstByte; | ||
214 | const qint64 lastBlockWriteOffset = copyDirection > 0 ? writeOffset + blockSize * blocksCopied : targetFirstByte; | 212 | 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); | 213 | report[QStringLiteral("report")]= xi18nc("@info:progress", "Copying remainder of block size %1 from %2 to %3.", lastBlock, lastBlockReadOffset, lastBlockWriteOffset); | ||
216 | HelperSupport::progressStep(report); | 214 | emit reportProgress(report); | ||
217 | rval = readData(sourceDevice, buffer, lastBlockReadOffset, lastBlock); | 215 | rval = readData(sourceDevice, buffer, lastBlockReadOffset, lastBlock); | ||
218 | 216 | | |||
219 | if (rval) { | 217 | if (rval) { | ||
220 | if (targetDevice.isEmpty()) | 218 | if (targetDevice.isEmpty()) | ||
221 | reply[QStringLiteral("targetByteArray")] = buffer; | 219 | reply[QStringLiteral("targetByteArray")] = buffer; | ||
222 | else | 220 | else | ||
223 | rval = writeData(targetDevice, buffer, lastBlockWriteOffset); | 221 | rval = writeData(targetDevice, buffer, lastBlockWriteOffset); | ||
224 | } | 222 | } | ||
225 | 223 | | |||
226 | if (rval) { | 224 | if (rval) { | ||
227 | HelperSupport::progressStep(100); | 225 | //HelperSupport::progressStep(100); | ||
226 | emit progress(100); | ||||
228 | bytesWritten += buffer.size(); | 227 | bytesWritten += buffer.size(); | ||
229 | } | 228 | } | ||
230 | } | 229 | } | ||
231 | 230 | | |||
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)); | 231 | 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)); | ||
233 | HelperSupport::progressStep(report); | 232 | emit reportProgress(report); | ||
234 | 233 | | |||
235 | reply[QStringLiteral("success")] = rval; | 234 | reply[QStringLiteral("success")] = rval; | ||
236 | return reply; | 235 | return reply; | ||
237 | } | 236 | } | ||
238 | 237 | | |||
239 | bool ExternalCommandHelper::writeData(const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte) | 238 | bool ExternalCommandHelper::writeData(const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte) | ||
240 | { | 239 | { | ||
241 | // Do not allow using this helper for writing to arbitrary location | 240 | // Do not allow using this helper for writing to arbitrary location | ||
Show All 13 Lines | 249 | { | |||
255 | if (command.isEmpty()) { | 254 | if (command.isEmpty()) { | ||
256 | reply[QStringLiteral("success")] = false; | 255 | reply[QStringLiteral("success")] = false; | ||
257 | return reply; | 256 | return reply; | ||
258 | } | 257 | } | ||
259 | 258 | | |||
260 | // Compare with command whitelist | 259 | // Compare with command whitelist | ||
261 | QString basename = command.mid(command.lastIndexOf(QLatin1Char('/')) + 1); | 260 | QString basename = command.mid(command.lastIndexOf(QLatin1Char('/')) + 1); | ||
262 | if (std::find(std::begin(allowedCommands), std::end(allowedCommands), basename) == std::end(allowedCommands)) { | 261 | if (std::find(std::begin(allowedCommands), std::end(allowedCommands), basename) == std::end(allowedCommands)) { | ||
263 | qInfo() << command <<" command is not one of the whitelisted command"; | 262 | qInfo() << command <<"Command is not one of the whitelisted command"; | ||
264 | m_loop->exit(); | 263 | m_loop->exit(); | ||
265 | reply[QStringLiteral("success")] = false; | 264 | reply[QStringLiteral("success")] = false; | ||
266 | return reply; | 265 | return reply; | ||
267 | } | 266 | } | ||
268 | 267 | | |||
269 | // connect(&cmd, &QProcess::readyReadStandardOutput, this, &ExternalCommandHelper::onReadOutput); | 268 | // connect(&cmd, &QProcess::readyReadStandardOutput, this, &ExternalCommandHelper::onReadOutput); | ||
270 | 269 | | |||
271 | m_cmd.setEnvironment( { QStringLiteral("LVM_SUPPRESS_FD_WARNINGS=1") } ); | 270 | m_cmd.setEnvironment( { QStringLiteral("LVM_SUPPRESS_FD_WARNINGS=1") } ); | ||
272 | m_cmd.setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(processChannelMode)); | 271 | m_cmd.setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(processChannelMode)); | ||
273 | m_cmd.start(command, arguments); | 272 | m_cmd.start(command, arguments); | ||
274 | m_cmd.write(input); | 273 | m_cmd.write(input); | ||
275 | m_cmd.closeWriteChannel(); | 274 | m_cmd.closeWriteChannel(); | ||
276 | m_cmd.waitForFinished(-1); | 275 | m_cmd.waitForFinished(-1); | ||
276 | | ||||
277 | QByteArray output = m_cmd.readAllStandardOutput(); | 277 | QByteArray output = m_cmd.readAllStandardOutput(); | ||
278 | reply[QStringLiteral("output")] = output; | 278 | reply[QStringLiteral("output")] = output; | ||
279 | reply[QStringLiteral("exitCode")] = m_cmd.exitCode(); | 279 | reply[QStringLiteral("exitCode")] = m_cmd.exitCode(); | ||
280 | 280 | | |||
281 | return reply; | 281 | return reply; | ||
282 | } | 282 | } | ||
283 | 283 | | |||
284 | void ExternalCommandHelper::exit() | 284 | void ExternalCommandHelper::exit() | ||
285 | { | 285 | { | ||
286 | m_loop->exit(); | 286 | m_loop->exit(); | ||
287 | 287 | | |||
288 | QDBusConnection::systemBus().unregisterObject(QStringLiteral("/Helper")); | 288 | QDBusConnection::systemBus().unregisterObject(QStringLiteral("/Helper")); | ||
289 | QDBusConnection::systemBus().unregisterService(QStringLiteral("org.kde.kpmcore.helperinterface")); | 289 | QDBusConnection::systemBus().unregisterService(QStringLiteral("org.kde.kpmcore.helperinterface")); | ||
290 | | ||||
291 | emit quit(); | ||||
290 | } | 292 | } | ||
291 | 293 | | |||
292 | void ExternalCommandHelper::onReadOutput() | 294 | /*void ExternalCommandHelper::onReadOutput() | ||
293 | { | 295 | { | ||
294 | /* const QByteArray s = cmd.readAllStandardOutput(); | 296 | const QByteArray s = cmd.readAllStandardOutput(); | ||
295 | 297 | | |||
296 | if(output.length() > 10*1024*1024) { // prevent memory overflow for badly corrupted file systems | 298 | if(output.length() > 10*1024*1024) { // prevent memory overflow for badly corrupted file systems | ||
297 | if (report()) | 299 | if (report()) | ||
298 | report()->line() << xi18nc("@info:status", "(Command is printing too much output)"); | 300 | report()->line() << xi18nc("@info:status", "(Command is printing too much output)"); | ||
299 | return; | 301 | return; | ||
300 | } | 302 | } | ||
301 | 303 | | |||
302 | output += s; | 304 | output += s; | ||
303 | 305 | | |||
304 | if (report()) | 306 | if (report()) | ||
305 | *report() << QString::fromLocal8Bit(s);*/ | 307 | *report() << QString::fromLocal8Bit(s); | ||
306 | } | 308 | }*/ | ||
307 | 309 | | |||
308 | KAUTH_HELPER_MAIN("org.kde.kpmcore.externalcommand", ExternalCommandHelper) | 310 | KAUTH_HELPER_MAIN("org.kde.kpmcore.externalcommand", ExternalCommandHelper) | ||
311 | | ||||
312 | #include "moc_externalcommandhelper.cpp" |