#include "CoreController.h" #include "OBSWConfig.h" #include "OBSWVersion.h" #include "watchdogConf.h" #include "fsfw/FSFWVersion.h" #include "fsfw/serviceinterface/ServiceInterface.h" #if OBSW_USE_TMTC_TCP_BRIDGE == 0 #include "fsfw/osal/common/UdpTmTcBridge.h" #else #include "fsfw/osal/common/TcpTmTcBridge.h" #endif #include "bsp_q7s/memory/scratchApi.h" #include "bsp_q7s/memory/SdCardManager.h" #include #include #include CoreController::Chip CoreController::currentChip = Chip::NO_CHIP; CoreController::Copy CoreController::currentCopy = Copy::NO_COPY; CoreController::CoreController(object_id_t objectId): ExtendedControllerBase(objectId, objects::NO_OBJECT, 5), opDivider(5) { ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; try { result = initWatchdogFifo(); if(result != HasReturnvaluesIF::RETURN_OK) { sif::warning << "CoreController::CoreController: Watchdog FIFO init failed" << std::endl; } result = initSdCard(); if(result != HasReturnvaluesIF::RETURN_OK) { sif::warning << "CoreController::CoreController: SD card init failed" << std::endl; } result = initBootCopy(); if(result != HasReturnvaluesIF::RETURN_OK) { sif::warning << "CoreController::CoreController: Boot copy init" << std::endl; } } catch(const std::filesystem::filesystem_error& e) { sif::error << "CoreController::CoreController: Failed with exception " << e.what() << std::endl; } } ReturnValue_t CoreController::handleCommandMessage(CommandMessage *message) { return ExtendedControllerBase::handleCommandMessage(message); } void CoreController::performControlOperation() { performWatchdogControlOperation(); } ReturnValue_t CoreController::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) { return HasReturnvaluesIF::RETURN_OK; } LocalPoolDataSetBase* CoreController::getDataSetHandle(sid_t sid) { return nullptr; } ReturnValue_t CoreController::initialize() { ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; result = scratch::writeNumber(scratch::ALLOC_FAILURE_COUNT, 0); if(result != HasReturnvaluesIF::RETURN_OK) { sif::warning << "CoreController::initialize: Setting up alloc failure " "count failed" << std::endl; } return ExtendedControllerBase::initialize(); } ReturnValue_t CoreController::checkModeCommand(Mode_t mode, Submode_t submode, uint32_t *msToReachTheMode) { return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t CoreController::initSdCard() { #if Q7S_SD_CARD_CONFIG == Q7S_SD_NONE sif::info << "No SD card initialization will be performed" << std::endl; return HasReturnvaluesIF::RETURN_OK; #else SdCardManager* sdcMan = SdCardManager::instance(); if(sdcMan == nullptr) { return HasReturnvaluesIF::RETURN_FAILED; } // Create update status file ReturnValue_t result = sdcMan->updateSdCardStateFile(); if(result != HasReturnvaluesIF::RETURN_OK) { sif::warning << "CoreController::initialize: Updating SD card state file failed" << std::endl; } auto statusPair = SdCardManager::SdStatusPair(sd::SdStatus::OFF, sd::SdStatus::OFF); result = sdcMan->getSdCardActiveStatus(statusPair); if(result != HasReturnvaluesIF::RETURN_OK) { sif::warning << "Getting SD card activity status failed" << std::endl; } #if Q7S_SD_CARD_CONFIG == Q7S_SD_COLD_REDUNDANT return sdCardColdRedundantInit(sdcMan, statusPair); #elif Q7S_SD_CARD_CONFIG == Q7S_SD_HOT_REDUNDANT sif::info << "Hot redundant SD card configuration" << std::endl; setUpSdCard(sd::SdCard::SLOT_0, sdStatus.first, "0"); setUpSdCard(sd::SdCard::SLOT_1, sdStatus.second, "1"); // Update status file sdcMan->updateSdCardStateFile(); return HasReturnvaluesIF::RETURN_OK; #endif #endif /* Q7S_SD_CARD_CONFIG != Q7S_SD_NONE */ } ReturnValue_t CoreController::sdCardSetup(SdCardManager& sdcMan, SdCardManager::SdStatusPair& statusPair,sd::SdCard sdCard, sd::SdStatus status, std::string sdString) { std::string mountString; if(sdCard == sd::SdCard::SLOT_0) { mountString = SdCardManager::SD_0_MOUNT_POINT; } else { mountString = SdCardManager::SD_1_MOUNT_POINT; } if(status == sd::SdStatus::OFF) { sif::info << "Switching on and mounting SD card " << sdString << " at " << mountString << std::endl; return sdcMan.switchOnSdCard(sdCard, true, &statusPair); } else if(status == sd::SdStatus::ON) { sif::info << "Mounting SD card " << sdString << " at " << mountString << std::endl; return sdcMan.mountSdCard(sdCard); } else { if(std::filesystem::exists(mountString)) { sif::info << "SD card " << sdString << " already on and mounted at " << mountString << std::endl; return SdCardManager::ALREADY_MOUNTED; } sif::error << "SD card mounted but expected mount point " << mountString << " not found!" << std::endl; return SdCardManager::MOUNT_ERROR; } } ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t *data, size_t size) { switch(actionId) { case(LIST_DIRECTORY_INTO_FILE): { return actionListDirectoryIntoFile(actionId, commandedBy, data, size); } case(REBOOT_OBC): { return actionPerformReboot(data, size); } default: { return HasActionsIF::INVALID_ACTION_ID; } } } ReturnValue_t CoreController::initializeAfterTaskCreation() { ReturnValue_t result = initVersionFile(); if(result != HasReturnvaluesIF::RETURN_OK) { sif::warning << "CoreController::initialize: Version initialization failed" << std::endl; } initPrint(); return result; } ReturnValue_t CoreController::sdCardColdRedundantInit(SdCardManager* sdcMan, SdCardManager::SdStatusPair& statusPair) { sd::SdCard preferredSdCard = sd::SdCard::SLOT_0; ReturnValue_t result = sdcMan->getPreferredSdCard(preferredSdCard); if(result != HasReturnvaluesIF::RETURN_OK) { if(result == scratch::KEY_NOT_FOUND) { sif::warning << "CoreController::sdCardInit: " "Preferred SD card not set. Setting to 0" << std::endl; sdcMan->setPreferredSdCard(preferredSdCard); } else { sif::warning << "CoreController::sdCardInit: Could not get preferred SD card" "information from the scratch buffer" << std::endl; } } std::string preferredString; sd::SdStatus preferredStatus = sd::SdStatus::OFF; sd::SdStatus otherStatus = sd::SdStatus::OFF; std::string otherString; sd::SdCard otherSdc = sd::SdCard::SLOT_0; if(preferredSdCard == sd::SdCard::SLOT_0) { preferredStatus = statusPair.first; preferredString = "0"; otherSdc = sd::SdCard::SLOT_1; otherStatus = statusPair.second; otherString = "1"; } else { preferredString = "1"; preferredStatus = statusPair.second; otherStatus = statusPair.first; otherSdc = sd::SdCard::SLOT_0; otherString = "0"; } sif::info << "Cold redundant SD card configuration, preferred SD card " << preferredString << std::endl; result = sdCardSetup(*sdcMan, statusPair, preferredSdCard, preferredStatus, preferredString); if(result != SdCardManager::ALREADY_MOUNTED and result != HasReturnvaluesIF::RETURN_OK) { sif::warning << "Setting up preferred card " << otherString << " in cold redundant mode failed" << std::endl; // Try other SD card and mark set up operation as failed sdCardSetup(*sdcMan, statusPair, preferredSdCard, preferredStatus, preferredString); result = HasReturnvaluesIF::RETURN_FAILED; } if(result != HasReturnvaluesIF::RETURN_FAILED and otherStatus != sd::SdStatus::OFF) { sif::info << "Switching off secondary SD card " << otherString << std::endl; // Switch off other SD card in cold redundant mode if setting up preferred one walked // without issues result = sdcMan->switchOffSdCard(otherSdc, otherStatus, &statusPair); if(result != HasReturnvaluesIF::RETURN_OK and result != SdCardManager::ALREADY_OFF) { sif::warning << "Switching off secondary SD card " << otherString << " in cold redundant mode failed" << std::endl; } } // Update status file sdcMan->updateSdCardStateFile(); return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t CoreController::incrementAllocationFailureCount() { uint32_t count = 0; ReturnValue_t result = scratch::readNumber(scratch::ALLOC_FAILURE_COUNT, count); if(result != HasReturnvaluesIF::RETURN_OK) { return result; } count++; return scratch::writeNumber(scratch::ALLOC_FAILURE_COUNT, count); } ReturnValue_t CoreController::initVersionFile() { std::string unameFileName = "/tmp/uname_version.txt"; // TODO: No -v flag for now. If the kernel version is used, need to cut off first few letters std::string unameCmd = "uname -mnrso > " + unameFileName; int result = std::system(unameCmd.c_str()); if(result != 0) { utility::handleSystemError(result, "CoreController::versionFileInit"); } std::ifstream unameFile(unameFileName); std::string unameLine; if(not std::getline(unameFile, unameLine)) { sif::warning << "CoreController::versionFileInit: Retrieving uname line failed" << std::endl; } std::string fullObswVersionString = "OBSW: v" + std::to_string(SW_VERSION) + "." + std::to_string(SW_SUBVERSION) + "." + std::to_string(SW_REVISION); std::string fullFsfwVersionString = "FSFW: v" + std::to_string(FSFW_VERSION) + "." + std::to_string(FSFW_SUBVERSION) + "." + std::to_string(FSFW_REVISION); std::string systemString = "System: " + unameLine; std::string mountPrefix = SdCardManager::instance()->getCurrentMountPrefix(); std::string versionFilePath = mountPrefix + "/conf/version.txt"; std::fstream versionFile; if(not std::filesystem::exists(versionFilePath)) { sif::info << "Writing version file " << versionFilePath << ".." << std::endl; versionFile.open(versionFilePath, std::ios_base::out); versionFile << fullObswVersionString << std::endl; versionFile << fullFsfwVersionString << std::endl; versionFile << systemString << std::endl; return HasReturnvaluesIF::RETURN_OK; } // Check whether any version has changed bool createNewFile = false; versionFile.open(versionFilePath); std::string currentVersionString; uint8_t idx = 0; while(std::getline(versionFile, currentVersionString)) { if(idx == 0) { if(currentVersionString != fullObswVersionString) { sif::info << "OBSW version changed" << std::endl; sif::info << "From " << currentVersionString << " to " << fullObswVersionString << std::endl; createNewFile = true; } } else if(idx == 1) { if(currentVersionString != fullFsfwVersionString) { sif::info << "FSFW version changed" << std::endl; sif::info << "From " << currentVersionString << " to " << fullFsfwVersionString << std::endl; createNewFile = true; } } else if(idx == 2) { if(currentVersionString != systemString) { sif::info << "System version changed" << std::endl; sif::info << "Old: " << currentVersionString << std::endl; sif::info << "New: " << systemString << std::endl; createNewFile = true; } } else { sif::warning << "Invalid version file! Rewriting it.." << std::endl; createNewFile = true; } idx++; } // Overwrite file if necessary if(createNewFile) { sif::info << "Rewriting version.txt file with updated versions.." << std::endl; versionFile.close(); versionFile.open(versionFilePath, std::ios_base::out | std::ios_base::trunc); versionFile << fullObswVersionString << std::endl; versionFile << fullFsfwVersionString << std::endl; versionFile << systemString << std::endl; } return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t CoreController::actionListDirectoryIntoFile(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t *data, size_t size) { // TODO: Packet definition for clean deserialization // 2 bytes for a and R flag, at least 5 bytes for minimum valid path /tmp with // null termination, at least 7 bytes for minimum target file name /tmp/a with // null termination. if(size < 14) { return HasActionsIF::INVALID_PARAMETERS; } // We could also make -l optional, but I can't think of a reason why to not use -l.. // This flag specifies to run ls with -a bool aFlag = data[0]; data += 1; // This flag specifies to run ls with -R bool RFlag = data[1]; data += 1; size_t remainingSize = size - 2; // One larger for null termination, which prevents undefined behaviour if the sent // strings are not 0 terminated properly std::vector repoAndTargetFileBuffer(remainingSize + 1, 0); std::memcpy(repoAndTargetFileBuffer.data(), data, remainingSize); const char* currentCharPtr = reinterpret_cast(repoAndTargetFileBuffer.data()); // Full target file name std::string repoName(currentCharPtr); size_t repoLength = repoName.length(); // The other string needs to be at least one letter plus NULL termination to be valid at all // The first string also needs to be NULL terminated, but the termination is not included // in the string length, so this is subtracted from the remaining size as well if(repoLength > remainingSize - 3) { return HasActionsIF::INVALID_PARAMETERS; } // The file length will not include the NULL termination, so we skip it currentCharPtr += repoLength + 1; std::string targetFileName(currentCharPtr); std::ostringstream oss; oss << "ls -l"; if(aFlag) { oss << "a"; } if(RFlag) { oss << "R"; } oss << " " << repoName << " > " << targetFileName; int result = std::system(oss.str().c_str()); if(result != 0) { utility::handleSystemError(result, "CoreController::actionListDirectoryIntoFile"); actionHelper.finish(false, commandedBy, actionId); } return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t CoreController::initBootCopy() { std::string fileName = "/tmp/curr_copy.txt"; if(not std::filesystem::exists(fileName)) { // Thils file is created by the systemd service eive-early-config so this should // not happen normally std::string cmd = "xsc_boot_copy > " + fileName; int result = std::system(cmd.c_str()); if(result != 0) { utility::handleSystemError(result, "CoreController::initBootCopy"); } } std::ifstream file(fileName); std::string line; std::getline(file, line); std::istringstream iss(line); int value = 0; iss >> value; currentChip = static_cast(value); iss >> value; currentCopy = static_cast(value); return HasReturnvaluesIF::RETURN_OK; } void CoreController::getCurrentBootCopy(Chip &chip, Copy ©) { // Not really thread-safe but it does not need to be chip = currentChip; copy = currentCopy; } ReturnValue_t CoreController::initWatchdogFifo() { if(not std::filesystem::exists(watchdog::FIFO_NAME)) { // Still return RETURN_OK for now sif::info << "Watchdog FIFO " << watchdog::FIFO_NAME << " does not exist, can't initiate" << " watchdog" << std::endl; return HasReturnvaluesIF::RETURN_OK; } // Open FIFO write only and non-blocking to prevent SW from killing itself. watchdogFifoFd = open(watchdog::FIFO_NAME.c_str(), O_WRONLY | O_NONBLOCK); if(watchdogFifoFd < 0) { if(errno == ENXIO) { watchdogFifoFd = RETRY_FIFO_OPEN; sif::info << "eive-watchdog not running. FIFO can not be opened" << std::endl; } else { sif::error << "Opening pipe " << watchdog::FIFO_NAME << " write-only failed with " << errno << ": " << strerror(errno) << std::endl; return HasReturnvaluesIF::RETURN_FAILED; } } return HasReturnvaluesIF::RETURN_OK; } void CoreController::initPrint() { #if OBSW_VERBOSE_LEVEL >= 1 #if OBSW_USE_TMTC_TCP_BRIDGE == 0 sif::info << "Created UDP server for TMTC commanding with listener port " << UdpTmTcBridge::DEFAULT_SERVER_PORT << std::endl; #else sif::info << "Created TCP server for TMTC commanding with listener port " << TcpTmTcBridge::DEFAULT_SERVER_PORT << std::endl; #endif if(watchdogFifoFd > 0) { sif::info << "Opened watchdog FIFO successfully.." << std::endl; } #endif } ReturnValue_t CoreController::actionPerformReboot(const uint8_t *data, size_t size) { if(size < 1) { return HasActionsIF::INVALID_PARAMETERS; } bool rebootSameBootCopy = data[0]; if(rebootSameBootCopy) { #if OBSW_VERBOSE_LEVEL >= 1 sif::info << "CoreController::actionPerformReboot: Rebooting on current image" << std::endl; #endif // Attempt graceful shutdown by unmounting and switching off SD cards SdCardManager::instance()->switchOffSdCard(sd::SdCard::SLOT_0); SdCardManager::instance()->switchOffSdCard(sd::SdCard::SLOT_1); int result = std::system("xsc_boot_copy -r"); if(result != 0) { utility::handleSystemError(result, "CoreController::executeAction"); return HasReturnvaluesIF::RETURN_FAILED; } return HasActionsIF::EXECUTION_FINISHED; } if(size < 3) { return HasActionsIF::INVALID_PARAMETERS; } #if OBSW_VERBOSE_LEVEL >= 1 sif::info << "CoreController::actionPerformReboot: Rebooting on " << static_cast(data[1]) << " " << static_cast(data[2]) << std::endl; #endif // The second byte in data is the target chip, the third byte is the target copy std::string cmdString = "xsc_boot_copy " + std::to_string(data[1]) + " " + std::to_string(data[2]); int result = std::system(cmdString.c_str()); if(result != 0) { utility::handleSystemError(result, "CoreController::executeAction"); return HasReturnvaluesIF::RETURN_FAILED; } return HasActionsIF::EXECUTION_FINISHED; } void CoreController::performWatchdogControlOperation() { // Only perform each fifth iteration if(watchdogFifoFd != 0 and opDivider.checkAndIncrement()) { if(watchdogFifoFd == RETRY_FIFO_OPEN) { // Open FIFO write only and non-blocking watchdogFifoFd = open(watchdog::FIFO_NAME.c_str(), O_WRONLY | O_NONBLOCK); if(watchdogFifoFd < 0) { if(errno == ENXIO) { watchdogFifoFd = RETRY_FIFO_OPEN; // No printout for now, would be spam return; } else { sif::error << "Opening pipe " << watchdog::FIFO_NAME << " write-only failed with " << errno << ": " << strerror(errno) << std::endl; return; } } sif::info << "Opened " << watchdog::FIFO_NAME << " successfully" << std::endl; } else if(watchdogFifoFd > 0) { // Write to OBSW watchdog FIFO here const char writeChar = 'a'; ssize_t writtenBytes = write(watchdogFifoFd, &writeChar, 1); if(writtenBytes < 0) { sif::error << "Errors writing to watchdog FIFO, code " << errno << ": " << strerror(errno) << std::endl; } } } }