Index: sources/device/DeviceController.cpp =================================================================== diff -u -r7c86f3854db9ad02d95681203198d75a0d65c9fa -r6c6f1f5d466badd9b4fd67be7c907234c342b2a2 --- sources/device/DeviceController.cpp (.../DeviceController.cpp) (revision 7c86f3854db9ad02d95681203198d75a0d65c9fa) +++ sources/device/DeviceController.cpp (.../DeviceController.cpp) (revision 6c6f1f5d466badd9b4fd67be7c907234c342b2a2) @@ -30,6 +30,8 @@ #include "ApplicationController.h" #include "FileHandler.h" #include "DeviceModels.h" +#include "Settings.h" +#include "encryption.h" // namespace using namespace Model; @@ -106,8 +108,20 @@ connect(&_fileSystemWatcher , SIGNAL( fileChanged(const QString &)), this , SLOT( onWatchFileChanged(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 &))); + DEVICE_DEV_INIT_CONNECTIONS_LIST + connect(this, SIGNAL(didEventThreadChange()), + this, SLOT( onEventThreadChange())); + } /*! @@ -126,6 +140,8 @@ connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(quit())); _thread->start(); moveToThread(_thread); + + emit didEventThreadChange( QPrivateSignal() ); } /*! @@ -157,7 +173,7 @@ { // 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 mmblk___ + // 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# @@ -228,21 +244,19 @@ */ void DeviceController::usbCheck() { - // disabled coco begin validated: Needed User Interaction so tested manually QString device = ""; usbSpaceCheck(); if (usbSeek(device)) { - if (! _umounted ) { + if (! _umounted ) { // avoid to mount the USB which has just been unmounted usbMount(device); - } else { + } else { // the umount is requested? usbUmount(USB_Mount_Point); } } else { if ( ! _removed ) { usbRemove(); } } - // disabled coco end } /*! @@ -256,7 +270,7 @@ 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; + static quint8 mOPercent = 0; quint8 mCPercent = 0; qint64 mCTotal = 0; @@ -394,29 +408,10 @@ * \param vDevice - USB device to be mounted (e.g. /dev/sda1) * \return true on successful mount */ -bool DeviceController::usbMount(const QString &vDevice) +void DeviceController::usbMount(const QString &vDevice) { - // disabled coco begin validated: This needs user interaction to plug-in the USB device - // has been tested manually - bool ok; - _usbDrive = vDevice.toLatin1().constData(); - ok = ::mount(_usbDrive, USB_Mount_Point, USB_File_System, - MS_SYNCHRONOUS | - MS_NOEXEC | // Disallow program execution - MS_NODEV | - MS_NOSUID , // Setting this option on a file system prevents users from introducing privileged programs onto the system and allowing non-root users to execute them. - "") == 0; - if (ok) { - _mounted = true; - _removed = false; - LOG_DEBUG(QString("USB flash drive %1 has been mounted on %2").arg(vDevice).arg(USB_Mount_Point)); - emit didUSBDriveMount(); - } else { - usbError(vDevice); - } - return ok; + usbMountReq(vDevice); } -// disabled coco end /*! * \brief DeviceController::usbUmount @@ -425,23 +420,10 @@ * \param vDevice - USB device to be unmounted (e.g. /dev/sda1) * \return true on successful unmount */ -bool DeviceController::usbUmount(const QString &vDevice) +void DeviceController::usbUmount(const QString &vDevice) { - // disabled coco begin validated: This needs user interaction to plug-out the USB device - // has been tested manually - bool ok; - ok = ::umount(vDevice.toLatin1().constData()) == 0; - if (ok) { - _mounted = false; - LOG_DEBUG(QString("USB drive %2 unmounted").arg(vDevice)); - emit didUSBDriveUmount(); - } else { - // the error is irrelevant, commented out for now - //usbError(vDevice); - } - return ok; + usbMountReq(vDevice, false); } -// disabled coco end /*! * \brief DeviceController::usbRemove @@ -477,15 +459,16 @@ vModel._data.mAccepted = false ; vModel._data.mReason = vError ; vModel._data.mMessage = DeviceError::deviceErrorText(vError, vError); - LOG_APPED(src + vModel._data.mMessage + " " + vExtraLogInfo); + LOG_APPED(" ," + src + vModel._data.mMessage + " " + vExtraLogInfo); emit didAttributeResponse(vModel._data); return true; } return false; } +///////////////////////////////////////////// DeviceBrightness /*! - * \brief DeviceController::doBrightness + * \brief DeviceController::onAttributeRequest * \details Sets the brightness level * \param vBrightness */ @@ -548,6 +531,150 @@ } } + +///////////////////////////////////////////// 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, vData.mIsGet ? RootSSHAccess_Get : RootSSHAccess_Set), _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 + + TimedProcess *timedProcess = new TimedProcess(&_processRootSSHAccess, script, 1000, { _deviceRootSSHAccessRequest._data.mRootSSHAccess ? "YES": "no" }); + 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 = false; + bool rootSSHAccess = _processRootSSHAccess.readLine().toUInt(&ok); + if ( ok ) { + _deviceRootSSHAccessResponse._data.mMessage = _deviceRootSSHAccessResponse.toString(); + _deviceRootSSHAccessResponse._data.mRootSSHAccess = rootSSHAccess; + } + else { + checkError(DeviceError::eDevice_Scripts_Error_Incorrect_Rsp, _deviceRootSSHAccessResponse, _deviceRootSSHAccessResponse.toString()); + return; + } + } + 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 + } +} + +///////////////////////////////////////////// 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) +{ + // 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 = _processCryptSetup.readAll(); + 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); + + if ( _deviceCryptSetupRequest._data.mCommand == "mount" ) emit didCryptSetupMount(model._data.mAccepted); + + // move the configuration files if the encrypted partition creation was successful. + if ( _deviceCryptSetupRequest._data.mCommand != "setup" ) return; + if ( ! model._data.mAccepted ) return; + QString msg = ""; + int err = 0 ; + //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. + Storage::Settings settings; + err = settings.configurationsMove(&msg); + if ( err ) { + model._data.mAccepted = false ; + model._data.mReason = err ; + model._data.mMessage = msg ; + emit didAttributeResponse(model.data()); + LOG_APPED_UI(model.data().mMessage); + } +} + +///////////////////////////////////////////// DeviceBluetoothPaired void DeviceController::onAttributeRequest(const DeviceBluetoothPairedResetRequestData &) { // ----- check that script exists. @@ -575,9 +702,10 @@ else deviceInfo = _processBluetoothPairedReset.readAll(); model.fromByteArray( deviceInfo, &vExitCode ); LOG_APPED_UI(model.data().mMessage); - didAttributeResponse(model.data()); + emit didAttributeResponse(model.data()); } +///////////////////////////////////////////// DeviceBluetoothPairedQuery void DeviceController::onAttributeRequest(const DeviceBluetoothPairedQueryRequestData &) { // ----- check that script exists. @@ -598,15 +726,15 @@ void DeviceController::onProcessBluetoothPairedQueryExitCode(int vExitCode, QProcess::ExitStatus vStatus) { - // The Exit code in this script is not used and is not used. + // 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 ); - didAttributeResponse(model.data()); + emit didAttributeResponse(model.data()); LOG_APPED_UI(model.data().mMessage); } @@ -657,3 +785,287 @@ { emit didWatchFileChange(vFile); } + +/*! + * \brief DeviceController::onEventThreadChange + * \details The signal handler for the DeviceController(this)::didEventThreadChange + * to start checking for the Encrypted partition readyness. + */ +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::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); +} + +/*! + * \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 the system is logged with root and configurations are in /home/root + /// therefore no need to mount the cononfiguraiton partition. + /// also for in manufacturing the partition is being set up + /// and is less steps for setup if the partition is not mounted. + if ( gEnableManufacturing ) return; + + + 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. +} + +///////////////////////////////////////////// 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."); + 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 = _processFactoryReset.readAll(); + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processFactoryReset.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + // DEBUG: qDebug() << model._data.mMessage << deviceInfo; + didAttributeResponse(model.data()); + 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."); + 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 = _processDecommission.readAll(); + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processDecommission.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + // DEBUG: qDebug() << model._data.mMessage << deviceInfo; + didAttributeResponse(model.data()); + 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"); + didAttributeResponse(model.data()); +} + +/*! + * \brief DeviceController::onProcessUSBMountExitCode + * \param vExitCode + * \param vStatus + */ +void DeviceController::onProcessUSBMountExitCode(int vExitCode, QProcess::ExitStatus vStatus) +{ + MDeviceUSBMountResponse model; + QByteArray deviceInfo = _processUSBMount.readAll(); + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processUSBMount.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + 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 = 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 = 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; +}