2017-11-07 22:55:28 +00:00
/*************************************************************************
* Copyright ( C ) 2017 by Andrius Š tikonas < andrius @ stikonas . eu > *
* *
* This program is free software ; you can redistribute it and / or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation ; either version 3 of *
* the License , or ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program . If not , see < http : //www.gnu.org/licenses/>.*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "externalcommandhelper.h"
2018-03-19 10:33:20 +00:00
# include <QtDBus>
# include <QDBusContext>
2017-11-07 22:55:28 +00:00
# include <QDebug>
2018-02-03 15:57:47 +00:00
# include <QFile>
2018-01-24 15:22:42 +00:00
# include <QString>
2018-01-27 18:54:48 +00:00
# include <QTime>
# include <QVariant>
# include <KLocalizedString>
2018-03-19 10:33:20 +00:00
/** Initialize ExternalCommandHelper Daemon and prepare DBus interface
*/
ActionReply ExternalCommandHelper : : init ( const QVariantMap & args )
{
ActionReply reply ;
if ( ! QDBusConnection : : systemBus ( ) . isConnected ( ) ) {
qWarning ( ) < < " Could not connect to DBus session bus " ;
reply . addData ( QStringLiteral ( " success " ) , false ) ;
return reply ;
}
m_callerUuid = args [ QStringLiteral ( " callerUuid " ) ] . toString ( ) ;
if ( ! QDBusConnection : : systemBus ( ) . registerService ( QStringLiteral ( " org.kde.kpmcore.helperinterface " ) ) ) {
qWarning ( ) < < QDBusConnection : : systemBus ( ) . lastError ( ) . message ( ) ;
reply . addData ( QStringLiteral ( " success " ) , false ) ;
return reply ;
}
QDBusConnection : : systemBus ( ) . registerObject ( QStringLiteral ( " /Helper " ) , this , QDBusConnection : : ExportAllSlots ) ;
2018-03-19 15:21:57 +00:00
HelperSupport : : progressStep ( QVariantMap ( ) ) ;
2018-03-19 10:33:20 +00:00
m_loop . exec ( ) ;
reply . addData ( QStringLiteral ( " success " ) , true ) ;
return reply ;
}
2018-02-06 16:48:02 +00:00
/** Reads the given number of bytes from the sourceDevice into the given buffer.
@ param sourceDevice device or file to read from
@ param buffer buffer to store the bytes read in
@ param offset offset where to begin reading
@ param size the number of bytes to read
@ return true on success
*/
2018-03-21 17:01:40 +00:00
bool ExternalCommandHelper : : readData ( const QString & sourceDevice , QByteArray & buffer , qint64 offset , qint64 size )
2018-01-24 15:22:42 +00:00
{
2018-02-05 12:58:37 +00:00
QFile device ( sourceDevice ) ;
2018-01-24 15:22:42 +00:00
2018-02-05 12:58:37 +00:00
if ( ! device . open ( QIODevice : : ReadOnly | QIODevice : : Unbuffered ) ) {
qCritical ( ) < < xi18n ( " Could not open device <filename>%1</filename> for reading. " , sourceDevice ) ;
return false ;
}
2018-01-24 15:22:42 +00:00
2018-02-05 12:58:37 +00:00
if ( ! device . seek ( offset ) ) {
qCritical ( ) < < xi18n ( " Could not seek position %1 on device <filename>%1</filename>. " , sourceDevice ) ;
return false ;
2018-01-24 15:22:42 +00:00
}
2018-02-05 12:58:37 +00:00
buffer = device . read ( size ) ;
if ( size ! = buffer . size ( ) ) {
qCritical ( ) < < xi18n ( " Could not read from device <filename>%1</filename>. " , sourceDevice ) ;
return false ;
}
return true ;
2018-01-24 15:22:42 +00:00
}
2018-02-06 16:48:02 +00:00
/** Writes the data from buffer to a given device or file.
@ param targetDevice device or file to write to
@ param buffer the data that we write
@ param offset offset where to begin writing
@ return true on success
*/
2018-03-21 17:01:40 +00:00
bool ExternalCommandHelper : : writeData ( const QString & targetDevice , const QByteArray & buffer , qint64 offset )
2018-01-24 15:22:42 +00:00
{
2018-02-03 15:57:47 +00:00
QFile device ( targetDevice ) ;
2018-02-05 12:46:10 +00:00
if ( ! device . open ( QIODevice : : WriteOnly | QIODevice : : Append | QIODevice : : Unbuffered ) ) {
2018-02-03 15:57:47 +00:00
qCritical ( ) < < xi18n ( " Could not open device <filename>%1</filename> for writing. " , targetDevice ) ;
return false ;
}
2018-01-24 15:22:42 +00:00
2018-02-03 15:57:47 +00:00
if ( ! device . seek ( offset ) ) {
qCritical ( ) < < xi18n ( " Could not seek position %1 on device <filename>%1</filename>. " , targetDevice ) ;
return false ;
2018-01-24 15:22:42 +00:00
}
2018-02-03 15:57:47 +00:00
if ( device . write ( buffer ) ! = buffer . size ( ) ) {
qCritical ( ) < < xi18n ( " Could not write to device <filename>%1</filename>. " , targetDevice ) ;
return false ;
}
return true ;
2018-01-24 15:22:42 +00:00
}
2018-03-21 20:51:30 +00:00
bool ExternalCommandHelper : : copyblocks ( const QString & Uuid , const QString & sourceDevice , const qint64 sourceFirstByte , const qint64 sourceLength , const QString & targetDevice , const qint64 targetFirstByte , const qint64 blockSize )
2018-01-24 15:22:42 +00:00
{
2018-03-21 17:01:40 +00:00
isCallerAuthorized ( Uuid ) ;
2018-01-24 15:22:42 +00:00
2018-03-21 17:01:40 +00:00
const qint64 blocksToCopy = sourceLength / blockSize ;
qint64 readOffset = sourceFirstByte ;
qint64 writeOffset = targetFirstByte ;
qint32 copyDirection = 1 ;
2018-01-24 15:22:42 +00:00
2018-03-21 17:01:40 +00:00
if ( targetFirstByte > sourceFirstByte ) {
readOffset = sourceFirstByte + sourceLength - blockSize ;
writeOffset = targetFirstByte + sourceLength - blockSize ;
copyDirection = - 1 ;
}
const qint64 lastBlock = sourceLength % blockSize ;
2018-01-24 15:22:42 +00:00
2018-01-27 18:54:48 +00:00
qint64 bytesWritten = 0 ;
2018-01-24 15:22:42 +00:00
qint64 blocksCopied = 0 ;
2018-01-24 15:35:11 +00:00
QByteArray buffer ;
2018-01-24 15:22:42 +00:00
int percent = 0 ;
2018-01-27 18:54:48 +00:00
QTime t ;
t . start ( ) ;
QVariantMap report ;
report [ QStringLiteral ( " report " ) ] = xi18nc ( " @info:progress " , " Copying %1 blocks (%2 bytes) from %3 to %4, direction: %5. " , blocksToCopy ,
sourceLength , readOffset , writeOffset , copyDirection = = 1 ? i18nc ( " direction: left " , " left " )
: i18nc ( " direction: right " , " right " ) ) ;
HelperSupport : : progressStep ( report ) ;
2018-01-24 15:22:42 +00:00
bool rval = true ;
2018-01-27 18:54:48 +00:00
2018-01-24 15:22:42 +00:00
while ( blocksCopied < blocksToCopy ) {
2018-02-03 15:57:47 +00:00
if ( ! ( rval = readData ( sourceDevice , buffer , readOffset + blockSize * blocksCopied * copyDirection , blockSize ) ) )
2018-01-24 15:22:42 +00:00
break ;
2018-02-03 15:57:47 +00:00
if ( ! ( rval = writeData ( targetDevice , buffer , writeOffset + blockSize * blocksCopied * copyDirection ) ) )
2018-01-24 15:22:42 +00:00
break ;
2018-01-27 18:54:48 +00:00
bytesWritten + = buffer . size ( ) ;
2018-01-24 15:22:42 +00:00
if ( + + blocksCopied * 100 / blocksToCopy ! = percent ) {
percent = blocksCopied * 100 / blocksToCopy ;
2018-01-27 18:54:48 +00:00
if ( percent % 5 = = 0 & & t . elapsed ( ) > 1000 ) {
const qint64 mibsPerSec = ( blocksCopied * blockSize / 1024 / 1024 ) / ( t . elapsed ( ) / 1000 ) ;
const qint64 estSecsLeft = ( 100 - percent ) * t . elapsed ( ) / percent / 1000 ;
report [ QStringLiteral ( " report " ) ] = xi18nc ( " @info:progress " , " Copying %1 MiB/second, estimated time left: %2 " , mibsPerSec , QTime ( 0 , 0 ) . addSecs ( estSecsLeft ) . toString ( ) ) ;
HelperSupport : : progressStep ( report ) ;
}
2018-01-24 15:22:42 +00:00
HelperSupport : : progressStep ( percent ) ;
}
}
// copy the remainder
if ( rval & & lastBlock > 0 ) {
Q_ASSERT ( lastBlock < blockSize ) ;
const qint64 lastBlockReadOffset = copyDirection > 0 ? readOffset + blockSize * blocksCopied : sourceFirstByte ;
const qint64 lastBlockWriteOffset = copyDirection > 0 ? writeOffset + blockSize * blocksCopied : targetFirstByte ;
2018-01-27 18:54:48 +00:00
report [ QStringLiteral ( " report " ) ] = xi18nc ( " @info:progress " , " Copying remainder of block size %1 from %2 to %3. " , lastBlock , lastBlockReadOffset , lastBlockWriteOffset ) ;
HelperSupport : : progressStep ( report ) ;
2018-02-03 15:57:47 +00:00
rval = readData ( sourceDevice , buffer , lastBlockReadOffset , lastBlock ) ;
2018-01-24 15:22:42 +00:00
if ( rval )
2018-02-03 15:57:47 +00:00
rval = writeData ( targetDevice , buffer , lastBlockWriteOffset ) ;
2018-01-24 15:22:42 +00:00
2018-01-27 18:54:48 +00:00
if ( rval ) {
HelperSupport : : progressStep ( 100 ) ;
bytesWritten + = buffer . size ( ) ;
}
2018-01-24 15:22:42 +00:00
}
2018-01-27 18:54:48 +00:00
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 ) ) ;
HelperSupport : : progressStep ( report ) ;
2018-01-24 15:22:42 +00:00
2018-03-21 20:51:30 +00:00
return rval ;
2018-01-24 15:22:42 +00:00
}
2017-11-07 22:55:28 +00:00
2018-03-19 10:33:20 +00:00
QVariantMap ExternalCommandHelper : : start ( const QString & Uuid , const QString & command , const QStringList & arguments , const QByteArray & input , const QStringList & environment )
2017-11-07 22:55:28 +00:00
{
2018-03-21 17:01:40 +00:00
isCallerAuthorized ( Uuid ) ;
2018-03-19 10:33:20 +00:00
QVariantMap reply ;
2017-11-07 22:55:28 +00:00
// connect(&cmd, &QProcess::readyReadStandardOutput, this, &ExternalCommandHelper::onReadOutput);
2018-03-19 10:33:20 +00:00
m_cmd . setEnvironment ( environment ) ;
m_cmd . start ( command , arguments ) ;
m_cmd . write ( input ) ;
m_cmd . closeWriteChannel ( ) ;
m_cmd . waitForFinished ( - 1 ) ;
QByteArray output = m_cmd . readAllStandardOutput ( ) ;
reply [ QStringLiteral ( " output " ) ] = output ;
reply [ QStringLiteral ( " exitCode " ) ] = m_cmd . exitCode ( ) ;
2017-11-07 22:55:28 +00:00
return reply ;
}
2018-03-21 17:01:40 +00:00
bool ExternalCommandHelper : : isCallerAuthorized ( const QString & Uuid )
{
if ( Uuid ! = m_callerUuid ) {
qWarning ( ) < < " Caller is not authorized " ;
return false ;
}
return true ;
}
2018-03-19 10:33:20 +00:00
void ExternalCommandHelper : : exit ( const QString & Uuid )
{
2018-03-21 17:01:40 +00:00
isCallerAuthorized ( Uuid ) ;
2018-03-19 10:33:20 +00:00
m_loop . exit ( ) ;
2018-03-22 05:32:59 +00:00
if ( QDBusConnection : : systemBus ( ) . unregisterService ( QStringLiteral ( " org.kde.kpmcore.helperinterface " ) ) )
qDebug ( ) < < " org.kde.kpmcore.helperinterface unregistered " ;
QDBusConnection : : systemBus ( ) . unregisterObject ( QStringLiteral ( " /Helper " ) ) ;
2018-03-19 10:33:20 +00:00
}
2017-11-07 22:55:28 +00:00
void ExternalCommandHelper : : onReadOutput ( )
{
// const QByteArray s = cmd.readAllStandardOutput();
// if(output.length() > 10*1024*1024) { // prevent memory overflow for badly corrupted file systems
// if (report())
// report()->line() << xi18nc("@info:status", "(Command is printing too much output)");
// return;
// }
// output += s;
// if (report())
// *report() << QString::fromLocal8Bit(s);
}
KAUTH_HELPER_MAIN ( " org.kde.kpmcore.externalcommand " , ExternalCommandHelper )