Index: sources/device/DeviceController.cpp =================================================================== diff -u -r5687815256ae070a9a207107088e3f72dd464da0 -rca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0 --- sources/device/DeviceController.cpp (.../DeviceController.cpp) (revision 5687815256ae070a9a207107088e3f72dd464da0) +++ sources/device/DeviceController.cpp (.../DeviceController.cpp) (revision ca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0) @@ -12,1305 +12,1326 @@ * \date (original) 03-Jun-2021 * */ -#include "DeviceController.h" - -// Linux -#include -#include - -// Qt -#include -#include -#include - -// Project -#include "Threads.h" -#include "StorageGlobals.h" -#include "Logger.h" -#include "CloudSyncController.h" -#include "ApplicationController.h" -#include "FileHandler.h" -#include "DeviceModels.h" -#include "Settings.h" -#include "encryption.h" - -// namespace -using namespace Model; -using namespace Device; -using namespace Storage; - -/*! - * \brief DeviceController::DeviceController - * \details Constructor - * \param parent - QObject parent owner object. - * Qt handles the children destruction by their parent objects life-cycle. - */ -DeviceController::DeviceController(QObject *parent) : QObject(parent) { - _fileSystemWatcher.setParent(this); - DEVICE_DEV_PARENT_LIST -} - -/*! - * \brief DeviceController::init - * \details Initializes the class by setting the connections and starting the timer - * \return False if it has been called before. - */ -bool DeviceController::init() -{ - if ( _init ) return false; - _init = true; - - // runs in DeviceController thread - initConnections(); - startTimer(_interval); - - return true; -} - -/*! - * \brief DeviceController::init - * \details Initialized the Class by calling the init() method first - * And initializes the thread vThread by calling initThread - * on success init(). - * \param vThread - the thread - * \return returns the return value of the init() method - */ -bool DeviceController::init(QThread &vThread) -{ - if ( ! init() ) return false; - initThread(vThread); - return true; -} - -/*! - * \brief DeviceController::quit - * \details quits the class - * Calls quitThread - */ -void DeviceController::quit() -{ - quitThread(); -} - - -/*! - * \brief DeviceController::initConnections - * \details Initializes the required signal/slot connection between this class and other objects - * to be able to communicate. - */ -void DeviceController::initConnections() -{ - connect(&_ApplicationController , SIGNAL(didUSBDriveUmount()), - this , SLOT( onUSBDriveUmount())); - - connect(this , SIGNAL(didScreenshot(const QImage &, const QString &)), - this , SLOT( onScreenshot(const QImage &, const QString &))); - - connect(&_fileSystemWatcher , SIGNAL( fileChanged(const QString &)), - this , SLOT( onWatchFileChanged(const QString &))); - - connect(&_ApplicationController , SIGNAL(didPOSTOSVersionData (const QString &)), - this , SLOT( onPOSTOSVersionData (const QString &))); - connect(&_ApplicationController , SIGNAL(didPOSTEthernetData (const QString &)), - this , SLOT( onPOSTEthernetData (const QString &))); - connect(&_ApplicationController , SIGNAL(didPOSTWirelessData (const QString &)), - this , SLOT( onPOSTWirelessData (const QString &))); - connect(&_ApplicationController , SIGNAL(didPOSTBluetoothData (const QString &)), - this , SLOT( onPOSTBluetoothData (const QString &))); - connect(&_ApplicationController , SIGNAL(didPOSTCloudSyncData (const QString &)), - this , SLOT( onPOSTCloudSyncData (const QString &))); - - connect(&_Logger , SIGNAL(didLogBackup (const QString &)), - this , SLOT( onLogBackup (const QString &))); - connect(&_CloudSyncController , SIGNAL(didLogUpload (const QString &)), - this , SLOT( onLogUpload (const QString &))); - - DEVICE_DEV_INIT_CONNECTIONS_LIST - - connect(this, SIGNAL(didEventThreadChange()), - this, SLOT( onEventThreadChange())); - -} - -/*! - * \brief ApplicationController::initThread - * \details Moves this object into the thread vThread. - * And checks that this method is called from main thread. - * Also connects quitThread to application aboutToQuit. - * \param vThread - the thread - */ -void DeviceController::initThread(QThread &vThread) -{ - // runs in main thread - Q_ASSERT_X(QThread::currentThread() == qApp->thread() , __func__, "The Class initialization must be done in Main Thread" ); - _thread = &vThread; - _thread->setObjectName(QString("%1_Thread").arg(metaObject()->className())); - connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(quit())); - _thread->start(); - moveToThread(_thread); - - emit didEventThreadChange( QPrivateSignal() ); -} - -/*! - * \brief DeviceController::quitThread - * \details Moves this object to main thread to be handled by QApplication - * And to be destroyed there. - */ -void DeviceController::quitThread() -{ - // disabled coco begin validated: Application termination is not correctly done in coco!!! - // it has been tested and works perfectly fine in normal run. - - if ( ! _thread ) return; - - // runs in thread - moveToThread(qApp->thread()); // validated -} -// disabled coco end - -/*! - * \brief DeviceController::usbSeek - * \details Tries to look for the available USB devices - * Starts from sda1 to sdz1. - * \note will only look for the first partition if there is any - * \param vDevice - Found device (/dev/sda1) - * \return true if a device found - */ -bool DeviceController::usbSeek(QString &vDevice) -{ - // disabled coco begin validated: Needed User Interaction so tested manually - // This function cannot be tested on any device other than target - // because only on device the usb is /dev/sdX# and others are mmcblk___ - // but on VM and normal Linuxes all drives are /dev/sdX# and cannot be identified as USB. - // And the difference between how USB is detected and sd-card is that - // for the USB we need to look into /dev/sdX# - // but for the sd-card we look for the mounted path which is much easier. - // That's why indication of the sd-card is more accurate than USB at least on VM. - QString dev = "/dev/sd"; - QString device = ""; - for (char a = 'a'; a <= 'z'; a++) { - device = dev + a + '1'; - if (QFileInfo::exists(device)) { - vDevice = device; - return true; // application is deciding on the first existing drive - } - } - vDevice = device; - return false; - // disabled coco end -} - -/*! - * \brief DeviceController::driveSpaceCheck - * \param vPath - Device mount point - * \param vTotalBytes - Total volume size in bytes - * \param vAvailableBytes - Size (in bytes) available for the current user - * \param vPercent - The percentage of available space - * \return - The drive mounted and ready - * \note if device ejected manually system assumes it's still ready. - */ -bool DeviceController::driveSpaceCheck(const QString &vPath, qint64 &vTotalBytes, qint64 &vAvailableBytes, bool *vIsReadOnly) -{ - QStorageInfo storage(vPath); - bool isReady = storage.isReady (); - bool isReadOnly = storage.isReadOnly(); - bool isMounted = FileHandler::isMounted(vPath); - - if ( vIsReadOnly ) *vIsReadOnly = isReadOnly; - - if ( isReady ) { - vTotalBytes = storage.bytesTotal(); - vAvailableBytes = storage.bytesAvailable(); - } - - if ( ! isMounted || ! isReady ) { - isReady = false; - vTotalBytes = 0; - vAvailableBytes = 0; - } - return isReady; -} - -/*! - * \brief DeviceController::timerEvent - * \details This event handler has been re-implemented in here - * to receive timer events for the object - * for the timer which has been set to _checkInterval - * Runs the usbCheck on interval - */ -void DeviceController::timerEvent(QTimerEvent *) -{ -#ifdef BUILD_FOR_TARGET - usbCheck(); - sdcardSpaceCheck(); - - // The treatment logs are held in a separate partition from the unencrypted - // logs and need a separate disk space usage check - settingsPartitionSpaceCheck(); -#endif - findPendingLogs(); -} - -/*! - * \brief DeviceController::usbCheck - * \details Runs usbSeek to mount or umount or remove it - * regarding the state it's in. - */ -void DeviceController::usbCheck() -{ - QString device = ""; - usbSpaceCheck(); - if (usbSeek(device)) { - if (! _umounted ) { // avoid to mount the USB which has just been unmounted - if (! _mounted ) { // avoid to mount the USB which has just been mounted - usbMount(device); - } - } else { // the umount is requested? - usbUmount(USB_Mount_Point); - } - } else { - if ( ! _removed ) { - usbRemove(); - } - } -} - -/*! - * \brief DeviceController::sdcardSpaceCheck - * \details Checks for the SD-Card drive space. - */ -void DeviceController::sdcardSpaceCheck() -{ - static bool mInitialized = false; - // Old Info ; // Current info - static bool mOIsReady = false; bool mCIsReady = false; - static bool mOIsReadOnly = false; bool mCIsReadOnly = false; - static qint64 mOAvailable = 0; qint64 mCAvailable = 0; - static quint8 mOPercent = 0; quint8 mCPercent = 0; - - qint64 mCTotal = 0; - - bool isMounted = FileHandler::isMounted(Storage::SDCard_Base_Path_Name); - QString pathToCheckSpace = isMounted ? Storage::SDCard_Base_Path_Name : gStandard_tmp; - mCIsReady = driveSpaceCheck(pathToCheckSpace, mCTotal, mCAvailable, &mCIsReadOnly); - - //DEBUG: qDebug()<< "Checking space for path of : " << pathToCheckSpace << " mCTotal " << mCTotal << " available " << mCAvailable; - - if (mOIsReadOnly != mCIsReadOnly || mOIsReady != mCIsReady || ! mInitialized ) { - mOIsReadOnly = mCIsReadOnly; - mOIsReady = mCIsReady; - mInitialized = true; - //DEBUG:0: qDebug() << " ~~~~~~~~~~ " << __FUNCTION__ << mInitialized << mCIsReady << mOIsReady << mCIsReadOnly << mOIsReadOnly; - emit didSDCardStateChange(mCIsReady, mCIsReadOnly); - } - //NOTE: this if block has to be independent of the mOIsReady != mCIsReady - // because current and old may be the same all the time and then this if block will not execute - // and reaches to the log and fills the log unnecessarily. - if (! mCIsReady ) { - mOPercent = 0; - mOAvailable = 0; - emit didSDCardSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); - return; - } - - mCPercent = mCTotal ? ((100 * mCAvailable) / mCTotal) : 0; - if (mCPercent < _minRequiredAvailableSpacePercent) { - LOG_DEBUG(QString("SD-CARD space lower than %1%").arg(_minRequiredAvailableSpacePercent)); - emit didSDCardSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); - emit didSDCardSpaceTooLow(_minRequiredAvailableSpacePercent); - } - - //DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mCTotal << mOAvailable << mCAvailable << (mOAvailable == mCAvailable) << mOPercent << mCPercent << mCIsReadOnly; - if (mOPercent != mCPercent || mOAvailable != mCAvailable ) { - mOPercent = mCPercent ; - mOAvailable = mCAvailable ; - emit didSDCardSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); - /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mCTotal << mCAvailable << mPercent ; - } -} - -/*! - * \brief DeviceController::usbSpaceCheck - * \details Checks for the USB drive space. - */ -void DeviceController::usbSpaceCheck() -{ - static bool mInitialized = false; - // Old Info ; // Current info - static bool mOIsReady = false; bool mCIsReady = false; - static bool mOIsReadOnly = false; bool mCIsReadOnly = false; - static qint64 mOTotal = 0; qint64 mCTotal = 0; - static qint64 mOAvailable = 0; qint64 mCAvailable = 0; - - quint8 mPercent = 0; - - mCIsReady = driveSpaceCheck(Storage::USB_Mount_Point, mCTotal, mCAvailable, &mCIsReadOnly); -#if BUILD_FOR_DESKTOP - mCIsReady = true; // it is set to always true since on desktop a local folder is used for the USB folder which doesn't need (un)mount. -#endif - if (mOIsReadOnly != mCIsReadOnly || mOIsReady != mCIsReady || ! mInitialized ) { - mOIsReadOnly = mCIsReadOnly; - mOIsReady = mCIsReady; - mInitialized = true; - emit didUSBStateChange(mCIsReady, mCIsReadOnly); - - if (! mCIsReady ) { - mOTotal = 0; - mOAvailable = 0; - emit didUSBSpaceChange(mCIsReady, mCTotal, mCAvailable, mPercent); - return; - } - } - - mPercent = mCTotal ? ((100 * mCAvailable) / mCTotal) : 0; - if (mOTotal != mCTotal || mOAvailable != mCAvailable) { - mOTotal = mCTotal ; - mOAvailable = mCAvailable ; - emit didUSBSpaceChange(mCIsReady, mCTotal, mCAvailable, mPercent); - } - - // DEBUG: qDebug() << "DeviceController::usbSpaceCheck" - // << mCIsReady - // << mCIsReadOnly - // << mCTotal - // << mCAvailable ; -} - -/*! - * \brief DeviceController::settingsPartitionSpaceCheck - * \details Checks the disk space of the encrypted partition - */ -void DeviceController::settingsPartitionSpaceCheck() -{ - static bool mInitialized = false; - // Old Info ; // Current info - static bool mOIsReady = false; bool mCIsReady = false; - static bool mOIsReadOnly = false; bool mCIsReadOnly = false; - static qint64 mOAvailable = 0; qint64 mCAvailable = 0; - static quint8 mOPercent = 0; quint8 mCPercent = 0; - - qint64 mCTotal = 0; - mCIsReady = driveSpaceCheck(Storage::Settings_Path(), mCTotal, mCAvailable, &mCIsReadOnly); - - //DEBUG: qDebug()<< "Checking space for path of : " << Storage::Settings_Path() << " mCTotal " << mCTotal << " available " << mCAvailable; - - if (mOIsReadOnly != mCIsReadOnly || mOIsReady != mCIsReady || ! mInitialized ) { - mOIsReadOnly = mCIsReadOnly; - mOIsReady = mCIsReady; - mInitialized = true; - //DEBUG:0: qDebug() << " ~~~~~~~~~~ " << __FUNCTION__ << mInitialized << mCIsReady << mOIsReady << mCIsReadOnly << mOIsReadOnly; - emit didSettingsPartitionStateChange(mCIsReady, mCIsReadOnly); - } - //NOTE: this if block has to be independent of the mOIsReady != mCIsReady - // because current and old may be the same all the time and then this if block will not execute - // and reaches to the log and fills the log unnecessarily. - if (! mCIsReady ) { - mOPercent = 0; - mOAvailable = 0; - emit didSettingsPartitionSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); - return; - } - - mCPercent = mCTotal ? ((100 * mCAvailable) / mCTotal) : 0; - if (mCPercent < _minRequiredAvailableSpacePercent) { - LOG_DEBUG(QString("Settings partition space lower than %1%").arg(_minRequiredAvailableSpacePercent)); - emit didSettingsPartitionSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); - emit didSDCardSpaceTooLow(_minRequiredAvailableSpacePercent); - } - - /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mOTotal << mCTotal << (mOTotal == mCTotal) << mOAvailable << mCAvailable << (mOAvailable == mCAvailable) << mPercent << mCIsReadOnly; - if (mOPercent != mCPercent && mOAvailable != mCAvailable ) { - mOPercent = mCPercent ; - mOAvailable = mCAvailable ; - emit didSettingsPartitionSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); - /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mCTotal << mCAvailable << mPercent ; - } -} - - -/*! - * \brief DeviceController::usbError - * \details Logs any error which has been happened - * On USB device vDevice - * \note When this method has been called error number will be read from errno variable, - * Which has been set by umount or mount. - * \param vDevice - */ -void DeviceController::usbError(const QString &vDevice) -{ - // disabled coco begin validated: This needs user interaction to plug-in/out the USB device - // has been tested manually - QString error; - static QString lastError; - switch (errno) { - case EBUSY: - error = tr("%1 - Device or resource busy (%2)").arg(errno).arg(vDevice); - _mounted = true; - break; - - default: - error = tr("%1 - %2 (%3 , %4)").arg(errno).arg(strerror(errno)).arg(vDevice).arg(USB_Mount_Point); - break; - - } - if (error != lastError) { - LOG_DEBUG("USB: " + error); - lastError = error; - } -} -// disabled coco end - -/*! - * \brief DeviceController::onUSBDriveUmount - * \details This is the slot connected to the _ApplicationController's didUSBDriveUmount SIGNAL, - * To notify the USB drive detach. - */ -void DeviceController::onUSBDriveUmount() -{ - // disabled coco begin validated: This needs user interaction to plug-in/out the USB device - // has been tested manually - _umounted = true; -} -// disabled coco end - -/*! - * \brief DeviceController::usbMount - * \details Mounts the USB device vDevice - * \note Emits didUSBDriveMount signal - * \param vDevice - USB device to be mounted (e.g. /dev/sda1) - * \return true on successful mount - */ -void DeviceController::usbMount(const QString &vDevice) -{ - usbMountReq(vDevice); -} - -/*! - * \brief DeviceController::usbUmount - * \details Unmounts the USB device vDevice - * \note Emits didUSBDriveUmount signal - * \param vDevice - USB device to be unmounted (e.g. /dev/sda1) - * \return true on successful unmount - */ -void DeviceController::usbUmount(const QString &vDevice) -{ - usbMountReq(vDevice, false); -} - -/*! - * \brief DeviceController::usbRemove - * \details Removed the USB mount point - * So next time it is not mounted as next device. - * \note Emits didUSBDriveRemove signal - */ -void DeviceController::usbRemove() -{ - // disabled coco begin validated: This needs user interaction to plug-out the USB device - // has been tested manually - usbUmount(USB_Mount_Point); - _umounted = false; - _removed = true; - LOG_DEBUG("USB drive removed"); - emit didUSBDriveRemove(); -} -// disabled coco end - -/*! - * \brief DeviceController::checkError - * \details check if has error, then sets the base model accept to false and the reason to the error. - * in that case logs the error message and emit the didAttributeResponse to notify the GUI. - * \param vError - the error code, this can be the Gui enum or system number - * \param vExtraLogInfo - any extra information to be logged. Not display to user. - * \return - */ -template -bool DeviceController::checkError(DeviceError::Scripts_Error_Enum vError, TModel &vModel, QString vExtraLogInfo) -{ - if ( vError ) { - QString src = (vError > DeviceError::eDevice_Scripts_Error_Start ? MAbstract::unitText(MAbstract::Unit_Enum::eUI) : MAbstract::unitText(MAbstract::Unit_Enum::eDV)) + ","; - vModel._data.mAccepted = false ; - vModel._data.mReason = vError ; - vModel._data.mMessage = DeviceError::deviceErrorText(vError, vError); - LOG_APPED(" ," + src + vModel._data.mMessage + " " + vExtraLogInfo); - emit didAttributeResponse(vModel._data); - return true; - } - return false; -} - -///////////////////////////////////////////// DeviceBrightness -/*! - * \brief DeviceController::onAttributeRequest - * \details Sets the brightness level - * \param vBrightness - */ -void DeviceController::onAttributeRequest(const DeviceBrightnessRequestData &vData) -{ - // ----- initializing the member variable models - _deviceBrightnessRequest ._data = vData; - - - // ----- extract the required data - _deviceBrightnessRequest.setBrightnessSysVal(); - LOG_APPED( _deviceBrightnessRequest.toString()); - - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, vData.mRead ? Brightness_Get : Brightness_Set), _deviceBrightnessResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processBrightness.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceBrightnessResponse); - return; - } - - // ----- run the process - int timeout_ms = 10000; - QStringList params; - params << QString::number(_deviceBrightnessRequest._data.mBrightness_val); - TimedProcess *timedProcess = new TimedProcess(&_processBrightness, script, timeout_ms, params); - timedProcess->start(); - -} - -/*! - * \brief DeviceController::onProcessBrightnessFinished - * \details Called when the process to set the brightness has finished - * \param vExitCode (int) the exit code - * \note exit code -> 0 : set Accept [MBase] -> Log -> emit - * !0 : set Attrib [MBrgh] -> Log -> emit - * 1 - get an error when in onAttributeRequest : scriptErrorText([Gui Enum ]) - * 2 - get an error when in onProcessBrightnessExitCode : scriptErrorText([vExitCode]) - * 3 - get no error when in onProcessBrightnessExitCode : MDeviceResponse.toString() - * - in case 3 the specific model _data has to be filled prior to the toString to have it in the log. - */ -void DeviceController::onProcessBrightnessExitCode(int vExitCode, QProcess::ExitStatus) -{ - if ( ! checkError(static_cast(vExitCode), _deviceBrightnessResponse, _deviceBrightnessResponse.toString()) ) { // has no error - if (_deviceBrightnessRequest._data.mRead) { - bool ok = false; - int brightness = _processBrightness.readLine().toInt(&ok); - if (ok) { - _deviceBrightnessResponse.setBrightnessPercent(brightness); - } - else { - checkError(DeviceError::eDevice_Scripts_Error_Incorrect_Rsp,_deviceBrightnessResponse, _deviceBrightnessResponse.toString()); - return; - } - } - else { - _deviceBrightnessResponse.setBrightnessPercent(_deviceBrightnessRequest._data.mBrightness_val); - _deviceBrightnessResponse._data.mMessage = _deviceBrightnessResponse.toString(); - } - LOG_APPED(_deviceBrightnessResponse._data.mMessage); - emit didAttributeResponse(_deviceBrightnessResponse._data); - } -} - - -///////////////////////////////////////////// DeviceRootSSHAccess -/*! - * \brief DeviceController::onAttributeRequest - * \details Sets the RootSSHAccess - * \param vRootSSHAccess - */ -void DeviceController::onAttributeRequest(const DeviceRootSSHAccessRequestData &vData) -{ - // ----- initializing the member variable models - _deviceRootSSHAccessRequest._data = vData; - LOG_APPED( _deviceRootSSHAccessRequest.toString()); - - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, RootSSHAccess), _deviceRootSSHAccessResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processRootSSHAccess.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceRootSSHAccessResponse); - return; - } - - // ----- run the process - int timeout_ms = 10000; - QStringList params; - if ( ! _deviceRootSSHAccessRequest._data.mIsGet ) params << FSN(_deviceRootSSHAccessRequest._data.mRootSSHAccess); - TimedProcess *timedProcess = new TimedProcess(&_processRootSSHAccess, script, timeout_ms, params); - timedProcess->start(); -} - -/*! - * \brief DeviceController::onProcessRootSSHAccessExitCode - * \details Called when the process to set the RootSSHAccess has finished - * \param vExitCode (int) the exit code - * \note exit code -> 0 : set Accept [MBase] -> Log -> emit - * !0 : set Attrib [MBrgh] -> Log -> emit - * 1 - get an error when in onAttributeRequest : scriptErrorText([Gui Enum ]) - * 2 - get an error when in onProcessRootSSHAccessExitCode : scriptErrorText([vExitCode]) - * 3 - get no error when in onProcessRootSSHAccessExitCode : MDeviceResponse.toString() - * - in case 3 the specific model _data has to be filled prior to the toString to have it in the log. - */ -void DeviceController::onProcessRootSSHAccessExitCode(int vExitCode, QProcess::ExitStatus) -{ - if ( ! checkError(static_cast(vExitCode), _deviceRootSSHAccessResponse, _deviceRootSSHAccessResponse.toString()) ) { // has no error - if (_deviceRootSSHAccessRequest._data.mIsGet) { - bool ok = true; - Qt::CheckState rootSSHAccess = Qt:: Unchecked; - uint value = _processRootSSHAccess.readLine().toUInt(&ok); - if ( ! ok ) goto lError; - - switch (value) { - case 0 : rootSSHAccess = Qt:: Unchecked; break; - case 1 : rootSSHAccess = Qt::PartiallyChecked; break; - case 2 : rootSSHAccess = Qt:: Checked; break; - default : ok = false; - } - - if ( ! ok ) goto lError; - - _deviceRootSSHAccessResponse._data.mMessage = _deviceRootSSHAccessResponse.toString(); - _deviceRootSSHAccessResponse._data.mRootSSHAccess = rootSSHAccess; - } - else { - _deviceRootSSHAccessResponse._data.mMessage = _deviceRootSSHAccessResponse.toString(); - _deviceRootSSHAccessResponse._data.mRootSSHAccess = _deviceRootSSHAccessRequest._data.mRootSSHAccess; - } - - LOG_APPED(_deviceRootSSHAccessResponse._data.mMessage); - emit didAttributeResponse(_deviceRootSSHAccessResponse._data); - } - else { - // the error in this case is handled in the checkError - } - return; - -lError: - checkError(DeviceError::eDevice_Scripts_Error_Incorrect_Rsp, _deviceRootSSHAccessResponse, _deviceRootSSHAccessResponse.toString()); -} - -///////////////////////////////////////////// DeviceCryptSetup -/*! - * \brief DeviceController::onAttributeRequest - * \details Calls the CryptSetup script with the model data DeviceCryptSetupRequestData - * \param vData - the model data - */ -void DeviceController::onAttributeRequest(const DeviceCryptSetupRequestData &vData) -{ - //DEBUG qDebug() << " ---------- " << vData.mCommand << vData.mPassword; - - _deviceCryptSetupRequest._data = vData; - - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, Crypt_Setup), _deviceCryptSetupResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processCryptSetup.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceCryptSetupResponse); - return; - } - - - // ----- run the process - int timeout = 10000; - TimedProcess *timedProcess = new TimedProcess(&_processCryptSetup, script, timeout, { _deviceCryptSetupRequest._data.mCommand }); - _processCryptSetup.setEnvironment(QProcess::systemEnvironment() << QString("PASSWORD=%1").arg(_deviceCryptSetupRequest._data.mPassword)); - timedProcess->start(); - - // Update UI with a response - MDeviceCryptSetupResponse model; - model._data.mAccepted = false; - model._data.mMessage = tr("Encrypted Partition %1 started.").arg(_deviceCryptSetupRequest._data.mCommand); - emit didAttributeResponse(model.data()); -} - -/*! - * \brief DeviceController::onProcessCryptSetupExitCode - * \param vExitCode - * \param vStatus - */ -void DeviceController::onProcessCryptSetupExitCode(int vExitCode, QProcess::ExitStatus vStatus) -{ - const int ERR_CRYPTSETUP_MOUNT_ISMOUNT=134; // is used in crypt_setup.sh do not modify - - // The Exit code in this script is not used. - // any other checking is done by UI Software at the moment this script is called. - // The only thing matters is the pared device info in text and it will be empty string if error happens. - MDeviceCryptSetupResponse model; - QByteArray deviceInfo; - if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; - else deviceInfo = _processCryptSetup.readAll(); - model.fromByteArray( deviceInfo, &vExitCode ); - // DEBUG: qDebug() << model._data.mEchoInfo; - emit didAttributeResponse(model.data()); - LOG_APPED_UI(model.data().mMessage); - - bool isSetup = _deviceCryptSetupRequest._data.mCommand == "setup"; - bool isMount = _deviceCryptSetupRequest._data.mCommand == "mount"; - bool isMounted = isMount && ( - vExitCode == ERR_CRYPTSETUP_MOUNT_ISMOUNT || // is already mounted - vExitCode == 0 // successful mount - ); - bool isUpdate = gEnableUpdating && isMounted; - - if ( isMount ) emit didCryptSetupMount(model._data.mAccepted); - - QString msg = ""; - int err = 0 ; - //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. - Storage::Settings settings; - // moving the configuration files if the encrypted partition creation was successful. - if ( isUpdate ) goto lMove; // if it is gEnableUpdating, bypass the mAccepted for already mounted. - if ( ! model._data.mAccepted ) goto lErr ; // any other case goto error - if ( ! isSetup ) goto lOut ; // if not setup do NOT continue to move configurations - -lMove: - err = settings.configurationsMove(&msg, isUpdate); - if ( err ) { - model._data.mAccepted = false ; - model._data.mReason = err ; - model._data.mMessage = msg ; - emit didAttributeResponse(model.data()); - LOG_APPED_UI(model.data().mMessage); - } - -lOut: - return; - -lErr: - LOG_DEBUG(QString("Encrypted Partition %1 failed").arg(_deviceCryptSetupRequest._data.mCommand)); -} - -///////////////////////////////////////////// DeviceBluetoothPaired -void DeviceController::onAttributeRequest(const DeviceBluetoothPairedResetRequestData &) -{ - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, Bluetooth_Paired_Reset), _deviceBluetoothPairedResetResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processBluetoothPairedReset.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceBluetoothPairedResetResponse); - return; - } - - // ----- run the process - int timeout_ms = 10000; - TimedProcess *timedProcess = new TimedProcess(&_processBluetoothPairedReset, script, timeout_ms); - timedProcess->start(); -} - -void DeviceController::onProcessBluetoothPairedResetExitCode(int vExitCode, QProcess::ExitStatus vStatus) -{ - // TODO: review the usage and definition of this object _deviceBluetoothPairedResetResponse. do we need it any more? - MDeviceBluetoothPairedResetResponse model; - QByteArray deviceInfo; - if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; - else deviceInfo = _processBluetoothPairedReset.readAll(); - model.fromByteArray( deviceInfo, &vExitCode ); - LOG_APPED_UI(model.data().mMessage); - emit didAttributeResponse(model.data()); -} - -///////////////////////////////////////////// DeviceBluetoothPairedQuery -void DeviceController::onAttributeRequest(const DeviceBluetoothPairedQueryRequestData &) -{ - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, Bluetooth_Paired_Query), _deviceBluetoothPairedQueryResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processBluetoothPairedQuery.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceBluetoothPairedQueryResponse); - return; - } - - // ----- run the process - int timeout_ms = 10000; - TimedProcess *timedProcess = new TimedProcess(&_processBluetoothPairedQuery, script, timeout_ms); - timedProcess->start(); -} - -void DeviceController::onProcessBluetoothPairedQueryExitCode(int vExitCode, QProcess::ExitStatus vStatus) -{ - // The Exit code in this script is not used. - // any other checking is done by UI Software at the moment this script is called. - // The only thing matters is the pared device info in text and it will be empty string if error happens. - MDeviceBluetoothPairedQueryResponse model; - QByteArray deviceInfo; - if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; - else deviceInfo = _processBluetoothPairedQuery.readAll(); - model.fromByteArray( deviceInfo, &vExitCode ); - emit didAttributeResponse(model.data()); - LOG_APPED_UI(model.data().mMessage); -} - -/*! - * \brief DeviceController::doScreenshot - * \details emit the screenshot signal to run that in Device controller thread - * \param vCurrentDateTime - */ -void DeviceController::doScreenshot(const QImage &vImage, const QString &vFileName) { - emit didScreenshot(vImage, vFileName); -} - -/*! - * \brief DeviceController::onScreenshot - * \details The function to save the image vImage in the file vFileName - * \param vImage - The image source object - * \param vFileName - The filename to same the image to - */ -void DeviceController::onScreenshot(const QImage &vImage, const QString &vFileName) { - vImage.save(vFileName); - LOG_DEBUG("Screenshot saved in " + vFileName); -} - -/*! - * \brief DeviceController::ondoAddWatch - * \details The thread safe add file watch method - * \param vFile - The file to add to watch. - */ -void DeviceController::ondoAddWatch(const QString &vFile, bool vCreate) -{ - DeviceError::Scripts_Error_Enum err = DeviceError::eDevice_OK; - if ( vCreate ) { - if ( ! FileHandler::write ( vFile, "", false) ) { err = DeviceError::eDevice_Watch_Error_NotCreate; goto lErr; }} - else { - if ( ! QFileInfo::exists ( vFile ) ) { err = DeviceError::eDevice_Watch_Error_NotFound ; goto lErr; }} - if ( ! _fileSystemWatcher.removePath ( vFile ) ) { LOG_APPED_UI(QString("Device NOT watching %1").arg(vFile)); } - else { LOG_APPED_UI(QString("Device watch removed %1").arg(vFile)); } - if ( ! _fileSystemWatcher.addPath ( vFile ) ) { err = DeviceError::eDevice_Watch_Error_NotAdded ; goto lErr; } - - LOG_APPED_UI(QString("Device watch %1").arg(vFile)); - return; -lErr: - LOG_DEBUG(DeviceError::deviceErrorText(err, 0)); -} - -/*! - * \brief DeviceController::onWatchFileChanged - * \details This slot is called once the files being watched is updated. - * \param vFile - the file name. - */ -void DeviceController::onWatchFileChanged(const QString &vFile) -{ - emit didWatchFileChange(vFile); -} - -/*! - * \brief DeviceController::onEventThreadChange - * \details The signal handler for the DeviceController(this)::didEventThreadChange - * to start checking for the Encrypted partition readiness. - */ -void DeviceController::onEventThreadChange() -{ - //DEBUG qDebug() << " ---------- " << __FUNCTION__ << QThread::currentThread()->objectName() << QThread::currentThread() << qApp->thread(); - if ( QThread::currentThread() != &Threads::_DeviceController_Thread ) { - qCritical() << " ***** Device controller thread not initialized correctly ***** "; - return; - } - _hasThread = true; - checkConfugurationMountReady(); -} - -/*! - * \brief DeviceController::onPOSTOSVersionData - * \details Collects the OS Version - * when it is ready after the POST is done reading OS Version - * \param vMacAddress - The Ethernet MAC address - */ -void DeviceController::onPOSTOSVersionData(const QString &vOSVersion) { - _osVersion = vOSVersion; - emit didPOSTOSVersionData (vOSVersion); -} - -/*! - * \brief DeviceController::onPOSTEthernetData - * \details Collects the ethernet mac address - * when it is ready after the POST is done for the Ethernet - * \param vMacAddress - The Ethernet MAC address - */ -void DeviceController::onPOSTEthernetData(const QString &vMacAddress) { - _macEthernet = vMacAddress; - emit didPOSTEthernetData (vMacAddress); -} - -/*! - * \brief DeviceController::onPOSTWirelessData - * \details Collects the wireless mac address - * when it is ready after the POST is done for the Wireless connection - * \param vMacAddress - The Wireless MAC address - */ -void DeviceController::onPOSTWirelessData(const QString &vMacAddress) { - _macWireless = vMacAddress; - - encryption::varSalt(vMacAddress); - _hasSalt = ! vMacAddress.trimmed().isEmpty(); - checkConfugurationMountReady(); - - emit didPOSTWirelessData (vMacAddress); -} - -/*! - * \brief DeviceController::onPOSTBluetoothData - * \details Collects the bluetooth mac address - * when it is ready after the POST is done for the Bluetooth - * \param vMacAddress - The Bluetooth MAC address - */ -void DeviceController::onPOSTBluetoothData(const QString &vMacAddress) { - _macBluetooth = vMacAddress; - emit didPOSTBluetoothData (vMacAddress); -} - -/*! - * \brief DeviceController::onPOSTCloudSyncData - * \details Collects the CloudSync Network Address - * when it is ready after the POST is done for the CloudSync - * \param vNetAddress - *** Not defined yet and is a placeholder for later use *** - */ -void DeviceController::onPOSTCloudSyncData(const QString &vNetAddress) { - _netCloudSync = vNetAddress; - emit didPOSTCloudSyncData (vNetAddress); -} - -bool DeviceController::logBackup(const QString &vFileName) -{ - if ( ! gLogUpload ) return false; // no log backup - - bool ok = true; - QString fileSrc = vFileName; - QFileInfo fileInfo(vFileName); - QString filePath(fileInfo.absolutePath()); - QString fileBase(fileInfo.baseName()); - QString fileSufx(fileInfo.completeSuffix().prepend(_Logger.logFileNamePendingSubExt())); - QString fileDest(QString("%1/%2.%3").arg(filePath, fileBase, fileSufx)); - // DEBUG qDebug() << __FUNCTION__ << "\n" << vFileName << "\n" << fileDest; - if ( gLogCompress ) { - fileSrc += _Logger.logFileNameCompressExt(); - fileDest += _Logger.logFileNameCompressExt(); - ok = FileHandler::backupFile(vFileName); - } - ok = QFile::rename(fileSrc, fileDest); - return ok; -} - -void DeviceController::onLogBackup(const QString &vFileName) -{ - if ( ! gLogUpload ) return; // no log backup ( slot ) - - bool ok = true; - ok = logBackup( vFileName); - emit didLogBackup(ok, vFileName); -} - -bool DeviceController::logUpload(const QString &vFileName) -{ - if ( ! gLogUpload ) return false; // no log Uploaded rename - - bool ok = true; - QFileInfo fileInfo(vFileName); - QString filePath; - QString fileBase(fileInfo.baseName()); - QString ext = _Logger.logFileNamePendingSubExt(); - QString fileSufx(fileInfo.completeSuffix().remove(ext)); - QString fileDest = QString("%1.%2").arg(fileBase, fileSufx); - - Logger::LogType logType = _Logger.logFileLogType(vFileName, filePath); - ok = logType != Logger::eLogNone; - if ( ! ok ) { LOG_APPED_UI(QString("CS Incorrect log upload type [%1]").arg(fileSufx)); goto lOut; } - // DEBUG qDebug() << __FUNCTION__ << "\n" << vFileName << "\n" << fileDest; - - ok = QFile::rename(filePath + vFileName, filePath + fileDest); -lOut: - return ok; -} - -void DeviceController::onLogUpload(const QString &vFileName) -{ - if ( ! gLogUpload ) return; // no log uploaded rename ( slot ) - - bool ok = true; - ok = logUpload( vFileName); - emit didLogUpload(ok, vFileName); -} - -/*! - * \brief DeviceController::checkConfugurationMountReady - * \details Cheks if the system is ready to mount the encrypted partition. - * The object has to be on its own thread and the salt for the decryption has to be ready. - */ -void DeviceController::checkConfugurationMountReady() -{ - /// in manufacturing or updating the system is logged with root and configurations are in /home/root - /// therefore no need to mount the cononfiguraiton partition. - /// also for manufacturing the partition is being set up - /// and is less steps for setup if the partition is not mounted. - if ( gEnableManufacturing ) return; // it should do the mount when gEnableUpdating, therefore here should not add gEnableUpdating. - if ( ! ( _hasThread && _hasSalt ) ) return; - - DeviceCryptSetupRequestData data; - data.mCommand = "mount"; - bool ok = false; - data.mPassword = encryption::configurationsPassword( ok ); - if ( ! ok ) { // not enough infromation to create a secure passowrd - // status(tr("Not enough secure information provided")); - } - else { - onAttributeRequest(data); - } - - // I_AM_HERE - // HERE move the settings read from ApplicationController to here - // here is when the DeviceController is moved to its thread - // and now can start mounting the encrypted partition - // and then read the settings. - // TODO don't forget to check for the required configurations files and parameters in the settings class. - // and take care of the security flag in the Storage class. -} - -/*! - * \brief DeviceController::findPendingLogs - * \details this function counts downs for the _pendingInterval - * when the _pendingCounter reaches 0 will search for the files - * and if there is any will get the recent file in the list - */ -void DeviceController::findPendingLogs() -{ - if ( ! gLogUpload ) return; // no log upload pending detection - - static QString pendingLog = ""; - if( _pendingCounter ) { - _pendingCounter -- ; - return; - } - else { - _pendingCounter = _pendingInterval; // every minute - } - - QFileInfoList pendingFiles; - QString logLoc = Log_Folder_Base; - QString logExt = QString("*.%1*").arg(_Logger.logFileNamePendingSubExt()); // "*.u.*"; - for( auto logFolder : { Log_Folder_Application, Log_Folder_Service/*, Log_Folder_CloudSync*/ } ) { - pendingFiles = Storage::FileHandler::find( logLoc + logFolder, { logExt } ); - // look into the list. - // if there are pending files, - // send a request only for the top on the list - /// Note I thought it makes sense to send the oldest on the application and service logs - /// but there are some conversation about the situation if something happens on the device, - /// and it would be a critical situation to get the recent/top log and won't wait for the old ones to upload. - // * When gets uploaded, moves from pending then next one comes to top - // the process repeats until there is no file in pending - - bool uploadOldestFirst = true; //TODO: make if configurable(cfg, or cli) - - if ( pendingFiles.count() ) { - // the most recent/first log file, to first ask for the current log which has just been saved as pending - if ( uploadOldestFirst ) { - _pendingLog = pendingFiles.last().absoluteFilePath(); - } else { - _pendingLog = pendingFiles.first().absoluteFilePath(); - } - QString message = pendingLog; - LOG_DEBUG(message); - emit didPendingLog( _pendingLog, FileHandler::sha256sum( _pendingLog ) ); - // when a pending file found in the first log folder stop - // until there is none in first one (log) - // then continue to the next log folder (service) ... - goto lOut; - } - } -lOut: - return; -} - -///////////////////////////////////////////// DeviceFactoryReset -void DeviceController::onAttributeRequest(const DeviceFactoryResetRequestData &vData) -{ - _deviceFactoryResetRequest._data = vData; - - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, Factory_Reset), _deviceFactoryResetResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processFactoryReset.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceFactoryResetResponse); - return; - } - - - // ----- run the process - int timeout_ms = 10000; - TimedProcess *timedProcess = new TimedProcess(&_processFactoryReset, script, timeout_ms); - timedProcess->start(); - - MDeviceFactoryResetResponse model; - model._data.mAccepted = false; // will indirectly set the property factoryResetEnabled - model._data.mMessage = tr("Factory Reset started."); - emit didAttributeResponse(model.data()); -} - -/*! - * \brief DeviceController::onProcessFactoryResetExitCode - * \param vExitCode - the script exit code. - * \param vStatus - the script echoed message. - */ -void DeviceController::onProcessFactoryResetExitCode(int vExitCode, QProcess::ExitStatus vStatus) -{ - // The Exit code in this script is not used. - // any other checking is done by UI Software at the moment this script is called. - // The only thing matters is the paired device info in text and it will be empty string if error happens. - MDeviceFactoryResetResponse model; - QByteArray deviceInfo; - if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; - else deviceInfo = _processFactoryReset.readAll(); - model.fromByteArray( deviceInfo, &vExitCode ); - // DEBUG: qDebug() << model._data.mMessage << deviceInfo; - emit didAttributeResponse (model.data()); - emit didFactoryReset (model._data.mAccepted); - - LOG_APPED_UI(model.data().mMessage); -} - -///////////////////////////////////////////// DeviceDecommission -void DeviceController::onAttributeRequest(const DeviceDecommissionRequestData &vData) -{ - _deviceDecommissionRequest._data = vData; - - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, Device_Decommission), _deviceDecommissionResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processDecommission.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceDecommissionResponse); - return; - } - - - // ----- run the process - int timeout_ms = 10000; - TimedProcess *timedProcess = new TimedProcess(&_processDecommission, script, timeout_ms, { CloudSyncPath }); - _processDecommission.setEnvironment(QProcess::systemEnvironment() << QString("PASSWORD=%1").arg(_deviceDecommissionRequest._data.mPassword)); - timedProcess->start(); - - MDeviceDecommissionResponse model; - model._data.mAccepted = false; // will indirectly set the property decommissionEnabled - model._data.mMessage = tr("Decommissioning started."); - emit didAttributeResponse(model.data()); -} - -/*! - * \brief DeviceController::onProcessDecommissionExitCode - * \param vExitCode - * \param vStatus - */ -void DeviceController::onProcessDecommissionExitCode(int vExitCode, QProcess::ExitStatus vStatus) -{ - // The Exit code in this script is not used. - // any other checking is done by UI Software at the moment this script is called. - // The only thing matters is the paired device info in text and it will be empty string if error happens. - MDeviceDecommissionResponse model; - QByteArray deviceInfo; - if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; - else deviceInfo = _processDecommission.readAll(); - model.fromByteArray( deviceInfo, &vExitCode ); - // DEBUG: qDebug() << model._data.mMessage << deviceInfo; - emit didAttributeResponse (model.data()); - emit didDecommissioning (model._data.mAccepted); - - LOG_APPED_UI(model.data().mMessage); -} - -///////////////////////////////////////////// DeviceUSBMounting -void DeviceController::onAttributeRequest(const DeviceUSBMountRequestData &vData) -{ - Q_UNUSED(vData) - usbMountReq(vData.usbDevice, vData.isMountRequest); -} - -/*! - * \brief DeviceController::usbMountReq - * \details Calls the Usb unmount/mount script - * \param vIsMount - indicate if the request is for mounting or unmounting - * \param vDevice - the path to the USB device - */ -void DeviceController::usbMountReq(const QString &vDevice, bool vIsMount) -{ - qDebug() << __FUNCTION__ << vDevice << vIsMount; - _deviceUSBMountRequest._data.isMountRequest = vIsMount ; - _deviceUSBMountRequest._data.usbDevice = vDevice ; // not necessary, but to be consistent - - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, (vIsMount ? USB_Mount : USB_Unmount )), _deviceUSBMountResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processUSBMount.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceUSBMountResponse); - return; - } - - // ----- run the process - int timeout_ms = 5000; - TimedProcess *timedProcess = new TimedProcess(&_processUSBMount, script, timeout_ms, {vDevice, USB_Mount_Point, USB_File_System}); - timedProcess->start(); - - MDeviceUSBMountResponse model; - model._data.mAccepted = false; - model._data.mMessage = vIsMount ? tr("USB unmount started.") : tr("USB mount started"); - emit didAttributeResponse(model.data()); -} - -/*! - * \brief DeviceController::onProcessUSBMountExitCode - * \param vExitCode - * \param vStatus - */ -void DeviceController::onProcessUSBMountExitCode(int vExitCode, QProcess::ExitStatus vStatus) -{ - MDeviceUSBMountResponse model; - QByteArray deviceInfo; - if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; - else deviceInfo = _processUSBMount.readAll(); - model.fromByteArray( deviceInfo, &vExitCode ); - emit didAttributeResponse(model.data()); - - // Re-evaluate the USB space available - need to call this here to avoid - // visual lag caused by waiting to call this function on the timer timeout - usbSpaceCheck(); - - bool ok = ! vStatus; - QString usbDevice = _deviceUSBMountRequest._data.usbDevice; - if(_deviceUSBMountRequest._data.isMountRequest) { // *** USB Mount - if ( ok && ! _mounted ) { - _mounted = true; - _removed = false; - LOG_DEBUG(QString("USB flash drive %1 has been mounted on %2").arg(usbDevice).arg(USB_Mount_Point)); - emit didUSBDriveMount(); - } else { - usbError(usbDevice); - } - } else { // *** USB Unmount - if ( ok && _mounted ) { - _mounted = false; - // _umounted = true; // I think it might be needed, but needs more testing. - LOG_DEBUG(QString("USB drive %2 unmounted").arg(usbDevice)); - emit didUSBDriveUmount(); - } else { - // the error is irrelevant, commented out for now - // usbError(usbDrive); - } - } - - // log error and exit - return; -} +#include "DeviceController.h" + +// Linux +#include +#include + +// Qt +#include +#include +#include + +// Project +#include "Threads.h" +#include "StorageGlobals.h" +#include "Logger.h" +#include "CloudSyncController.h" +#include "ApplicationController.h" +#include "FileHandler.h" +#include "DeviceModels.h" +#include "Settings.h" +#include "encryption.h" + +// namespace +using namespace Model; +using namespace Device; +using namespace Storage; + +/*! + * \brief DeviceController::DeviceController + * \details Constructor + * \param parent - QObject parent owner object. + * Qt handles the children destruction by their parent objects life-cycle. + */ +DeviceController::DeviceController(QObject *parent) : QObject(parent) { + _fileSystemWatcher.setParent(this); + DEVICE_DEV_PARENT_LIST +} + +/*! + * \brief DeviceController::init + * \details Initializes the class by setting the connections and starting the timer + * \return False if it has been called before. + */ +bool DeviceController::init() +{ + if ( _init ) return false; + _init = true; + + // runs in DeviceController thread + initConnections(); + startTimer(_interval); + + return true; +} + +/*! + * \brief DeviceController::init + * \details Initialized the Class by calling the init() method first + * And initializes the thread vThread by calling initThread + * on success init(). + * \param vThread - the thread + * \return returns the return value of the init() method + */ +bool DeviceController::init(QThread &vThread) +{ + if ( ! init() ) return false; + initThread(vThread); + return true; +} + +/*! + * \brief DeviceController::quit + * \details quits the class + * Calls quitThread + */ +void DeviceController::quit() +{ + quitThread(); +} + + +/*! + * \brief DeviceController::initConnections + * \details Initializes the required signal/slot connection between this class and other objects + * to be able to communicate. + */ +void DeviceController::initConnections() +{ + connect(&_ApplicationController , SIGNAL(didUSBDriveUmount()), + this , SLOT( onUSBDriveUmount())); + + connect(this , SIGNAL(didScreenshot(const QImage &, const QString &)), + this , SLOT( onScreenshot(const QImage &, const QString &))); + + connect(&_fileSystemWatcher , SIGNAL( fileChanged(const QString &)), + this , SLOT( onWatchFileChanged(const QString &))); + + connect(&_ApplicationController , SIGNAL(didPOSTOSVersionData (const QString &)), + this , SLOT( onPOSTOSVersionData (const QString &))); + connect(&_ApplicationController , SIGNAL(didPOSTEthernetData (const QString &)), + this , SLOT( onPOSTEthernetData (const QString &))); + connect(&_ApplicationController , SIGNAL(didPOSTWirelessData (const QString &)), + this , SLOT( onPOSTWirelessData (const QString &))); + connect(&_ApplicationController , SIGNAL(didPOSTBluetoothData (const QString &)), + this , SLOT( onPOSTBluetoothData (const QString &))); + connect(&_ApplicationController , SIGNAL(didPOSTCloudSyncData (const QString &)), + this , SLOT( onPOSTCloudSyncData (const QString &))); + + connect(&_Logger , SIGNAL(didLogBackup (const QString &)), + this , SLOT( onLogBackup (const QString &))); + connect(&_CloudSyncController , SIGNAL(didLogUpload (const QString &)), + this , SLOT( onLogUpload (const QString &))); + + DEVICE_DEV_INIT_CONNECTIONS_LIST + + connect(this, SIGNAL(didEventThreadChange()), + this, SLOT( onEventThreadChange())); + +} + +/*! + * \brief ApplicationController::initThread + * \details Moves this object into the thread vThread. + * And checks that this method is called from main thread. + * Also connects quitThread to application aboutToQuit. + * \param vThread - the thread + */ +void DeviceController::initThread(QThread &vThread) +{ + // runs in main thread + Q_ASSERT_X(QThread::currentThread() == qApp->thread() , __func__, "The Class initialization must be done in Main Thread" ); + _thread = &vThread; + _thread->setObjectName(QString("%1_Thread").arg(metaObject()->className())); + connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(quit())); + _thread->start(); + moveToThread(_thread); + + emit didEventThreadChange( QPrivateSignal() ); +} + +/*! + * \brief DeviceController::quitThread + * \details Moves this object to main thread to be handled by QApplication + * And to be destroyed there. + */ +void DeviceController::quitThread() +{ + // disabled coco begin validated: Application termination is not correctly done in coco!!! + // it has been tested and works perfectly fine in normal run. + + if ( ! _thread ) return; + + // runs in thread + moveToThread(qApp->thread()); // validated +} +// disabled coco end + +/*! + * \brief DeviceController::usbSeek + * \details Tries to look for the available USB devices + * Starts from sda1 to sdz1. + * \note will only look for the first partition if there is any + * \param vDevice - Found device (/dev/sda1) + * \return true if a device found + */ +bool DeviceController::usbSeek(QString &vDevice) +{ + // disabled coco begin validated: Needed User Interaction so tested manually + // This function cannot be tested on any device other than target + // because only on device the usb is /dev/sdX# and others are mmcblk___ + // but on VM and normal Linuxes all drives are /dev/sdX# and cannot be identified as USB. + // And the difference between how USB is detected and sd-card is that + // for the USB we need to look into /dev/sdX# + // but for the sd-card we look for the mounted path which is much easier. + // That's why indication of the sd-card is more accurate than USB at least on VM. + QString dev = "/dev/sd"; + QString device = ""; + for (char a = 'a'; a <= 'z'; a++) { + device = dev + a + '1'; + if (QFileInfo::exists(device)) { + vDevice = device; + return true; // application is deciding on the first existing drive + } + } + vDevice = device; + return false; + // disabled coco end +} + +/*! + * \brief DeviceController::driveSpaceCheck + * \param vPath - Device mount point + * \param vTotalBytes - Total volume size in bytes + * \param vAvailableBytes - Size (in bytes) available for the current user + * \param vPercent - The percentage of available space + * \return - The drive mounted and ready + * \note if device ejected manually system assumes it's still ready. + */ +bool DeviceController::driveSpaceCheck(const QString &vPath, qint64 &vTotalBytes, qint64 &vAvailableBytes, bool *vIsReadOnly) +{ + QStorageInfo storage(vPath); + bool isReady = storage.isReady (); + bool isReadOnly = storage.isReadOnly(); + bool isMounted = FileHandler::isMounted(vPath); + + if ( vIsReadOnly ) *vIsReadOnly = isReadOnly; + + if ( isReady ) { + vTotalBytes = storage.bytesTotal(); + vAvailableBytes = storage.bytesAvailable(); + } + + if ( ! isMounted || ! isReady ) { + isReady = false; + vTotalBytes = 0; + vAvailableBytes = 0; + } + return isReady; +} + +/*! + * \brief DeviceController::timerEvent + * \details This event handler has been re-implemented in here + * to receive timer events for the object + * for the timer which has been set to _checkInterval + * Runs the usbCheck on interval + */ +void DeviceController::timerEvent(QTimerEvent *) +{ +#ifdef BUILD_FOR_TARGET + usbCheck(); + sdcardSpaceCheck(); + + // The treatment logs are held in a separate partition from the unencrypted + // logs and need a separate disk space usage check + settingsPartitionSpaceCheck(); +#endif + findPendingLogs(); +} + +/*! + * \brief DeviceController::usbCheck + * \details Runs usbSeek to mount or umount or remove it + * regarding the state it's in. + */ +void DeviceController::usbCheck() +{ + QString device = ""; + usbSpaceCheck(); + if (usbSeek(device)) { + if (! _umounted ) { // avoid to mount the USB which has just been unmounted + if (! _mounted ) { // avoid to mount the USB which has just been mounted + usbMount(device); + } + } else { // the umount is requested? + usbUmount(USB_Mount_Point); + } + } else { + if ( ! _removed ) { + usbRemove(); + } + } +} + +/*! + * \brief DeviceController::sdcardSpaceCheck + * \details Checks for the SD-Card drive space. + */ +void DeviceController::sdcardSpaceCheck() +{ + static bool mInitialized = false; + // Old Info ; // Current info + static bool mOIsReady = false; bool mCIsReady = false; + static bool mOIsReadOnly = false; bool mCIsReadOnly = false; + static qint64 mOAvailable = 0; qint64 mCAvailable = 0; + static quint8 mOPercent = 0; quint8 mCPercent = 0; + + qint64 mCTotal = 0; + + bool isMounted = FileHandler::isMounted(Storage::SDCard_Base_Path_Name); + QString pathToCheckSpace = isMounted ? Storage::SDCard_Base_Path_Name : gStandard_tmp; + mCIsReady = driveSpaceCheck(pathToCheckSpace, mCTotal, mCAvailable, &mCIsReadOnly); + + //DEBUG: qDebug()<< "Checking space for path of : " << pathToCheckSpace << " mCTotal " << mCTotal << " available " << mCAvailable; + + if (mOIsReadOnly != mCIsReadOnly || mOIsReady != mCIsReady || ! mInitialized ) { + mOIsReadOnly = mCIsReadOnly; + mOIsReady = mCIsReady; + mInitialized = true; + //DEBUG:0: qDebug() << " ~~~~~~~~~~ " << __FUNCTION__ << mInitialized << mCIsReady << mOIsReady << mCIsReadOnly << mOIsReadOnly; + emit didSDCardStateChange(mCIsReady, mCIsReadOnly); + } + //NOTE: this if block has to be independent of the mOIsReady != mCIsReady + // because current and old may be the same all the time and then this if block will not execute + // and reaches to the log and fills the log unnecessarily. + if (! mCIsReady ) { + mOPercent = 0; + mOAvailable = 0; + emit didSDCardSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); + return; + } + + mCPercent = mCTotal ? ((100 * mCAvailable) / mCTotal) : 0; + if (mCPercent < _minRequiredAvailableSpacePercent) { + LOG_DEBUG(QString("SD-CARD space lower than %1%").arg(_minRequiredAvailableSpacePercent)); + emit didSDCardSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); + emit didSDCardSpaceTooLow(_minRequiredAvailableSpacePercent); + } + + //DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mCTotal << mOAvailable << mCAvailable << (mOAvailable == mCAvailable) << mOPercent << mCPercent << mCIsReadOnly; + if (mOPercent != mCPercent || mOAvailable != mCAvailable ) { + mOPercent = mCPercent ; + mOAvailable = mCAvailable ; + emit didSDCardSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); + /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mCTotal << mCAvailable << mPercent ; + } +} + +/*! + * \brief DeviceController::usbSpaceCheck + * \details Checks for the USB drive space. + */ +void DeviceController::usbSpaceCheck() +{ + static bool mInitialized = false; + // Old Info ; // Current info + static bool mOIsReady = false; bool mCIsReady = false; + static bool mOIsReadOnly = false; bool mCIsReadOnly = false; + static qint64 mOTotal = 0; qint64 mCTotal = 0; + static qint64 mOAvailable = 0; qint64 mCAvailable = 0; + + quint8 mPercent = 0; + + mCIsReady = driveSpaceCheck(Storage::USB_Mount_Point, mCTotal, mCAvailable, &mCIsReadOnly); +#if BUILD_FOR_DESKTOP + mCIsReady = true; // it is set to always true since on desktop a local folder is used for the USB folder which doesn't need (un)mount. +#endif + if (mOIsReadOnly != mCIsReadOnly || mOIsReady != mCIsReady || ! mInitialized ) { + mOIsReadOnly = mCIsReadOnly; + mOIsReady = mCIsReady; + mInitialized = true; + emit didUSBStateChange(mCIsReady, mCIsReadOnly); + + if (! mCIsReady ) { + mOTotal = 0; + mOAvailable = 0; + emit didUSBSpaceChange(mCIsReady, mCTotal, mCAvailable, mPercent); + return; + } + } + + mPercent = mCTotal ? ((100 * mCAvailable) / mCTotal) : 0; + if (mOTotal != mCTotal || mOAvailable != mCAvailable) { + mOTotal = mCTotal ; + mOAvailable = mCAvailable ; + emit didUSBSpaceChange(mCIsReady, mCTotal, mCAvailable, mPercent); + } + + // DEBUG: qDebug() << "DeviceController::usbSpaceCheck" + // << mCIsReady + // << mCIsReadOnly + // << mCTotal + // << mCAvailable ; +} + +/*! + * \brief DeviceController::settingsPartitionSpaceCheck + * \details Checks the disk space of the encrypted partition + */ +void DeviceController::settingsPartitionSpaceCheck() +{ + static bool mInitialized = false; + // Old Info ; // Current info + static bool mOIsReady = false; bool mCIsReady = false; + static bool mOIsReadOnly = false; bool mCIsReadOnly = false; + static qint64 mOAvailable = 0; qint64 mCAvailable = 0; + static quint8 mOPercent = 0; quint8 mCPercent = 0; + + qint64 mCTotal = 0; + mCIsReady = driveSpaceCheck(Storage::Settings_Path(), mCTotal, mCAvailable, &mCIsReadOnly); + + //DEBUG: qDebug()<< "Checking space for path of : " << Storage::Settings_Path() << " mCTotal " << mCTotal << " available " << mCAvailable; + + if (mOIsReadOnly != mCIsReadOnly || mOIsReady != mCIsReady || ! mInitialized ) { + mOIsReadOnly = mCIsReadOnly; + mOIsReady = mCIsReady; + mInitialized = true; + //DEBUG:0: qDebug() << " ~~~~~~~~~~ " << __FUNCTION__ << mInitialized << mCIsReady << mOIsReady << mCIsReadOnly << mOIsReadOnly; + emit didSettingsPartitionStateChange(mCIsReady, mCIsReadOnly); + } + //NOTE: this if block has to be independent of the mOIsReady != mCIsReady + // because current and old may be the same all the time and then this if block will not execute + // and reaches to the log and fills the log unnecessarily. + if (! mCIsReady ) { + mOPercent = 0; + mOAvailable = 0; + emit didSettingsPartitionSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); + return; + } + + mCPercent = mCTotal ? ((100 * mCAvailable) / mCTotal) : 0; + if (mCPercent < _minRequiredAvailableSpacePercent) { + LOG_DEBUG(QString("Settings partition space lower than %1%").arg(_minRequiredAvailableSpacePercent)); + emit didSettingsPartitionSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); + emit didSDCardSpaceTooLow(_minRequiredAvailableSpacePercent); + } + + /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mOTotal << mCTotal << (mOTotal == mCTotal) << mOAvailable << mCAvailable << (mOAvailable == mCAvailable) << mPercent << mCIsReadOnly; + if (mOPercent != mCPercent && mOAvailable != mCAvailable ) { + mOPercent = mCPercent ; + mOAvailable = mCAvailable ; + emit didSettingsPartitionSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); + /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mCTotal << mCAvailable << mPercent ; + } +} + + +/*! + * \brief DeviceController::usbError + * \details Logs any error which has been happened + * On USB device vDevice + * \note When this method has been called error number will be read from errno variable, + * Which has been set by umount or mount. + * \param vDevice + */ +void DeviceController::usbError(const QString &vDevice) +{ + // disabled coco begin validated: This needs user interaction to plug-in/out the USB device + // has been tested manually + QString error; + static QString lastError; + switch (errno) { + case EBUSY: + error = tr("%1 - Device or resource busy (%2)").arg(errno).arg(vDevice); + _mounted = true; + break; + + default: + error = tr("%1 - %2 (%3 , %4)").arg(errno).arg(strerror(errno)).arg(vDevice).arg(USB_Mount_Point); + break; + + } + if (error != lastError) { + LOG_DEBUG("USB: " + error); + lastError = error; + } +} +// disabled coco end + +/*! + * \brief DeviceController::onUSBDriveUmount + * \details This is the slot connected to the _ApplicationController's didUSBDriveUmount SIGNAL, + * To notify the USB drive detach. + */ +void DeviceController::onUSBDriveUmount() +{ + // disabled coco begin validated: This needs user interaction to plug-in/out the USB device + // has been tested manually + _umounted = true; +} +// disabled coco end + +/*! + * \brief DeviceController::usbMount + * \details Mounts the USB device vDevice + * \note Emits didUSBDriveMount signal + * \param vDevice - USB device to be mounted (e.g. /dev/sda1) + * \return true on successful mount + */ +void DeviceController::usbMount(const QString &vDevice) +{ + usbMountReq(vDevice); +} + +/*! + * \brief DeviceController::usbUmount + * \details Unmounts the USB device vDevice + * \note Emits didUSBDriveUmount signal + * \param vDevice - USB device to be unmounted (e.g. /dev/sda1) + * \return true on successful unmount + */ +void DeviceController::usbUmount(const QString &vDevice) +{ + usbMountReq(vDevice, false); +} + +/*! + * \brief DeviceController::usbRemove + * \details Removed the USB mount point + * So next time it is not mounted as next device. + * \note Emits didUSBDriveRemove signal + */ +void DeviceController::usbRemove() +{ + // disabled coco begin validated: This needs user interaction to plug-out the USB device + // has been tested manually + usbUmount(USB_Mount_Point); + _umounted = false; + _removed = true; + LOG_DEBUG("USB drive removed"); + emit didUSBDriveRemove(); +} +// disabled coco end + +/*! + * \brief DeviceController::checkError + * \details check if has error, then sets the base model accept to false and the reason to the error. + * in that case logs the error message and emit the didAttributeResponse to notify the GUI. + * \param vError - the error code, this can be the Gui enum or system number + * \param vExtraLogInfo - any extra information to be logged. Not display to user. + * \return + */ +template +bool DeviceController::checkError(DeviceError::Scripts_Error_Enum vError, TModel &vModel, QString vExtraLogInfo) +{ + if ( vError ) { + QString src = (vError > DeviceError::eDevice_Scripts_Error_Start ? MAbstract::unitText(MAbstract::Unit_Enum::eUI) : MAbstract::unitText(MAbstract::Unit_Enum::eDV)) + ","; + vModel._data.mAccepted = false ; + vModel._data.mReason = vError ; + vModel._data.mMessage = DeviceError::deviceErrorText(vError, vError); + LOG_APPED(" ," + src + vModel._data.mMessage + " " + vExtraLogInfo); + emit didAttributeResponse(vModel._data); + return true; + } + return false; +} + +///////////////////////////////////////////// DeviceBrightness +/*! + * \brief DeviceController::onAttributeRequest + * \details Sets the brightness level + * \param vBrightness + */ +void DeviceController::onAttributeRequest(const DeviceBrightnessRequestData &vData) +{ + // ----- initializing the member variable models + _deviceBrightnessRequest ._data = vData; + + + // ----- extract the required data + _deviceBrightnessRequest.setBrightnessSysVal(); + LOG_APPED( _deviceBrightnessRequest.toString()); + + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, vData.mRead ? Brightness_Get : Brightness_Set), _deviceBrightnessResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processBrightness.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceBrightnessResponse); + return; + } + + // ----- run the process + int timeout_ms = 10000; + QStringList params; + params << QString::number(_deviceBrightnessRequest._data.mBrightness_val); + TimedProcess *timedProcess = new TimedProcess(&_processBrightness, script, timeout_ms, params); + timedProcess->start(); + +} + +/*! + * \brief DeviceController::onProcessBrightnessFinished + * \details Called when the process to set the brightness has finished + * \param vExitCode (int) the exit code + * \note exit code -> 0 : set Accept [MBase] -> Log -> emit + * !0 : set Attrib [MBrgh] -> Log -> emit + * 1 - get an error when in onAttributeRequest : scriptErrorText([Gui Enum ]) + * 2 - get an error when in onProcessBrightnessExitCode : scriptErrorText([vExitCode]) + * 3 - get no error when in onProcessBrightnessExitCode : MDeviceResponse.toString() + * - in case 3 the specific model _data has to be filled prior to the toString to have it in the log. + */ +void DeviceController::onProcessBrightnessExitCode(int vExitCode, QProcess::ExitStatus) +{ + if ( ! checkError(static_cast(vExitCode), _deviceBrightnessResponse, _deviceBrightnessResponse.toString()) ) { // has no error + if (_deviceBrightnessRequest._data.mRead) { + bool ok = false; + int brightness = _processBrightness.readLine().toInt(&ok); + if (ok) { + _deviceBrightnessResponse.setBrightnessPercent(brightness); + } + else { + checkError(DeviceError::eDevice_Scripts_Error_Incorrect_Rsp,_deviceBrightnessResponse, _deviceBrightnessResponse.toString()); + return; + } + } + else { + _deviceBrightnessResponse.setBrightnessPercent(_deviceBrightnessRequest._data.mBrightness_val); + _deviceBrightnessResponse._data.mMessage = _deviceBrightnessResponse.toString(); + } + LOG_APPED(_deviceBrightnessResponse._data.mMessage); + emit didAttributeResponse(_deviceBrightnessResponse._data); + } +} + + +///////////////////////////////////////////// DeviceRootSSHAccess +/*! + * \brief DeviceController::onAttributeRequest + * \details Sets the RootSSHAccess + * \param vRootSSHAccess + */ +void DeviceController::onAttributeRequest(const DeviceRootSSHAccessRequestData &vData) +{ + // ----- initializing the member variable models + _deviceRootSSHAccessRequest._data = vData; + LOG_APPED( _deviceRootSSHAccessRequest.toString()); + + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, RootSSHAccess), _deviceRootSSHAccessResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processRootSSHAccess.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceRootSSHAccessResponse); + return; + } + + // ----- run the process + int timeout_ms = 10000; + QStringList params; + if ( ! _deviceRootSSHAccessRequest._data.mIsGet ) params << FSN(_deviceRootSSHAccessRequest._data.mRootSSHAccess); + TimedProcess *timedProcess = new TimedProcess(&_processRootSSHAccess, script, timeout_ms, params); + timedProcess->start(); +} + +/*! + * \brief DeviceController::onProcessRootSSHAccessExitCode + * \details Called when the process to set the RootSSHAccess has finished + * \param vExitCode (int) the exit code + * \note exit code -> 0 : set Accept [MBase] -> Log -> emit + * !0 : set Attrib [MBrgh] -> Log -> emit + * 1 - get an error when in onAttributeRequest : scriptErrorText([Gui Enum ]) + * 2 - get an error when in onProcessRootSSHAccessExitCode : scriptErrorText([vExitCode]) + * 3 - get no error when in onProcessRootSSHAccessExitCode : MDeviceResponse.toString() + * - in case 3 the specific model _data has to be filled prior to the toString to have it in the log. + */ +void DeviceController::onProcessRootSSHAccessExitCode(int vExitCode, QProcess::ExitStatus) +{ + if ( ! checkError(static_cast(vExitCode), _deviceRootSSHAccessResponse, _deviceRootSSHAccessResponse.toString()) ) { // has no error + if (_deviceRootSSHAccessRequest._data.mIsGet) { + bool ok = true; + Qt::CheckState rootSSHAccess = Qt:: Unchecked; + uint value = _processRootSSHAccess.readLine().toUInt(&ok); + if ( ! ok ) goto lError; + + switch (value) { + case 0 : rootSSHAccess = Qt:: Unchecked; break; + case 1 : rootSSHAccess = Qt::PartiallyChecked; break; + case 2 : rootSSHAccess = Qt:: Checked; break; + default : ok = false; + } + + if ( ! ok ) goto lError; + + _deviceRootSSHAccessResponse._data.mMessage = _deviceRootSSHAccessResponse.toString(); + _deviceRootSSHAccessResponse._data.mRootSSHAccess = rootSSHAccess; + } + else { + _deviceRootSSHAccessResponse._data.mMessage = _deviceRootSSHAccessResponse.toString(); + _deviceRootSSHAccessResponse._data.mRootSSHAccess = _deviceRootSSHAccessRequest._data.mRootSSHAccess; + } + + LOG_APPED(_deviceRootSSHAccessResponse._data.mMessage); + emit didAttributeResponse(_deviceRootSSHAccessResponse._data); + } + else { + // the error in this case is handled in the checkError + } + return; + +lError: + checkError(DeviceError::eDevice_Scripts_Error_Incorrect_Rsp, _deviceRootSSHAccessResponse, _deviceRootSSHAccessResponse.toString()); +} + +///////////////////////////////////////////// DeviceCryptSetup +/*! + * \brief DeviceController::onAttributeRequest + * \details Calls the CryptSetup script with the model data DeviceCryptSetupRequestData + * \param vData - the model data + */ +void DeviceController::onAttributeRequest(const DeviceCryptSetupRequestData &vData) +{ + //DEBUG qDebug() << " ---------- " << vData.mCommand << vData.mPassword; + + _deviceCryptSetupRequest._data = vData; + + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, Crypt_Setup), _deviceCryptSetupResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processCryptSetup.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceCryptSetupResponse); + return; + } + + + // ----- run the process + int timeout = 10000; + TimedProcess *timedProcess = new TimedProcess(&_processCryptSetup, script, timeout, { _deviceCryptSetupRequest._data.mCommand }); + _processCryptSetup.setEnvironment(QProcess::systemEnvironment() << QString("PASSWORD=%1").arg(_deviceCryptSetupRequest._data.mPassword)); + timedProcess->start(); + + // Update UI with a response + MDeviceCryptSetupResponse model; + model._data.mAccepted = false; + model._data.mMessage = tr("Encrypted Partition %1 started.").arg(_deviceCryptSetupRequest._data.mCommand); + emit didAttributeResponse(model.data()); +} + +/*! + * \brief DeviceController::onProcessCryptSetupExitCode + * \param vExitCode + * \param vStatus + */ +void DeviceController::onProcessCryptSetupExitCode(int vExitCode, QProcess::ExitStatus vStatus) +{ + const int ERR_CRYPTSETUP_MOUNT_ISMOUNT=134; // is used in crypt_setup.sh do not modify + + // The Exit code in this script is not used. + // any other checking is done by UI Software at the moment this script is called. + // The only thing matters is the pared device info in text and it will be empty string if error happens. + MDeviceCryptSetupResponse model; + QByteArray deviceInfo; + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processCryptSetup.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + // DEBUG: qDebug() << model._data.mEchoInfo; + emit didAttributeResponse(model.data()); + LOG_APPED_UI(model.data().mMessage); + + bool isSetup = _deviceCryptSetupRequest._data.mCommand == "setup"; + bool isMount = _deviceCryptSetupRequest._data.mCommand == "mount"; + bool isMounted = isMount && ( + vExitCode == ERR_CRYPTSETUP_MOUNT_ISMOUNT || // is already mounted + vExitCode == 0 // successful mount + ); + bool isUpdate = gEnableUpdating && isMounted; + + if ( isMount ) emit didCryptSetupMount(model._data.mAccepted); + + QString msg = ""; + int err = 0 ; + //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. + Storage::Settings settings; + // moving the configuration files if the encrypted partition creation was successful. + if ( isUpdate ) goto lMove; // if it is gEnableUpdating, bypass the mAccepted for already mounted. + if ( ! model._data.mAccepted ) goto lErr ; // any other case goto error + if ( ! isSetup ) goto lOut ; // if not setup do NOT continue to move configurations + +lMove: + err = settings.configurationsMove(&msg, isUpdate); + if ( err ) { + model._data.mAccepted = false ; + model._data.mReason = err ; + model._data.mMessage = msg ; + emit didAttributeResponse(model.data()); + LOG_APPED_UI(model.data().mMessage); + } + +lOut: + return; + +lErr: + LOG_DEBUG(QString("Encrypted Partition %1 failed").arg(_deviceCryptSetupRequest._data.mCommand)); +} + +///////////////////////////////////////////// DeviceBluetoothPaired +void DeviceController::onAttributeRequest(const DeviceBluetoothPairedResetRequestData &) +{ + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, Bluetooth_Paired_Reset), _deviceBluetoothPairedResetResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processBluetoothPairedReset.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceBluetoothPairedResetResponse); + return; + } + + // ----- run the process + int timeout_ms = 10000; + TimedProcess *timedProcess = new TimedProcess(&_processBluetoothPairedReset, script, timeout_ms); + timedProcess->start(); +} + +void DeviceController::onProcessBluetoothPairedResetExitCode(int vExitCode, QProcess::ExitStatus vStatus) +{ + // TODO: review the usage and definition of this object _deviceBluetoothPairedResetResponse. do we need it any more? + MDeviceBluetoothPairedResetResponse model; + QByteArray deviceInfo; + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processBluetoothPairedReset.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + LOG_APPED_UI(model.data().mMessage); + emit didAttributeResponse(model.data()); +} + +///////////////////////////////////////////// DeviceBluetoothPairedQuery +void DeviceController::onAttributeRequest(const DeviceBluetoothPairedQueryRequestData &) +{ + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, Bluetooth_Paired_Query), _deviceBluetoothPairedQueryResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processBluetoothPairedQuery.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceBluetoothPairedQueryResponse); + return; + } + + // ----- run the process + int timeout_ms = 10000; + TimedProcess *timedProcess = new TimedProcess(&_processBluetoothPairedQuery, script, timeout_ms); + timedProcess->start(); +} + +void DeviceController::onProcessBluetoothPairedQueryExitCode(int vExitCode, QProcess::ExitStatus vStatus) +{ + // The Exit code in this script is not used. + // any other checking is done by UI Software at the moment this script is called. + // The only thing matters is the pared device info in text and it will be empty string if error happens. + MDeviceBluetoothPairedQueryResponse model; + QByteArray deviceInfo; + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processBluetoothPairedQuery.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + emit didAttributeResponse(model.data()); + LOG_APPED_UI(model.data().mMessage); +} + +/*! + * \brief DeviceController::doScreenshot + * \details emit the screenshot signal to run that in Device controller thread + * \param vCurrentDateTime + */ +void DeviceController::doScreenshot(const QImage &vImage, const QString &vFileName) { + emit didScreenshot(vImage, vFileName); +} + +/*! + * \brief DeviceController::onScreenshot + * \details The function to save the image vImage in the file vFileName + * \param vImage - The image source object + * \param vFileName - The filename to same the image to + */ +void DeviceController::onScreenshot(const QImage &vImage, const QString &vFileName) { + vImage.save(vFileName); + LOG_DEBUG("Screenshot saved in " + vFileName); +} + +/*! + * \brief DeviceController::ondoAddWatch + * \details The thread safe add file watch method + * \param vFile - The file to add to watch. + */ +void DeviceController::ondoAddWatch(const QString &vFile, bool vCreate) +{ + DeviceError::Scripts_Error_Enum err = DeviceError::eDevice_OK; + if ( vCreate ) { + if ( ! FileHandler::write ( vFile, "", false) ) { err = DeviceError::eDevice_Watch_Error_NotCreate; goto lErr; }} + else { + if ( ! QFileInfo::exists ( vFile ) ) { err = DeviceError::eDevice_Watch_Error_NotFound ; goto lErr; }} + if ( ! _fileSystemWatcher.removePath ( vFile ) ) { LOG_APPED_UI(QString("Device NOT watching %1").arg(vFile)); } + else { LOG_APPED_UI(QString("Device watch removed %1").arg(vFile)); } + if ( ! _fileSystemWatcher.addPath ( vFile ) ) { err = DeviceError::eDevice_Watch_Error_NotAdded ; goto lErr; } + + LOG_APPED_UI(QString("Device watch %1").arg(vFile)); + return; +lErr: + LOG_DEBUG(DeviceError::deviceErrorText(err, 0)); +} + +/*! + * \brief DeviceController::onWatchFileChanged + * \details This slot is called once the files being watched is updated. + * \param vFile - the file name. + */ +void DeviceController::onWatchFileChanged(const QString &vFile) +{ + emit didWatchFileChange(vFile); +} + +/*! + * \brief DeviceController::onEventThreadChange + * \details The signal handler for the DeviceController(this)::didEventThreadChange + * to start checking for the Encrypted partition readiness. + */ +void DeviceController::onEventThreadChange() +{ + //DEBUG qDebug() << " ---------- " << __FUNCTION__ << QThread::currentThread()->objectName() << QThread::currentThread() << qApp->thread(); + if ( QThread::currentThread() != &Threads::_DeviceController_Thread ) { + qCritical() << " ***** Device controller thread not initialized correctly ***** "; + return; + } + _hasThread = true; + checkConfugurationMountReady(); +} + +/*! + * \brief DeviceController::onPOSTOSVersionData + * \details Collects the OS Version + * when it is ready after the POST is done reading OS Version + * \param vMacAddress - The Ethernet MAC address + */ +void DeviceController::onPOSTOSVersionData(const QString &vOSVersion) { + _osVersion = vOSVersion; + emit didPOSTOSVersionData (vOSVersion); +} + +/*! + * \brief DeviceController::onPOSTEthernetData + * \details Collects the ethernet mac address + * when it is ready after the POST is done for the Ethernet + * \param vMacAddress - The Ethernet MAC address + */ +void DeviceController::onPOSTEthernetData(const QString &vMacAddress) { + _macEthernet = vMacAddress; + emit didPOSTEthernetData (vMacAddress); +} + +/*! + * \brief DeviceController::onPOSTWirelessData + * \details Collects the wireless mac address + * when it is ready after the POST is done for the Wireless connection + * \param vMacAddress - The Wireless MAC address + */ +void DeviceController::onPOSTWirelessData(const QString &vMacAddress) { + _macWireless = vMacAddress; + + encryption::varSalt(vMacAddress); + _hasSalt = ! vMacAddress.trimmed().isEmpty(); + checkConfugurationMountReady(); + + emit didPOSTWirelessData (vMacAddress); +} + +/*! + * \brief DeviceController::onPOSTBluetoothData + * \details Collects the bluetooth mac address + * when it is ready after the POST is done for the Bluetooth + * \param vMacAddress - The Bluetooth MAC address + */ +void DeviceController::onPOSTBluetoothData(const QString &vMacAddress) { + _macBluetooth = vMacAddress; + emit didPOSTBluetoothData (vMacAddress); +} + +/*! + * \brief DeviceController::onPOSTCloudSyncData + * \details Collects the CloudSync Network Address + * when it is ready after the POST is done for the CloudSync + * \param vNetAddress - *** Not defined yet and is a placeholder for later use *** + */ +void DeviceController::onPOSTCloudSyncData(const QString &vNetAddress) { + _netCloudSync = vNetAddress; + emit didPOSTCloudSyncData (vNetAddress); +} + +bool DeviceController::logBackup(const QString &vFileName) +{ + if ( ! gLogUpload ) return false; // no log backup + + bool ok = true; + QString fileSrc = vFileName; + QFileInfo fileInfo(vFileName); + + bool isBootPOST = _Logger.isBootPOST( vFileName ); + bool isCurrent = fileInfo.lastModified().date() == QDate::currentDate(); + if ( isBootPOST && isCurrent ) return false; // no log backup if it is the BootPOST log file. + + QString filePath(fileInfo.absolutePath()); + QString fileBase(fileInfo.baseName()); + QString fileSufx(fileInfo.completeSuffix().prepend(_Logger.logFileNamePendingSubExt())); + QString fileDest(QString("%1/%2.%3").arg(filePath, fileBase, fileSufx)); + QString gzipSufx(gLogCompress ? Storage::gzipExt : ""); + + if ( gLogCompress ) { + ok = FileHandler::backupFile(fileSrc); + } + ok = QFile::rename(fileSrc + gzipSufx, fileDest + gzipSufx); + + // Now the serial received and the system is logging in the log with the serial number, + // therefore it is not bootPOST. + // Now try to back up the skipped bootPOST log. + if ( ! isBootPOST ) { + QString fileBoot = fileSrc; + fileBoot .replace( _Logger.logFileNameHDSN(), _Logger.logFileNameHDSN_default() ); + if ( ! QFile::exists(fileBoot) ) goto lOut; + + fileSrc .replace( _Logger.logFileNameHDSN(), _Logger.logFileNameHDSN() + _Logger.fileSeparator() + _Logger.logFileNameHDSN_default() ); + fileDest.replace( _Logger.logFileNameHDSN(), _Logger.logFileNameHDSN() + _Logger.fileSeparator() + _Logger.logFileNameHDSN_default() ); + ok = QFile::rename(fileBoot, fileSrc); + ok = FileHandler::backupFile(fileSrc); + ok = QFile::rename(fileSrc + gzipSufx, fileDest + gzipSufx); + } + +lOut: + return ok; +} + +void DeviceController::onLogBackup(const QString &vFileName) +{ + if ( ! gLogUpload ) return; // no log backup ( slot ) + + bool ok = true; + ok = logBackup( vFileName); + emit didLogBackup(ok, vFileName); +} + +bool DeviceController::logUpload(const QString &vFileName) +{ + if ( ! gLogUpload ) return false; // no log Uploaded rename + + bool ok = true; + QFileInfo fileInfo(vFileName); + QString filePath; + QString fileBase(fileInfo.baseName()); + QString ext = _Logger.logFileNamePendingSubExt(); + QString fileSufx(fileInfo.completeSuffix().remove(ext)); + QString fileDest = QString("%1.%2").arg(fileBase, fileSufx); + + Logger::LogType logType = _Logger.logFileLogType(vFileName, filePath); + ok = logType != Logger::eLogNone; + if ( ! ok ) { LOG_APPED_UI(QString("CS Incorrect log upload type [%1]").arg(fileSufx)); goto lOut; } + // DEBUG qDebug() << __FUNCTION__ << "\n" << vFileName << "\n" << fileDest; + + ok = QFile::rename(filePath + vFileName, filePath + fileDest); +lOut: + return ok; +} + +void DeviceController::onLogUpload(const QString &vFileName) +{ + if ( ! gLogUpload ) return; // no log uploaded rename ( slot ) + + bool ok = true; + ok = logUpload( vFileName); + emit didLogUpload(ok, vFileName); +} + +/*! + * \brief DeviceController::checkConfugurationMountReady + * \details Cheks if the system is ready to mount the encrypted partition. + * The object has to be on its own thread and the salt for the decryption has to be ready. + */ +void DeviceController::checkConfugurationMountReady() +{ + /// in manufacturing or updating the system is logged with root and configurations are in /home/root + /// therefore no need to mount the cononfiguraiton partition. + /// also for manufacturing the partition is being set up + /// and is less steps for setup if the partition is not mounted. + if ( gEnableManufacturing ) return; // it should do the mount when gEnableUpdating, therefore here should not add gEnableUpdating. + if ( ! ( _hasThread && _hasSalt ) ) return; + + DeviceCryptSetupRequestData data; + data.mCommand = "mount"; + bool ok = false; + data.mPassword = encryption::configurationsPassword( ok ); + if ( ! ok ) { // not enough infromation to create a secure passowrd + // status(tr("Not enough secure information provided")); + } + else { + onAttributeRequest(data); + } + + // I_AM_HERE + // HERE move the settings read from ApplicationController to here + // here is when the DeviceController is moved to its thread + // and now can start mounting the encrypted partition + // and then read the settings. + // TODO don't forget to check for the required configurations files and parameters in the settings class. + // and take care of the security flag in the Storage class. +} + +/*! + * \brief DeviceController::findPendingLogs + * \details this function counts downs for the _pendingInterval + * when the _pendingCounter reaches 0 will search for the files + * and if there is any will get the recent file in the list + */ +void DeviceController::findPendingLogs() +{ + if ( ! gLogUpload ) return; // no log upload pending detection + + static QString pendingLog = ""; + if( _pendingCounter ) { + _pendingCounter -- ; + return; + } + else { + _pendingCounter = _pendingInterval; // every minute + } + + QFileInfoList pendingFiles; + QString logLoc = Log_Folder_Base; + QString logExt = QString("*.%1*").arg(_Logger.logFileNamePendingSubExt()); // "*.u.*"; + for( auto logFolder : { Log_Folder_Application, Log_Folder_Service/*, Log_Folder_CloudSync*/ } ) { + pendingFiles = Storage::FileHandler::find( logLoc + logFolder, { logExt } ); + // look into the list. + // if there are pending files, + // send a request only for the top on the list + /// Note I thought it makes sense to send the oldest on the application and service logs + /// but there are some conversation about the situation if something happens on the device, + /// and it would be a critical situation to get the recent/top log and won't wait for the old ones to upload. + // * When gets uploaded, moves from pending then next one comes to top + // the process repeats until there is no file in pending + + bool uploadOldestFirst = true; //TODO: make if configurable(cfg, or cli) + + if ( pendingFiles.count() ) { + // the most recent/first log file, to first ask for the current log which has just been saved as pending + if ( uploadOldestFirst ) { + _pendingLog = pendingFiles.last().absoluteFilePath(); + } else { + _pendingLog = pendingFiles.first().absoluteFilePath(); + } + QString message = pendingLog; + LOG_DEBUG(message); + emit didPendingLog( _pendingLog, FileHandler::sha256sum( _pendingLog ) ); + // when a pending file found in the first log folder stop + // until there is none in first one (log) + // then continue to the next log folder (service) ... + goto lOut; + } + } +lOut: + return; +} + +///////////////////////////////////////////// DeviceFactoryReset +void DeviceController::onAttributeRequest(const DeviceFactoryResetRequestData &vData) +{ + _deviceFactoryResetRequest._data = vData; + + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, Factory_Reset), _deviceFactoryResetResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processFactoryReset.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceFactoryResetResponse); + return; + } + + + // ----- run the process + int timeout_ms = 10000; + TimedProcess *timedProcess = new TimedProcess(&_processFactoryReset, script, timeout_ms); + timedProcess->start(); + + MDeviceFactoryResetResponse model; + model._data.mAccepted = false; // will indirectly set the property factoryResetEnabled + model._data.mMessage = tr("Factory Reset started."); + emit didAttributeResponse(model.data()); +} + +/*! + * \brief DeviceController::onProcessFactoryResetExitCode + * \param vExitCode - the script exit code. + * \param vStatus - the script echoed message. + */ +void DeviceController::onProcessFactoryResetExitCode(int vExitCode, QProcess::ExitStatus vStatus) +{ + // The Exit code in this script is not used. + // any other checking is done by UI Software at the moment this script is called. + // The only thing matters is the paired device info in text and it will be empty string if error happens. + MDeviceFactoryResetResponse model; + QByteArray deviceInfo; + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processFactoryReset.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + // DEBUG: qDebug() << model._data.mMessage << deviceInfo; + emit didAttributeResponse (model.data()); + emit didFactoryReset (model._data.mAccepted); + + LOG_APPED_UI(model.data().mMessage); +} + +///////////////////////////////////////////// DeviceDecommission +void DeviceController::onAttributeRequest(const DeviceDecommissionRequestData &vData) +{ + _deviceDecommissionRequest._data = vData; + + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, Device_Decommission), _deviceDecommissionResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processDecommission.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceDecommissionResponse); + return; + } + + + // ----- run the process + int timeout_ms = 10000; + TimedProcess *timedProcess = new TimedProcess(&_processDecommission, script, timeout_ms, { CloudSyncPath }); + _processDecommission.setEnvironment(QProcess::systemEnvironment() << QString("PASSWORD=%1").arg(_deviceDecommissionRequest._data.mPassword)); + timedProcess->start(); + + MDeviceDecommissionResponse model; + model._data.mAccepted = false; // will indirectly set the property decommissionEnabled + model._data.mMessage = tr("Decommissioning started."); + emit didAttributeResponse(model.data()); +} + +/*! + * \brief DeviceController::onProcessDecommissionExitCode + * \param vExitCode + * \param vStatus + */ +void DeviceController::onProcessDecommissionExitCode(int vExitCode, QProcess::ExitStatus vStatus) +{ + // The Exit code in this script is not used. + // any other checking is done by UI Software at the moment this script is called. + // The only thing matters is the paired device info in text and it will be empty string if error happens. + MDeviceDecommissionResponse model; + QByteArray deviceInfo; + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processDecommission.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + // DEBUG: qDebug() << model._data.mMessage << deviceInfo; + emit didAttributeResponse (model.data()); + emit didDecommissioning (model._data.mAccepted); + + LOG_APPED_UI(model.data().mMessage); +} + +///////////////////////////////////////////// DeviceUSBMounting +void DeviceController::onAttributeRequest(const DeviceUSBMountRequestData &vData) +{ + Q_UNUSED(vData) + usbMountReq(vData.usbDevice, vData.isMountRequest); +} + +/*! + * \brief DeviceController::usbMountReq + * \details Calls the Usb unmount/mount script + * \param vIsMount - indicate if the request is for mounting or unmounting + * \param vDevice - the path to the USB device + */ +void DeviceController::usbMountReq(const QString &vDevice, bool vIsMount) +{ + qDebug() << __FUNCTION__ << vDevice << vIsMount; + _deviceUSBMountRequest._data.isMountRequest = vIsMount ; + _deviceUSBMountRequest._data.usbDevice = vDevice ; // not necessary, but to be consistent + + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, (vIsMount ? USB_Mount : USB_Unmount )), _deviceUSBMountResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processUSBMount.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceUSBMountResponse); + return; + } + + // ----- run the process + int timeout_ms = 5000; + TimedProcess *timedProcess = new TimedProcess(&_processUSBMount, script, timeout_ms, {vDevice, USB_Mount_Point, USB_File_System}); + timedProcess->start(); + + MDeviceUSBMountResponse model; + model._data.mAccepted = false; + model._data.mMessage = vIsMount ? tr("USB unmount started.") : tr("USB mount started"); + emit didAttributeResponse(model.data()); +} + +/*! + * \brief DeviceController::onProcessUSBMountExitCode + * \param vExitCode + * \param vStatus + */ +void DeviceController::onProcessUSBMountExitCode(int vExitCode, QProcess::ExitStatus vStatus) +{ + MDeviceUSBMountResponse model; + QByteArray deviceInfo; + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processUSBMount.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + emit didAttributeResponse(model.data()); + + // Re-evaluate the USB space available - need to call this here to avoid + // visual lag caused by waiting to call this function on the timer timeout + usbSpaceCheck(); + + bool ok = ! vStatus; + QString usbDevice = _deviceUSBMountRequest._data.usbDevice; + if(_deviceUSBMountRequest._data.isMountRequest) { // *** USB Mount + if ( ok && ! _mounted ) { + _mounted = true; + _removed = false; + LOG_DEBUG(QString("USB flash drive %1 has been mounted on %2").arg(usbDevice).arg(USB_Mount_Point)); + emit didUSBDriveMount(); + } else { + usbError(usbDevice); + } + } else { // *** USB Unmount + if ( ok && _mounted ) { + _mounted = false; + // _umounted = true; // I think it might be needed, but needs more testing. + LOG_DEBUG(QString("USB drive %2 unmounted").arg(usbDevice)); + emit didUSBDriveUmount(); + } else { + // the error is irrelevant, commented out for now + // usbError(usbDrive); + } + } + + // log error and exit + return; +} Index: sources/storage/FileHandler.cpp =================================================================== diff -u -r5687815256ae070a9a207107088e3f72dd464da0 -rca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0 --- sources/storage/FileHandler.cpp (.../FileHandler.cpp) (revision 5687815256ae070a9a207107088e3f72dd464da0) +++ sources/storage/FileHandler.cpp (.../FileHandler.cpp) (revision ca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0) @@ -131,6 +131,30 @@ } /*! + * \brief FileHandler::backupFile + * \details Calls the gzip command + * The vPrepend file is prepended to the vDestination file, + * gets compressed with the name of the vDestination file appended with .gz + * The two files of vPrepend and vDestination will be delitted in this function. + * \param vSource - file to be prepended to the vDestination file before compression. + * \param vDestination - the file to be prepared and compressed, also the base name of the compressed file. + * \return NOT FINALIZED YET. + * \note THIS FUNCTION IS NOT TESTED DUE TO ISSUES IN RETURN VALUE CHECK OF TWO PIPED COMMAND. + */ +int FileHandler::backupFile(const QString &vPrepend, const QString &vDestination) +{ + QString cmd = QString("cat %1 %2 | gzip > %2.%3") + .arg(vPrepend) + .arg(vDestination) + .arg(Storage::gzipExt) + ; + int result = QProcess::execute(cmd, {}); + QFile::remove(vPrepend); + QFile::remove(vDestination); + return result; +} + +/*! * \brief FileHandler::copyFolder * \details Copies all the file and folders recursively. * \param vSource - The source folder Index: sources/storage/FileHandler.h =================================================================== diff -u -r5687815256ae070a9a207107088e3f72dd464da0 -rca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0 --- sources/storage/FileHandler.h (.../FileHandler.h) (revision 5687815256ae070a9a207107088e3f72dd464da0) +++ sources/storage/FileHandler.h (.../FileHandler.h) (revision ca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0) @@ -62,6 +62,7 @@ static bool read (const QString &vFileName, QString &vContent, bool vAppend = false); static int backupFile (const QString &vSource); + static int backupFile (const QString &vPrepend, const QString &vDestination); static int copyFolder (const QString &vSource , const QString &vDestination); static int moveFolder (const QString &vSource , const QString &vDestination); static int removeFolder(const QString &vFolder); Index: sources/storage/Logger.cpp =================================================================== diff -u -r6bc950c28922ec4734ef3b10cb5cbc0e68be6ce9 -rca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0 --- sources/storage/Logger.cpp (.../Logger.cpp) (revision 6bc950c28922ec4734ef3b10cb5cbc0e68be6ce9) +++ sources/storage/Logger.cpp (.../Logger.cpp) (revision ca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0) @@ -355,7 +355,6 @@ if ( gLogSerialName ) { // -X fileName = _logFileNameHDSN + _fileSeparator + _logFileNameDate - + _fileSeparator + _logFileNameTime ; } else { Index: sources/storage/Logger.h =================================================================== diff -u -r5687815256ae070a9a207107088e3f72dd464da0 -rca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0 --- sources/storage/Logger.h (.../Logger.h) (revision 5687815256ae070a9a207107088e3f72dd464da0) +++ sources/storage/Logger.h (.../Logger.h) (revision ca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0) @@ -108,7 +108,8 @@ // The HSSerial is going to be the HD Serial always, // but while booting the logger is initialized first before the HD sends its serial number. // therefore during the bootup and POST that we don't have the serial, will use BootPOST. - QString _logFileNameHDSN = "BootPOST" ; + const QString _logFileNameHDSN_default = "BootPOST" ; + QString _logFileNameHDSN = _logFileNameHDSN_default ; // The Mode is the device main states comming from HD_OpMode // this is not exactly the HD_OpModes and will be interpreted in desired cycles. @@ -146,7 +147,7 @@ }; const char * _logFileNamePendingSubExt = "u."; - const char * _logFileNameCompressExt = ".gz"; + const char * _logFileNameCompressExt = Storage::gzipExt; const TLogData _logFileNameExt { { LogType::eLogAppED, ".log" }, // Application log { LogType::eLogDebug, ".err" }, // Application error @@ -189,10 +190,6 @@ return _logFileNamePendingSubExt; } - QString logFileNameCompressExt() { - return _logFileNameCompressExt; - } - QString logFileNameExt ( LogType vLogType ) { return _logFileNameExt[vLogType]; } @@ -201,6 +198,21 @@ return _logPathNames[vLogType]; } + QString logFileNameHDSN() { + return _logFileNameHDSN; + } + + QString fileSeparator() { + return _fileSeparator; + } + QString logFileNameHDSN_default() { + return _logFileNameHDSN_default; + } + + bool isBootPOST(const QString &vFileName) { + return vFileName.contains(_Logger.logFileNameHDSN_default()); + } + /*! * \brief logFileNameExt_AD * \details Finds the type of the log by log file extention. @@ -239,7 +251,7 @@ signals: void didLogPathSet ( Logger::LogType vLogType, const QString &vLogPath ); void didLogIOFail (); - void didLogBackup ( const QString vFileName ); + void didLogBackup ( const QString &vFileName ); void didRetentionLogCS ( quint8 vPercent ); Index: sources/storage/StorageGlobals.cpp =================================================================== diff -u -rf724589acaa51725f5e5f8a746404d01804efbcf -rca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0 --- sources/storage/StorageGlobals.cpp (.../StorageGlobals.cpp) (revision f724589acaa51725f5e5f8a746404d01804efbcf) +++ sources/storage/StorageGlobals.cpp (.../StorageGlobals.cpp) (revision ca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0) @@ -141,6 +141,9 @@ const char *Log_Folder_Service = "service/" ; // Service Log const char *Log_Folder_CloudSync = "cloudsync/log/" ; // CloudSync Log + const char *gzipExt = ".gz" ; // GZip compressed file extension. + + // Txr_Max_Allowable_Trtmt_Space_Percent notice that is the folder not the path const char *Txr_Folder_Base = Settings_Path_Name ; // Base Txr Folder Index: sources/storage/StorageGlobals.h =================================================================== diff -u -rf724589acaa51725f5e5f8a746404d01804efbcf -rca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0 --- sources/storage/StorageGlobals.h (.../StorageGlobals.h) (revision f724589acaa51725f5e5f8a746404d01804efbcf) +++ sources/storage/StorageGlobals.h (.../StorageGlobals.h) (revision ca40cfec5ff6b63d4685ceb2e62ebe9366adcfd0) @@ -84,6 +84,8 @@ extern const char *Log_Folder_Service; // Service Log extern const char *Log_Folder_CloudSync; // CloudSync Log + extern const char *gzipExt; + extern const char *Txr_Folder_Base; // Base Txr Folder extern const char *Txr_Folder_Treatment; // Treatment Rep