text:
/*
// Copyright (c) 2018 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

#include "channel_mgmt.hpp"

#include "apphandler.hpp"
#include "user_layer.hpp"

#include <ifaddrs.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <boost/interprocess/sync/scoped_lock.hpp>
#include <cerrno>
#include <exception>
#include <experimental/filesystem>
#include <fstream>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/bus/match.hpp>
#include <sdbusplus/server/object.hpp>
#include <unordered_map>

namespace ipmi
{

using namespace phosphor::logging;

static constexpr const char* channelAccessDefaultFilename =
    "/usr/share/ipmi-providers/channel_access.json";
static constexpr const char* channelConfigDefaultFilename =
    "/usr/share/ipmi-providers/channel_config.json";
static constexpr const char* channelNvDataFilename =
    "/var/lib/ipmi/channel_access_nv.json";
static constexpr const char* channelVolatileDataFilename =
    "/run/ipmi/channel_access_volatile.json";

// TODO: Get the service name dynamically..
static constexpr const char* networkIntfServiceName =
    "xyz.openbmc_project.Network";
static constexpr const char* networkIntfObjectBasePath =
    "/xyz/openbmc_project/network";
static constexpr const char* networkChConfigIntfName =
    "xyz.openbmc_project.Channel.ChannelAccess";
static constexpr const char* privilegePropertyString = "MaxPrivilege";
static constexpr const char* dBusPropertiesInterface =
    "org.freedesktop.DBus.Properties";
static constexpr const char* propertiesChangedSignal = "PropertiesChanged";

// STRING DEFINES: Should sync with key's in JSON
static constexpr const char* nameString = "name";
static constexpr const char* isValidString = "is_valid";
static constexpr const char* activeSessionsString = "active_sessions";
static constexpr const char* maxTransferSizeString = "max_transfer_size";
static constexpr const char* channelInfoString = "channel_info";
static constexpr const char* mediumTypeString = "medium_type";
static constexpr const char* protocolTypeString = "protocol_type";
static constexpr const char* sessionSupportedString = "session_supported";
static constexpr const char* isIpmiString = "is_ipmi";
static constexpr const char* isManagementNIC = "is_management_nic";
static constexpr const char* authTypeSupportedString = "auth_type_supported";
static constexpr const char* accessModeString = "access_mode";
static constexpr const char* userAuthDisabledString = "user_auth_disabled";
static constexpr const char* perMsgAuthDisabledString = "per_msg_auth_disabled";
static constexpr const char* alertingDisabledString = "alerting_disabled";
static constexpr const char* privLimitString = "priv_limit";
static constexpr const char* authTypeEnabledString = "auth_type_enabled";

// Default values
static constexpr const char* defaultChannelName = "RESERVED";
static constexpr const uint8_t defaultMediumType =
    static_cast<uint8_t>(EChannelMediumType::reserved);
static constexpr const uint8_t defaultProtocolType =
    static_cast<uint8_t>(EChannelProtocolType::reserved);
static constexpr const uint8_t defaultSessionSupported =
    static_cast<uint8_t>(EChannelSessSupported::none);
static constexpr const uint8_t defaultAuthType =
    static_cast<uint8_t>(EAuthType::none);
static constexpr const bool defaultIsIpmiState = false;
static constexpr size_t smallChannelSize = 64;

std::unique_ptr<sdbusplus::bus::match_t> chPropertiesSignal
    __attribute__((init_priority(101)));

// String mappings use in JSON config file
static std::unordered_map<std::string, EChannelMediumType> mediumTypeMap = {
    {"reserved", EChannelMediumType::reserved},
    {"ipmb", EChannelMediumType::ipmb},
    {"icmb-v1.0", EChannelMediumType::icmbV10},
    {"icmb-v0.9", EChannelMediumType::icmbV09},
    {"lan-802.3", EChannelMediumType::lan8032},
    {"serial", EChannelMediumType::serial},
    {"other-lan", EChannelMediumType::otherLan},
    {"pci-smbus", EChannelMediumType::pciSmbus},
    {"smbus-v1.0", EChannelMediumType::smbusV11},
    {"smbus-v2.0", EChannelMediumType::smbusV20},
    {"usb-1x", EChannelMediumType::usbV1x},
    {"usb-2x", EChannelMediumType::usbV2x},
    {"system-interface", EChannelMediumType::systemInterface},
    {"oem", EChannelMediumType::oem},
    {"unknown", EChannelMediumType::unknown}};

static std::unordered_map<EInterfaceIndex, std::string> interfaceMap = {
    {interfaceKCS, "SMS"},
    {interfaceLAN1, "eth0"},
    {interfaceUnknown, "unknown"}};

static std::unordered_map<std::string, EChannelProtocolType> protocolTypeMap = {
    {"na", EChannelProtocolType::na},
    {"ipmb-1.0", EChannelProtocolType::ipmbV10},
    {"icmb-2.0", EChannelProtocolType::icmbV11},
    {"reserved", EChannelProtocolType::reserved},
    {"ipmi-smbus", EChannelProtocolType::ipmiSmbus},
    {"kcs", EChannelProtocolType::kcs},
    {"smic", EChannelProtocolType::smic},
    {"bt-10", EChannelProtocolType::bt10},
    {"bt-15", EChannelProtocolType::bt15},
    {"tmode", EChannelProtocolType::tMode},
    {"oem", EChannelProtocolType::oem}};

static std::array<std::string, 4> accessModeList = {
    "disabled", "pre-boot", "always_available", "shared"};

static std::array<std::string, 4> sessionSupportList = {
    "session-less", "single-session", "multi-session", "session-based"};

const std::array<std::string, PRIVILEGE_OEM + 1> privList = {
    "priv-reserved", "priv-callback", "priv-user",
    "priv-operator", "priv-admin",    "priv-oem"};

std::string ChannelConfig::getChannelName(const uint8_t chNum)
{
    if (!isValidChannel(chNum))
    {
        log<level::ERR>("Invalid channel number.",
                        entry("CHANNEL_ID=%d", chNum));
        throw std::invalid_argument("Invalid channel number");
    }

    return channelData[chNum].chName;
}

int ChannelConfig::convertToChannelNumberFromChannelName(
    const std::string& chName)
{
    for (const auto& it : channelData)
    {
        if (it.chName == chName)
        {
            return it.chID;
        }
    }
    log<level::ERR>("Invalid channel name.",
                    entry("CHANNEL=%s", chName.c_str()));
    throw std::invalid_argument("Invalid channel name");

    return -1;
}

std::string ChannelConfig::getChannelNameFromPath(const std::string& path)
{

    constexpr size_t length = strlen(networkIntfObjectBasePath);
    if (((length + 1) >= path.size()) ||
        path.compare(0, length, networkIntfObjectBasePath))
    {
        log<level::ERR>("Invalid object path.", entry("PATH=%s", path.c_str()));
        throw std::invalid_argument("Invalid object path");
    }
    std::string chName(path, length + 1);
    return chName;
}

void ChannelConfig::processChAccessPropChange(
    const std::string& path, const DbusChObjProperties& chProperties)
{
    // Get interface name from path. ex: '/xyz/openbmc_project/network/eth0'
    std::string chName;
    try
    {
        chName = getChannelNameFromPath(path);
    }
    catch (const std::invalid_argument& e)
    {
        log<level::ERR>("Exception: ", entry("MSG=%s", e.what()));
        return;
    }

    // Get the MaxPrivilege property value from the signal
    std::string intfPrivStr;
    std::string propName;
    for (const auto& prop : chProperties)
    {
        if (prop.first == privilegePropertyString)
        {
            propName = privilegePropertyString;
            intfPrivStr = std::get<std::string>(prop.second);
            break;
        }
    }

    if (propName != privilegePropertyString)
    {
        log<level::ERR>("Unknown signal caught.");
        return;
    }

    if (intfPrivStr.empty())
    {
        log<level::ERR>("Invalid privilege string.",
                        entry("INTF=%s", chName.c_str()));
        return;
    }

    uint8_t intfPriv = 0;
    int chNum;
    try
    {
        intfPriv = static_cast<uint8_t>(convertToPrivLimitIndex(intfPrivStr));
        chNum = convertToChannelNumberFromChannelName(chName);
    }
    catch (const std::invalid_argument& e)
    {
        log<level::ERR>("Exception: ", entry("MSG=%s", e.what()));
        return;
    }

    boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
        channelLock{*channelMutex};
    // skip updating the values, if this property change originated from IPMI.
    if (signalFlag & (1 << chNum))
    {
        signalFlag &= ~(1 << chNum);
        log<level::DEBUG>("Request originated from IPMI so ignoring signal");
        return;
    }

    // Update both volatile & Non-volatile, if there is mismatch.
    // as property change other than IPMI, has to update both volatile &
    // non-volatile data.
    checkAndReloadVolatileData();
    checkAndReloadNVData();
    if (channelData[chNum].chAccess.chNonVolatileData.privLimit != intfPriv)
    {
        // Update NV data
        channelData[chNum].chAccess.chNonVolatileData.privLimit = intfPriv;
        if (writeChannelPersistData() != 0)
        {
            log<level::ERR>("Failed to update the persist data file");
            return;
        }

        // Update Volatile data
        if (channelData[chNum].chAccess.chVolatileData.privLimit != intfPriv)
        {
            channelData[chNum].chAccess.chVolatileData.privLimit = intfPriv;
            if (writeChannelVolatileData() != 0)
            {
                log<level::ERR>("Failed to update the volatile data file");
                return;
            }
        }
    }

    return;
}

ChannelConfig& getChannelConfigObject()
{
    static ChannelConfig channelConfig;
    return channelConfig;
}

ChannelConfig::~ChannelConfig()
{
    if (signalHndlrObjectState)
    {
        chPropertiesSignal.reset();
        sigHndlrLock.unlock();
    }
}

ChannelConfig::ChannelConfig() : bus(ipmid_get_sd_bus_connection())
{
    std::ofstream mutexCleanUpFile;
    mutexCleanUpFile.open(ipmiChMutexCleanupLockFile,
                          std::ofstream::out | std::ofstream::app);
    if (!mutexCleanUpFile.good())
    {
        log<level::DEBUG>("Unable to open mutex cleanup file");
        return;
    }
    mutexCleanUpFile.close();
    mutexCleanupLock =
        boost::interprocess::file_lock(ipmiChMutexCleanupLockFile);
    if (mutexCleanupLock.try_lock())
    {
        boost::interprocess::named_recursive_mutex::remove(ipmiChannelMutex);
        channelMutex =
            std::make_unique<boost::interprocess::named_recursive_mutex>(
                boost::interprocess::open_or_create, ipmiChannelMutex);
        mutexCleanupLock.lock_sharable();
    }
    else
    {
        mutexCleanupLock.lock_sharable();
        channelMutex =
            std::make_unique<boost::interprocess::named_recursive_mutex>(
                boost::interprocess::open_or_create, ipmiChannelMutex);
    }

    initChannelPersistData();

    sigHndlrLock = boost::interprocess::file_lock(channelNvDataFilename);
    // Register it for single object and single process either netipimd /
    // host-ipmid
    if (chPropertiesSignal == nullptr && sigHndlrLock.try_lock())
    {
        log<level::DEBUG>("Registering channel signal handler.");
        chPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
            bus,
            sdbusplus::bus::match::rules::path_namespace(
                networkIntfObjectBasePath) +
                sdbusplus::bus::match::rules::type::signal() +
                sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
                sdbusplus::bus::match::rules::interface(
                    dBusPropertiesInterface) +
                sdbusplus::bus::match::rules::argN(0, networkChConfigIntfName),
            [&](sdbusplus::message::message& msg) {
                DbusChObjProperties props;
                std::string iface;
                std::string path = msg.get_path();
                msg.read(iface, props);
                processChAccessPropChange(path, props);
            });
        signalHndlrObjectState = true;
    }
}

bool ChannelConfig::isValidChannel(const uint8_t chNum)
{
    if (chNum >= maxIpmiChannels)
    {
        log<level::DEBUG>("Invalid channel ID - Out of range");
        return false;
    }

    if (channelData[chNum].isChValid == false)
    {
        log<level::DEBUG>("Channel is not valid");
    }

    return channelData[chNum].isChValid;
}

EChannelSessSupported
    ChannelConfig::getChannelSessionSupport(const uint8_t chNum)
{
    EChannelSessSupported chSessSupport =
        (EChannelSessSupported)channelData[chNum].chInfo.sessionSupported;
    return chSessSupport;
}

bool ChannelConfig::isValidAuthType(const uint8_t chNum,
                                    const EAuthType& authType)
{
    if ((authType < EAuthType::md2) || (authType > EAuthType::oem))
    {
        log<level::DEBUG>("Invalid authentication type");
        return false;
    }

    uint8_t authTypeSupported = channelData[chNum].chInfo.authTypeSupported;
    if (!(authTypeSupported & (1 << static_cast<uint8_t>(authType))))
    {
        log<level::DEBUG>("Authentication type is not supported.");
        return false;
    }

    return true;
}

int ChannelConfig::getChannelActiveSessions(const uint8_t chNum)
{
    // TODO: TEMPORARY FIX
    // Channels active session count is managed separately
    // by monitoring channel session which includes LAN and
    // RAKP layer changes. This will be updated, once the
    // authentication part is implemented.
    return channelData[chNum].activeSessCount;
}

size_t ChannelConfig::getChannelMaxTransferSize(uint8_t chNum)
{
    return channelData[chNum].maxTransferSize;
}

Cc ChannelConfig::getChannelInfo(const uint8_t chNum, ChannelInfo& chInfo)
{
    if (!isValidChannel(chNum))
    {
        log<level::DEBUG>("Invalid channel");
        return ccInvalidFieldRequest;
    }

    std::copy_n(reinterpret_cast<uint8_t*>(&channelData[chNum].chInfo),
                sizeof(channelData[chNum].chInfo),
                reinterpret_cast<uint8_t*>(&chInfo));
    return ccSuccess;
}

Cc ChannelConfig::getChannelAccessData(const uint8_t chNum,
                                       ChannelAccess& chAccessData)
{
    if (!isValidChannel(chNum))
    {
        log<level::DEBUG>("Invalid channel");
        return ccInvalidFieldRequest;
    }

    if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
    {
        log<level::DEBUG>("Session-less channel doesn't have access data.");
        return ccActionNotSupportedForChannel;
    }

    if (checkAndReloadVolatileData() != 0)
    {
        return ccUnspecifiedError;
    }

    std::copy_n(
        reinterpret_cast<uint8_t*>(&channelData[chNum].chAccess.chVolatileData),
        sizeof(channelData[chNum].chAccess.chVolatileData),
        reinterpret_cast<uint8_t*>(&chAccessData));

    return ccSuccess;
}

Cc ChannelConfig::setChannelAccessData(const uint8_t chNum,
                                       const ChannelAccess& chAccessData,
                                       const uint8_t setFlag)
{
    if (!isValidChannel(chNum))
    {
        log<level::DEBUG>("Invalid channel");
        return ccInvalidFieldRequest;
    }

    if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
    {
        log<level::DEBUG>("Session-less channel doesn't have access data.");
        return ccActionNotSupportedForChannel;
    }

    if ((setFlag & setAccessMode) &&
        (!isValidAccessMode(chAccessData.accessMode)))
    {
        log<level::DEBUG>("Invalid access mode specified");
        return ccAccessModeNotSupportedForChannel;
    }
    if ((setFlag & setPrivLimit) && (!isValidPrivLimit(chAccessData.privLimit)))
    {
        log<level::DEBUG>("Invalid privilege limit specified");
        return ccInvalidFieldRequest;
    }

    boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
        channelLock{*channelMutex};

    if (checkAndReloadVolatileData() != 0)
    {
        return ccUnspecifiedError;
    }

    if (setFlag & setAccessMode)
    {
        channelData[chNum].chAccess.chVolatileData.accessMode =
            chAccessData.accessMode;
    }
    if (setFlag & setUserAuthEnabled)
    {
        channelData[chNum].chAccess.chVolatileData.userAuthDisabled =
            chAccessData.userAuthDisabled;
    }
    if (setFlag & setMsgAuthEnabled)
    {
        channelData[chNum].chAccess.chVolatileData.perMsgAuthDisabled =
            chAccessData.perMsgAuthDisabled;
    }
    if (setFlag & setAlertingEnabled)
    {
        channelData[chNum].chAccess.chVolatileData.alertingDisabled =
            chAccessData.alertingDisabled;
    }
    if (setFlag & setPrivLimit)
    {
        channelData[chNum].chAccess.chVolatileData.privLimit =
            chAccessData.privLimit;
    }

    // Write Volatile data to file
    if (writeChannelVolatileData() != 0)
    {
        log<level::DEBUG>("Failed to update the channel volatile data");
        return ccUnspecifiedError;
    }
    return ccSuccess;
}

Cc ChannelConfig::getChannelAccessPersistData(const uint8_t chNum,
                                              ChannelAccess& chAccessData)
{
    if (!isValidChannel(chNum))
    {
        log<level::DEBUG>("Invalid channel");
        return ccInvalidFieldRequest;
    }

    if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
    {
        log<level::DEBUG>("Session-less channel doesn't have access data.");
        return ccActionNotSupportedForChannel;
    }

    if (checkAndReloadNVData() != 0)
    {
        return ccUnspecifiedError;
    }

    std::copy_n(reinterpret_cast<uint8_t*>(
                    &channelData[chNum].chAccess.chNonVolatileData),
                sizeof(channelData[chNum].chAccess.chNonVolatileData),
                reinterpret_cast<uint8_t*>(&chAccessData));

    return ccSuccess;
}

Cc ChannelConfig::setChannelAccessPersistData(const uint8_t chNum,
                                              const ChannelAccess& chAccessData,
                                              const uint8_t setFlag)
{
    if (!isValidChannel(chNum))
    {
        log<level::DEBUG>("Invalid channel");
        return ccInvalidFieldRequest;
    }

    if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
    {
        log<level::DEBUG>("Session-less channel doesn't have access data.");
        return ccActionNotSupportedForChannel;
    }

    if ((setFlag & setAccessMode) &&
        (!isValidAccessMode(chAccessData.accessMode)))
    {
        log<level::DEBUG>("Invalid access mode specified");
        return ccAccessModeNotSupportedForChannel;
    }
    if ((setFlag & setPrivLimit) && (!isValidPrivLimit(chAccessData.privLimit)))
    {
        log<level::DEBUG>("Invalid privilege limit specified");
        return ccInvalidFieldRequest;
    }

    boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
        channelLock{*channelMutex};

    if (checkAndReloadNVData() != 0)
    {
        return ccUnspecifiedError;
    }

    if (setFlag & setAccessMode)
    {
        channelData[chNum].chAccess.chNonVolatileData.accessMode =
            chAccessData.accessMode;
    }
    if (setFlag & setUserAuthEnabled)
    {
        channelData[chNum].chAccess.chNonVolatileData.userAuthDisabled =
            chAccessData.userAuthDisabled;
    }
    if (setFlag & setMsgAuthEnabled)
    {
        channelData[chNum].chAccess.chNonVolatileData.perMsgAuthDisabled =
            chAccessData.perMsgAuthDisabled;
    }
    if (setFlag & setAlertingEnabled)
    {
        channelData[chNum].chAccess.chNonVolatileData.alertingDisabled =
            chAccessData.alertingDisabled;
    }
    if (setFlag & setPrivLimit)
    {
        // Send Update to network channel config interfaces over dbus
        std::string privStr = convertToPrivLimitString(chAccessData.privLimit);
        std::string networkIntfObj = std::string(networkIntfObjectBasePath) +
                                     "/" + channelData[chNum].chName;
        try
        {
            if (0 != setDbusProperty(networkIntfServiceName, networkIntfObj,
                                     networkChConfigIntfName,
                                     privilegePropertyString, privStr))
            {
                log<level::DEBUG>(
                    "Network interface does not exist",
                    entry("INTERFACE=%s", channelData[chNum].chName.c_str()));
                return ccUnspecifiedError;
            }
        }
        catch (const sdbusplus::exception::exception& e)
        {
            log<level::ERR>("Exception: Network interface does not exist");
            return ccInvalidFieldRequest;
        }
        signalFlag |= (1 << chNum);
        channelData[chNum].chAccess.chNonVolatileData.privLimit =
            chAccessData.privLimit;
    }

    // Write persistent data to file
    if (writeChannelPersistData() != 0)
    {
        log<level::DEBUG>("Failed to update the presist data file");
        return ccUnspecifiedError;
    }
    return ccSuccess;
}

Cc ChannelConfig::getChannelAuthTypeSupported(const uint8_t chNum,
                                              uint8_t& authTypeSupported)
{
    if (!isValidChannel(chNum))
    {
        log<level::DEBUG>("Invalid channel");
        return ccInvalidFieldRequest;
    }

    authTypeSupported = channelData[chNum].chInfo.authTypeSupported;
    return ccSuccess;
}

Cc ChannelConfig::getChannelEnabledAuthType(const uint8_t chNum,
                                            const uint8_t priv,
                                            EAuthType& authType)
{
    if (!isValidChannel(chNum))
    {
        log<level::DEBUG>("Invalid channel");
        return ccInvalidFieldRequest;
    }

    if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
    {
        log<level::DEBUG>("Sessionless channel doesn't have access data.");
        return ccInvalidFieldRequest;
    }

    if (!isValidPrivLimit(priv))
    {
        log<level::DEBUG>("Invalid privilege specified.");
        return ccInvalidFieldRequest;
    }

    // TODO: Hardcoded for now. Need to implement.
    authType = EAuthType::none;

    return ccSuccess;
}

std::time_t ChannelConfig::getUpdatedFileTime(const std::string& fileName)
{
    struct stat fileStat;
    if (stat(fileName.c_str(), &fileStat) != 0)
    {
        log<level::DEBUG>("Error in getting last updated time stamp");
        return -EIO;
    }
    return fileStat.st_mtime;
}

EChannelAccessMode
    ChannelConfig::convertToAccessModeIndex(const std::string& mode)
{
    auto iter = std::find(accessModeList.begin(), accessModeList.end(), mode);
    if (iter == accessModeList.end())
    {
        log<level::ERR>("Invalid access mode.",
                        entry("MODE_STR=%s", mode.c_str()));
        throw std::invalid_argument("Invalid access mode.");
    }

    return static_cast<EChannelAccessMode>(
        std::distance(accessModeList.begin(), iter));
}

std::string ChannelConfig::convertToAccessModeString(const uint8_t value)
{
    if (accessModeList.size() <= value)
    {
        log<level::ERR>("Invalid access mode.", entry("MODE_IDX=%d", value));
        throw std::invalid_argument("Invalid access mode.");
    }

    return accessModeList.at(value);
}

CommandPrivilege
    ChannelConfig::convertToPrivLimitIndex(const std::string& value)
{
    auto iter = std::find(privList.begin(), privList.end(), value);
    if (iter == privList.end())
    {
        log<level::ERR>("Invalid privilege.",
                        entry("PRIV_STR=%s", value.c_str()));
        throw std::invalid_argument("Invalid privilege.");
    }

    return static_cast<CommandPrivilege>(std::distance(privList.begin(), iter));
}

std::string ChannelConfig::convertToPrivLimitString(const uint8_t value)
{
    if (privList.size() <= value)
    {
        log<level::ERR>("Invalid privilege.", entry("PRIV_IDX=%d", value));
        throw std::invalid_argument("Invalid privilege.");
    }

    return privList.at(value);
}

EChannelSessSupported
    ChannelConfig::convertToSessionSupportIndex(const std::string& value)
{
    auto iter =
        std::find(sessionSupportList.begin(), sessionSupportList.end(), value);
    if (iter == sessionSupportList.end())
    {
        log<level::ERR>("Invalid session supported.",
                        entry("SESS_STR=%s", value.c_str()));
        throw std::invalid_argument("Invalid session supported.");
    }

    return static_cast<EChannelSessSupported>(
        std::distance(sessionSupportList.begin(), iter));
}

EChannelMediumType
    ChannelConfig::convertToMediumTypeIndex(const std::string& value)
{
    std::unordered_map<std::string, EChannelMediumType>::iterator it =
        mediumTypeMap.find(value);
    if (it == mediumTypeMap.end())
    {
        log<level::ERR>("Invalid medium type.",
                        entry("MEDIUM_STR=%s", value.c_str()));
        throw std::invalid_argument("Invalid medium type.");
    }

    return static_cast<EChannelMediumType>(it->second);
}

EChannelProtocolType
    ChannelConfig::convertToProtocolTypeIndex(const std::string& value)
{
    std::unordered_map<std::string, EChannelProtocolType>::iterator it =
        protocolTypeMap.find(value);
    if (it == protocolTypeMap.end())
    {
        log<level::ERR>("Invalid protocol type.",
                        entry("PROTO_STR=%s", value.c_str()));
        throw std::invalid_argument("Invalid protocol type.");
    }

    return static_cast<EChannelProtocolType>(it->second);
}

Json ChannelConfig::readJsonFile(const std::string& configFile)
{
    std::ifstream jsonFile(configFile);
    if (!jsonFile.good())
    {
        log<level::INFO>("JSON file not found",
                         entry("FILE_NAME=%s", configFile.c_str()));
        return nullptr;
    }

    Json data = nullptr;
    try
    {
        data = Json::parse(jsonFile, nullptr, false);
    }
    catch (Json::parse_error& e)
    {
        log<level::DEBUG>("Corrupted channel config.",
                          entry("MSG=%s", e.what()));
        throw std::runtime_error("Corrupted channel config file");
    }

    return data;
}

int ChannelConfig::writeJsonFile(const std::string& configFile,
                                 const Json& jsonData)
{
    const std::string tmpFile = configFile + "_tmp";
    int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
                  S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (fd < 0)
    {
        log<level::ERR>("Error in creating json file",
                        entry("FILE_NAME = %s", tmpFile.c_str()));
        return -EIO;
    }
    const auto& writeData = jsonData.dump();
    if (write(fd, writeData.c_str(), writeData.size()) !=
        static_cast<ssize_t>(writeData.size()))
    {
        close(fd);
        log<level::ERR>("Error in writing configuration file",
                        entry("FILE_NAME = %s", tmpFile.c_str()));
        return -EIO;
    }
    close(fd);

    if (std::rename(tmpFile.c_str(), configFile.c_str()) != 0)
    {
        log<level::ERR>("Error in renaming temporary data file",
                        entry("FILE_NAME = %s", tmpFile.c_str()));
        return -EIO;
    }

    return 0;
}

void ChannelConfig::setDefaultChannelConfig(const uint8_t chNum,
                                            const std::string& chName)
{
    channelData[chNum].chName = chName;
    channelData[chNum].chID = chNum;
    channelData[chNum].isChValid = false;
    channelData[chNum].activeSessCount = 0;
    channelData[chNum].isManagementNIC = false;

    channelData[chNum].chInfo.mediumType = defaultMediumType;
    channelData[chNum].chInfo.protocolType = defaultProtocolType;
    channelData[chNum].chInfo.sessionSupported = defaultSessionSupported;
    channelData[chNum].chInfo.isIpmi = defaultIsIpmiState;
    channelData[chNum].chInfo.authTypeSupported = defaultAuthType;
}

uint8_t ChannelConfig::getManagementNICID()
{
    static bool idFound = false;
    static uint8_t id = 0;

    if (idFound)
    {
        return id;
    }

    for (uint8_t chIdx = 0; chIdx < maxIpmiChannels; chIdx++)
    {
        if (channelData[chIdx].isManagementNIC)
        {
            id = chIdx;
            idFound = true;
            break;
        }
    }

    if (!idFound)
    {
        id = static_cast<uint8_t>(EChannelID::chanLan1);
        idFound = true;
    }
    return id;
}

int ChannelConfig::loadChannelConfig()
{
    boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
        channelLock{*channelMutex};

    Json data = readJsonFile(channelConfigDefaultFilename);
    if (data.empty())
    {
        log<level::DEBUG>("Error in opening IPMI Channel data file");
        return -EIO;
    }

    channelData.fill(ChannelProperties{});

    // Collect the list of NIC interfaces connected to the BMC. Use this
    // information to only add IPMI channels that have active NIC interfaces.
    struct ifaddrs *ifaddr = nullptr, *ifa = nullptr;
    if (int err = getifaddrs(&ifaddr); err < 0)
    {
        log<level::DEBUG>("Unable to acquire network interfaces");
        return -EIO;
    }

    for (int chNum = 0; chNum < maxIpmiChannels; chNum++)
    {
        try
        {
            std::string chKey = std::to_string(chNum);
            Json jsonChData = data[chKey].get<Json>();
            if (jsonChData.is_null())
            {
                log<level::WARNING>(
                    "Channel not configured so loading default.",
                    entry("CHANNEL_NUM=%d", chNum));
                // If user didn't want to configure specific channel (say
                // reserved channel), then load that index with default values.
                setDefaultChannelConfig(chNum, defaultChannelName);
                continue;
            }
            Json jsonChInfo = jsonChData[channelInfoString].get<Json>();
            if (jsonChInfo.is_null())
            {
                log<level::ERR>("Invalid/corrupted channel config file");
                freeifaddrs(ifaddr);
                return -EBADMSG;
            }

            bool channelFound = true;
            // Confirm the LAN channel is present
            if (jsonChInfo[mediumTypeString].get<std::string>() == "lan-802.3")
            {
                channelFound = false;
                for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
                {
                    if (jsonChData[nameString].get<std::string>() ==
                        ifa->ifa_name)
                    {
                        channelFound = true;
                        break;
                    }
                }
            }
            ChannelProperties& chData = channelData[chNum];
            chData.chID = chNum;
            chData.chName = jsonChData[nameString].get<std::string>();
            chData.isChValid =
                channelFound && jsonChData[isValidString].get<bool>();
            chData.activeSessCount = jsonChData.value(activeSessionsString, 0);
            chData.maxTransferSize =
                jsonChData.value(maxTransferSizeString, smallChannelSize);
            if (jsonChData.count(isManagementNIC) != 0)
            {
                chData.isManagementNIC =
                    jsonChData[isManagementNIC].get<bool>();
            }

            std::string medTypeStr =
                jsonChInfo[mediumTypeString].get<std::string>();
            chData.chInfo.mediumType =
                static_cast<uint8_t>(convertToMediumTypeIndex(medTypeStr));
            std::string protoTypeStr =
                jsonChInfo[protocolTypeString].get<std::string>();
            chData.chInfo.protocolType =
                static_cast<uint8_t>(convertToProtocolTypeIndex(protoTypeStr));
            std::string sessStr =
                jsonChInfo[sessionSupportedString].get<std::string>();
            chData.chInfo.sessionSupported =
                static_cast<uint8_t>(convertToSessionSupportIndex(sessStr));
            chData.chInfo.isIpmi = jsonChInfo[isIpmiString].get<bool>();
            chData.chInfo.authTypeSupported = defaultAuthType;
        }
        catch (const Json::exception& e)
        {
            log<level::DEBUG>("Json Exception caught.",
                              entry("MSG=%s", e.what()));
            freeifaddrs(ifaddr);

            return -EBADMSG;
        }
        catch (const std::invalid_argument& e)
        {
            log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what()));
            freeifaddrs(ifaddr);
            return -EBADMSG;
        }
    }
    freeifaddrs(ifaddr);

    return 0;
}

int ChannelConfig::readChannelVolatileData()
{
    boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
        channelLock{*channelMutex};

    Json data = readJsonFile(channelVolatileDataFilename);
    if (data == nullptr)
    {
        log<level::DEBUG>("Error in opening IPMI Channel data file");
        return -EIO;
    }
    try
    {
        // Fill in global structure
        for (auto it = data.begin(); it != data.end(); ++it)
        {
            std::string chKey = it.key();
            uint8_t chNum = std::stoi(chKey, nullptr, 10);
            if (chNum >= maxIpmiChannels)
            {
                log<level::DEBUG>(
                    "Invalid channel access entry in config file");
                throw std::out_of_range("Out of range - channel number");
            }
            Json jsonChData = it.value();
            if (!jsonChData.is_null())
            {
                std::string accModeStr =
                    jsonChData[accessModeString].get<std::string>();
                channelData[chNum].chAccess.chVolatileData.accessMode =
                    static_cast<uint8_t>(convertToAccessModeIndex(accModeStr));
                channelData[chNum].chAccess.chVolatileData.userAuthDisabled =
                    jsonChData[userAuthDisabledString].get<bool>();
                channelData[chNum].chAccess.chVolatileData.perMsgAuthDisabled =
                    jsonChData[perMsgAuthDisabledString].get<bool>();
                channelData[chNum].chAccess.chVolatileData.alertingDisabled =
                    jsonChData[alertingDisabledString].get<bool>();
                std::string privStr =
                    jsonChData[privLimitString].get<std::string>();
                channelData[chNum].chAccess.chVolatileData.privLimit =
                    static_cast<uint8_t>(convertToPrivLimitIndex(privStr));
            }
            else
            {
                log<level::ERR>(
                    "Invalid/corrupted volatile channel access file",
                    entry("FILE=%s", channelVolatileDataFilename));
                throw std::runtime_error(
                    "Corrupted volatile channel access file");
            }
        }
    }
    catch (const Json::exception& e)
    {
        log<level::DEBUG>("Json Exception caught.", entry("MSG=%s", e.what()));
        throw std::runtime_error("Corrupted volatile channel access file");
    }
    catch (const std::invalid_argument& e)
    {
        log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what()));
        throw std::runtime_error("Corrupted volatile channel access file");
    }

    // Update the timestamp
    voltFileLastUpdatedTime = getUpdatedFileTime(channelVolatileDataFilename);
    return 0;
}

int ChannelConfig::readChannelPersistData()
{
    boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
        channelLock{*channelMutex};

    Json data = readJsonFile(channelNvDataFilename);
    if (data == nullptr)
    {
        log<level::DEBUG>("Error in opening IPMI Channel data file");
        return -EIO;
    }
    try
    {
        // Fill in global structure
        for (auto it = data.begin(); it != data.end(); ++it)
        {
            std::string chKey = it.key();
            uint8_t chNum = std::stoi(chKey, nullptr, 10);
            if (chNum >= maxIpmiChannels)
            {
                log<level::DEBUG>(
                    "Invalid channel access entry in config file");
                throw std::out_of_range("Out of range - channel number");
            }
            Json jsonChData = it.value();
            if (!jsonChData.is_null())
            {
                std::string accModeStr =
                    jsonChData[accessModeString].get<std::string>();
                channelData[chNum].chAccess.chNonVolatileData.accessMode =
                    static_cast<uint8_t>(convertToAccessModeIndex(accModeStr));
                channelData[chNum].chAccess.chNonVolatileData.userAuthDisabled =
                    jsonChData[userAuthDisabledString].get<bool>();
                channelData[chNum]
                    .chAccess.chNonVolatileData.perMsgAuthDisabled =
                    jsonChData[perMsgAuthDisabledString].get<bool>();
                channelData[chNum].chAccess.chNonVolatileData.alertingDisabled =
                    jsonChData[alertingDisabledString].get<bool>();
                std::string privStr =
                    jsonChData[privLimitString].get<std::string>();
                channelData[chNum].chAccess.chNonVolatileData.privLimit =
                    static_cast<uint8_t>(convertToPrivLimitIndex(privStr));
            }
            else
            {
                log<level::ERR>("Invalid/corrupted nv channel access file",
                                entry("FILE=%s", channelNvDataFilename));
                throw std::runtime_error("Corrupted nv channel access file");
            }
        }
    }
    catch (const Json::exception& e)
    {
        log<level::DEBUG>("Json Exception caught.", entry("MSG=%s", e.what()));
        throw std::runtime_error("Corrupted nv channel access file");
    }
    catch (const std::invalid_argument& e)
    {
        log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what()));
        throw std::runtime_error("Corrupted nv channel access file");
    }

    // Update the timestamp
    nvFileLastUpdatedTime = getUpdatedFileTime(channelNvDataFilename);
    return 0;
}

int ChannelConfig::writeChannelVolatileData()
{
    boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
        channelLock{*channelMutex};
    Json outData;

    try
    {
        for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++)
        {
            if (getChannelSessionSupport(chNum) != EChannelSessSupported::none)
            {
                Json jsonObj;
                std::string chKey = std::to_string(chNum);
                std::string accModeStr = convertToAccessModeString(
                    channelData[chNum].chAccess.chVolatileData.accessMode);
                jsonObj[accessModeString] = accModeStr;
                jsonObj[userAuthDisabledString] =
                    channelData[chNum].chAccess.chVolatileData.userAuthDisabled;
                jsonObj[perMsgAuthDisabledString] =
                    channelData[chNum]
                        .chAccess.chVolatileData.perMsgAuthDisabled;
                jsonObj[alertingDisabledString] =
                    channelData[chNum].chAccess.chVolatileData.alertingDisabled;
                std::string privStr = convertToPrivLimitString(
                    channelData[chNum].chAccess.chVolatileData.privLimit);
                jsonObj[privLimitString] = privStr;

                outData[chKey] = jsonObj;
            }
        }
    }
    catch (const std::invalid_argument& e)
    {
        log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what()));
        return -EINVAL;
    }

    if (writeJsonFile(channelVolatileDataFilename, outData) != 0)
    {
        log<level::DEBUG>("Error in write JSON data to file");
        return -EIO;
    }

    // Update the timestamp
    voltFileLastUpdatedTime = getUpdatedFileTime(channelVolatileDataFilename);
    return 0;
}

int ChannelConfig::writeChannelPersistData()
{
    boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
        channelLock{*channelMutex};
    Json outData;

    try
    {
        for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++)
        {
            if (getChannelSessionSupport(chNum) != EChannelSessSupported::none)
            {
                Json jsonObj;
                std::string chKey = std::to_string(chNum);
                std::string accModeStr = convertToAccessModeString(
                    channelData[chNum].chAccess.chNonVolatileData.accessMode);
                jsonObj[accessModeString] = accModeStr;
                jsonObj[userAuthDisabledString] =
                    channelData[chNum]
                        .chAccess.chNonVolatileData.userAuthDisabled;
                jsonObj[perMsgAuthDisabledString] =
                    channelData[chNum]
                        .chAccess.chNonVolatileData.perMsgAuthDisabled;
                jsonObj[alertingDisabledString] =
                    channelData[chNum]
                        .chAccess.chNonVolatileData.alertingDisabled;
                std::string privStr = convertToPrivLimitString(
                    channelData[chNum].chAccess.chNonVolatileData.privLimit);
                jsonObj[privLimitString] = privStr;

                outData[chKey] = jsonObj;
            }
        }
    }
    catch (const std::invalid_argument& e)
    {
        log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what()));
        return -EINVAL;
    }

    if (writeJsonFile(channelNvDataFilename, outData) != 0)
    {
        log<level::DEBUG>("Error in write JSON data to file");
        return -EIO;
    }

    // Update the timestamp
    nvFileLastUpdatedTime = getUpdatedFileTime(channelNvDataFilename);
    return 0;
}

int ChannelConfig::checkAndReloadNVData()
{
    std::time_t updateTime = getUpdatedFileTime(channelNvDataFilename);
    int ret = 0;
    if (updateTime != nvFileLastUpdatedTime || updateTime == -EIO)
    {
        try
        {
            ret = readChannelPersistData();
        }
        catch (const std::exception& e)
        {
            log<level::ERR>("Exception caught in readChannelPersistData.",
                            entry("MSG=%s", e.what()));
            ret = -EIO;
        }
    }
    return ret;
}

int ChannelConfig::checkAndReloadVolatileData()
{
    std::time_t updateTime = getUpdatedFileTime(channelVolatileDataFilename);
    int ret = 0;
    if (updateTime != voltFileLastUpdatedTime || updateTime == -EIO)
    {
        try
        {
            ret = readChannelVolatileData();
        }
        catch (const std::exception& e)
        {
            log<level::ERR>("Exception caught in readChannelVolatileData.",
                            entry("MSG=%s", e.what()));
            ret = -EIO;
        }
    }
    return ret;
}

int ChannelConfig::setDbusProperty(const std::string& service,
                                   const std::string& objPath,
                                   const std::string& interface,
                                   const std::string& property,
                                   const DbusVariant& value)
{
    try
    {
        auto method =
            bus.new_method_call(service.c_str(), objPath.c_str(),
                                "org.freedesktop.DBus.Properties", "Set");

        method.append(interface, property, value);

        auto reply = bus.call(method);
    }
    catch (const sdbusplus::exception::exception& e)
    {
        log<level::DEBUG>("set-property failed",
                          entry("SERVICE=%s", service.c_str()),
                          entry("OBJPATH=%s", objPath.c_str()),
                          entry("INTERFACE=%s", interface.c_str()),
                          entry("PROP=%s", property.c_str()));
        return -EIO;
    }

    return 0;
}

int ChannelConfig::getDbusProperty(const std::string& service,
                                   const std::string& objPath,
                                   const std::string& interface,
                                   const std::string& property,
                                   DbusVariant& value)
{
    try
    {
        auto method =
            bus.new_method_call(service.c_str(), objPath.c_str(),
                                "org.freedesktop.DBus.Properties", "Get");

        method.append(interface, property);

        auto reply = bus.call(method);
        reply.read(value);
    }
    catch (const sdbusplus::exception::exception& e)
    {
        log<level::DEBUG>("get-property failed",
                          entry("SERVICE=%s", service.c_str()),
                          entry("OBJPATH=%s", objPath.c_str()),
                          entry("INTERFACE=%s", interface.c_str()),
                          entry("PROP=%s", property.c_str()));
        return -EIO;
    }
    return 0;
}

int ChannelConfig::syncNetworkChannelConfig()
{
    boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
        channelLock{*channelMutex};
    bool isUpdated = false;
    for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++)
    {
        if (getChannelSessionSupport(chNum) != EChannelSessSupported::none)
        {
            std::string intfPrivStr;
            try
            {
                std::string networkIntfObj =
                    std::string(networkIntfObjectBasePath) + "/" +
                    channelData[chNum].chName;
                DbusVariant variant;
                if (0 != getDbusProperty(networkIntfServiceName, networkIntfObj,
                                         networkChConfigIntfName,
                                         privilegePropertyString, variant))
                {
                    log<level::DEBUG>("Network interface does not exist",
                                      entry("INTERFACE=%s",
                                            channelData[chNum].chName.c_str()));
                    continue;
                }
                intfPrivStr = std::get<std::string>(variant);
            }
            catch (const std::bad_variant_access& e)
            {
                log<level::DEBUG>(
                    "exception: Network interface does not exist");
                continue;
            }
            catch (const sdbusplus::exception::exception& e)
            {
                log<level::DEBUG>(
                    "exception: Network interface does not exist");
                continue;
            }

            uint8_t intfPriv =
                static_cast<uint8_t>(convertToPrivLimitIndex(intfPrivStr));
            if (channelData[chNum].chAccess.chNonVolatileData.privLimit !=
                intfPriv)
            {
                isUpdated = true;
                channelData[chNum].chAccess.chNonVolatileData.privLimit =
                    intfPriv;
                channelData[chNum].chAccess.chVolatileData.privLimit = intfPriv;
            }
        }
    }

    if (isUpdated)
    {
        // Write persistent data to file
        if (writeChannelPersistData() != 0)
        {
            log<level::DEBUG>("Failed to update the persistent data file");
            return -EIO;
        }
        // Write Volatile data to file
        if (writeChannelVolatileData() != 0)
        {
            log<level::DEBUG>("Failed to update the channel volatile data");
            return -EIO;
        }
    }

    return 0;
}

void ChannelConfig::initChannelPersistData()
{
    boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
        channelLock{*channelMutex};

    /* Always read the channel config */
    if (loadChannelConfig() != 0)
    {
        log<level::ERR>("Failed to read channel config file");
        throw std::ios_base::failure("Failed to load channel configuration");
    }

    /* Populate the channel persist data */
    if (readChannelPersistData() != 0)
    {
        // Copy default NV data to RW location
        std::experimental::filesystem::copy_file(channelAccessDefaultFilename,
                                                 channelNvDataFilename);

        // Load the channel access NV data
        if (readChannelPersistData() != 0)
        {
            log<level::ERR>("Failed to read channel access NV data");
            throw std::ios_base::failure(
                "Failed to read channel access NV configuration");
        }
    }

    // First check the volatile data file
    // If not present, load the default values
    if (readChannelVolatileData() != 0)
    {
        // Copy default volatile data to temporary location
        // NV file(channelNvDataFilename) must have created by now.
        std::experimental::filesystem::copy_file(channelNvDataFilename,
                                                 channelVolatileDataFilename);

        // Load the channel access volatile data
        if (readChannelVolatileData() != 0)
        {
            log<level::ERR>("Failed to read channel access volatile data");
            throw std::ios_base::failure(
                "Failed to read channel access volatile configuration");
        }
    }

    // Synchronize the channel config(priv) with network channel
    // configuration(priv) over dbus
    if (syncNetworkChannelConfig() != 0)
    {
        log<level::ERR>(
            "Failed to synchronize data with network channel config over dbus");
        throw std::ios_base::failure(
            "Failed to synchronize data with network channel config over dbus");
    }

    log<level::DEBUG>("Successfully completed channel data initialization.");
    return;
}

} // namespace ipmi

ext: cpp

max_stars_repo_name: vejmarie/phosphor-host-ipmid

max_issues_repo_name: vejmarie/phosphor-host-ipmid

max_forks_repo_name: vejmarie/phosphor-host-ipmid


text:
#include <fstream>
#include <iomanip>
#include <boost/container/flat_map.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <SFML/Window/Event.hpp>

#include "nlohmann/json.hpp"
#include "utility/type/Key.hpp"
#include "KeyControls.hpp"

namespace nemo
{

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

namespace {
	// For the json file storing the configurations, if you want to change a key
	// name, the path of the file, or the indentation, then change the literals
	// below.
	constexpr auto key_up     = "up";
	constexpr auto key_down   = "down";
	constexpr auto key_left   = "left";
	constexpr auto key_right  = "right";
	constexpr auto key_select = "select";
	constexpr auto key_cancel = "cancel";
	constexpr auto key_pause  = "pause";

	constexpr auto path = "data/settings/controls.json";
	constexpr auto indent = 4;
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

KeyControls::KeyControls()
	: map_(
		[](const Key a, const Key b) {
			return a < b;
		}
	)
{
	map_.reserve(static_cast<size_t>(KeyAction::count));
	load();
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

KeyControls::~KeyControls()
{
	save();
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

void
KeyControls::load()
{
	try {
		// Parse the json file.
		std::ifstream ifs(path);
		ifs.exceptions(std::ios::failbit | std::ios::badbit);
		nlohmann::json js;
		ifs >> js;

		set(
			Up     { js.at(key_up)     },
			Down   { js.at(key_down)   },
			Left   { js.at(key_left)   },
			Right  { js.at(key_right)  },
			Select { js.at(key_select) },
			Cancel { js.at(key_cancel) },
			Pause  { js.at(key_pause)  }
		);
	}
	catch (const std::ios_base::failure& e) {
		// Some error trying to read the file.
		// Use default controls.
		set();
	}
	catch (const nlohmann::json::out_of_range& e) {
		// Cannot find a key name in the file.
		// Use default controls.
		set();
	}
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

void
KeyControls::save()
const
{
	nlohmann::json js;

	for (auto m : map_) {
		const auto [key, action] = m;

		// JSON content.
		switch (action) {
			case KeyAction::Up:     js[key_up]     = sf::Keyboard::Key(key); break;
			case KeyAction::Down:   js[key_down]   = sf::Keyboard::Key(key); break;
			case KeyAction::Left:   js[key_left]   = sf::Keyboard::Key(key); break;
			case KeyAction::Right:  js[key_right]  = sf::Keyboard::Key(key); break;
			case KeyAction::Select: js[key_select] = sf::Keyboard::Key(key); break;
			case KeyAction::Cancel: js[key_cancel] = sf::Keyboard::Key(key); break;
			case KeyAction::Pause:  js[key_pause]  = sf::Keyboard::Key(key); break;
			default: break;
		}
	};

	// Save it to the file.
	std::ofstream ofs(path);
	ofs << std::setw(indent) << js << std::endl;
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

std::optional<KeyAction>
KeyControls::convert(const Key& k)
const noexcept
{
	const auto it = map_.find(k);

	if (it == map_.cend()) {
		// Not a registered key.
		return {};
	}

	[[maybe_unused]] const auto [UNUSED_, action] = *it;
	return { action };
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

void
KeyControls::set(
	Up     up,
	Down   down,
	Left   left,
	Right  right,
	Select select,
	Cancel cancel,
	Pause  pause
) noexcept
{
	// This is to prevent lingering mappings when new keys are used
	map_.clear();

	// New mappings.
	map_[up]     = KeyAction::Up;
	map_[down]   = KeyAction::Down;
	map_[left]   = KeyAction::Left;
	map_[right]  = KeyAction::Right;
	map_[select] = KeyAction::Select;
	map_[cancel] = KeyAction::Cancel;
	map_[pause]  = KeyAction::Pause;
}

}


text:
/* vim: set sw=4 sts=4 et foldmethod=syntax : */

#include "formats/read_file_format.hh"
#include "common_subgraph.hh"
#include "configuration.hh"
#include "proof.hh"

#include <boost/program_options.hpp>

#include <chrono>
#include <cstdlib>
#include <ctime>
#include <exception>
#include <iomanip>
#include <iostream>
#include <memory>
#include <optional>

#include <unistd.h>

namespace po = boost::program_options;

using std::boolalpha;
using std::cerr;
using std::cout;
using std::endl;
using std::exception;
using std::function;
using std::localtime;
using std::make_optional;
using std::make_pair;
using std::make_shared;
using std::make_unique;
using std::put_time;
using std::string;
using std::string_view;

using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::chrono::operator""s;
using std::chrono::seconds;
using std::chrono::steady_clock;
using std::chrono::system_clock;

auto main(int argc, char * argv[]) -> int
{
    try {
        po::options_description display_options{ "Program options" };
        display_options.add_options()
            ("help",                                         "Display help information")
            ("timeout",            po::value<int>(),         "Abort after this many seconds")
            ("decide",             po::value<int>(),         "Solve this decision problem")
            ("count-solutions",                              "Count the number of solutions (--decide only)")
            ("print-all-solutions",                          "Print out every solution, rather than one (--decide only)")
            ("connected",                                    "Only find connected graphs")
            ("clique",                                       "Use the clique solver")
            ;

        po::options_description input_options{ "Input file options" };
        input_options.add_options()
            ("format",             po::value<string>(),      "Specify input file format (auto, lad, vertexlabelledlad, labelledlad, dimacs)")
            ("first-format",       po::value<string>(),      "Specify input file format just for the first graph")
            ("second-format",      po::value<string>(),      "Specify input file format just for the second graph");
        display_options.add(input_options);

        po::options_description proof_logging_options{ "Proof logging options" };
        proof_logging_options.add_options()
            ("prove",               po::value<string>(),       "Write unsat proofs to this filename (suffixed with .opb and .log)")
            ("proof-names",                                    "Use 'friendly' variable names in the proof, rather than x1, x2, ...")
            ("compress-proof",                                 "Compress the proof using bz2");
        display_options.add(proof_logging_options);

        po::options_description all_options{ "All options" };
        all_options.add_options()
            ("first-file", "Specify the first graph file")
            ("second-file", "Specify the second graph file")
            ;

        all_options.add(display_options);

        po::positional_options_description positional_options;
        positional_options
            .add("first-file", 1)
            .add("second-file", 1)
            ;

        po::variables_map options_vars;
        po::store(po::command_line_parser(argc, argv)
                .options(all_options)
                .positional(positional_options)
                .run(), options_vars);
        po::notify(options_vars);

        /* --help? Show a message, and exit. */
        if (options_vars.count("help")) {
            cout << "Usage: " << argv[0] << " [options] graph-file graph-file" << endl;
            cout << endl;
            cout << display_options << endl;
            return EXIT_SUCCESS;
        }

        /* No algorithm or no input file specified? Show a message and exit. */
        if (! options_vars.count("first-file") || ! options_vars.count("second-file")) {
            cout << "Usage: " << argv[0] << " [options] graph-file graph-file" << endl;
            return EXIT_FAILURE;
        }

        /* Figure out what our options should be. */
        CommonSubgraphParams params;

        if (options_vars.count("decide"))
            params.decide = make_optional(options_vars["decide"].as<int>());

        params.connected = options_vars.count("connected");
        params.count_solutions = options_vars.count("count-solutions") || options_vars.count("print-all-solutions");
        params.clique = options_vars.count("clique");

        char hostname_buf[255];
        if (0 == gethostname(hostname_buf, 255))
            cout << "hostname = " << string(hostname_buf) << endl;
        cout << "commandline =";
        for (int i = 0 ; i < argc ; ++i)
            cout << " " << argv[i];
        cout << endl;

        auto started_at = system_clock::to_time_t(system_clock::now());
        cout << "started_at = " << put_time(localtime(&started_at), "%F %T") << endl;

        /* Read in the graphs */
        string default_format_name = options_vars.count("format") ? options_vars["format"].as<string>() : "auto";
        string first_format_name = options_vars.count("first-format") ? options_vars["first-format"].as<string>() : default_format_name;
        string second_format_name = options_vars.count("second-format") ? options_vars["second-format"].as<string>() : default_format_name;
        auto first = read_file_format(first_format_name, options_vars["first-file"].as<string>());
        auto second = read_file_format(second_format_name, options_vars["second-file"].as<string>());

        cout << "first_file = " << options_vars["first-file"].as<string>() << endl;
        cout << "second_file = " << options_vars["second-file"].as<string>() << endl;

        if (options_vars.count("print-all-solutions")) {
            params.enumerate_callback = [&] (const VertexToVertexMapping & mapping) {
                cout << "mapping = ";
                for (auto v : mapping)
                    cout << "(" << first.vertex_name(v.first) << " -> " << second.vertex_name(v.second) << ") ";
                cout << endl;
            };
        }

        if (options_vars.count("prove")) {
            bool friendly_names = options_vars.count("proof-names");
            bool compress_proof = options_vars.count("compress-proof");
            string fn = options_vars["prove"].as<string>();
            string suffix = compress_proof ? ".bz2" : "";
            params.proof = make_unique<Proof>(fn + ".opb", fn + ".log", friendly_names, compress_proof);
            cout << "proof_model = " << fn << ".opb" << suffix << endl;
            cout << "proof_log = " << fn << ".log" << suffix << endl;
        }

        /* Prepare and start timeout */
        params.timeout = make_shared<Timeout>(options_vars.count("timeout") ? seconds{ options_vars["timeout"].as<int>() } : 0s);

        /* Start the clock */
        params.start_time = steady_clock::now();

        auto result = solve_common_subgraph_problem(first, second, params);

        /* Stop the clock. */
        auto overall_time = duration_cast<milliseconds>(steady_clock::now() - params.start_time);

        params.timeout->stop();

        cout << "status = ";
        if (params.timeout->aborted())
            cout << "aborted";
        else if ((! result.mapping.empty()) || (params.count_solutions && result.solution_count > 0))
            cout << "true";
        else
            cout << "false";
        cout << endl;

        if (params.count_solutions)
            cout << "solution_count = " << result.solution_count << endl;

        cout << "nodes = " << result.nodes << endl;

        if (! result.mapping.empty() && ! options_vars.count("print-all-solutions")) {
            cout << "size = " << result.mapping.size() << endl;
            cout << "mapping = ";
            for (auto v : result.mapping)
                cout << "(" << first.vertex_name(v.first) << " -> " << second.vertex_name(v.second) << ") ";
            cout << endl;
        }

        cout << "runtime = " << overall_time.count() << endl;

        for (const auto & s : result.extra_stats)
            cout << s << endl;

        return EXIT_SUCCESS;
    }
    catch (const GraphFileError & e) {
        cerr << "Error: " << e.what() << endl;
        if (e.file_at_least_existed())
            cerr << "Maybe try specifying --format?" << endl;
        return EXIT_FAILURE;
    }
    catch (const po::error & e) {
        cerr << "Error: " << e.what() << endl;
        cerr << "Try " << argv[0] << " --help" << endl;
        return EXIT_FAILURE;
    }
    catch (const exception & e) {
        cerr << "Error: " << e.what() << endl;
        return EXIT_FAILURE;
    }
}

ext: cc

max_stars_repo_name: jdmoorman/subgraph-matching

max_issues_repo_name: jdmoorman/subgraph-matching

max_forks_repo_name: jdmoorman/subgraph-matching


text:
/*
 * @file
 * @author University of Warwick
 * @version 1.0
 *
 * @section LICENSE
 *
 * @section DESCRIPTION
 *
 * Unit Tests for the PartitionerNaive class
 */

#define BOOST_TEST_MODULE PartitionerNaive
#include <boost/test/unit_test.hpp>
#include <boost/test/output_test_stream.hpp>
#include <stdexcept>

#include "PartitionerNaive.h"
#include "Error.h"

#include "DistributedAdjacencyList.h"

#include <iostream>

// ========================================
// ============== Tests ===================
// ========================================

// ======= Setup ========
// Some tests require MPI
BOOST_AUTO_TEST_CASE(setup)
{
    int argc = boost::unit_test::framework::master_test_suite().argc;
    char ** argv = boost::unit_test::framework::master_test_suite().argv;
    MPI_Init(&argc, &argv);
}

using namespace cupcfd::partitioner;

// === Constructors ===
// Test 1:
BOOST_AUTO_TEST_CASE(constructor_test1)
{

}

// Test 2: Test that the Partitioner can be given a graph and nParts, and set up the internal
// data structures correctly
BOOST_AUTO_TEST_CASE(constructor_test2)
{

}

// === initialize ===
BOOST_AUTO_TEST_CASE(initialize_test1)
{

}

// === partition ===
// Test 1: Test that a correctly setup PartitionerNaive can partition the data without causing an error
BOOST_AUTO_TEST_CASE(partition_test1)
{
	cupcfd::error::eCodes status;
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);
	cupcfd::data_structures::DistributedAdjacencyList<int, int> graph(comm);

	// Build a test distributed graph to serve as input
	// Add Local Nodes
	if(comm.rank == 0)
	{
		int localNodes[20] = {1, 2, 3, 6, 7, 8, 9, 10, 11, 13, 18, 19,
							  21, 22, 28, 30, 31, 33, 36, 39};
		int nLocalNodes = 20;

		for(int i = 0; i < nLocalNodes; i ++)
		{
			status = graph.addLocalNode(localNodes[i]);
			BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
		}
	}
	else if(comm.rank == 1)
	{
		int localNodes[7] = {4, 5, 32, 34, 35, 26, 27};
		int nLocalNodes = 7;

		for(int i = 0; i < nLocalNodes; i ++)
		{
			status = graph.addLocalNode(localNodes[i]);
			BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
		}
	}
	else if(comm.rank == 2)
	{
		int localNodes[7] = {20, 12, 17, 38, 23, 24, 25};
		int nLocalNodes = 7;

		for(int i = 0; i < nLocalNodes; i ++)
		{
			status = graph.addLocalNode(localNodes[i]);
			BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
		}
	}
	else if(comm.rank == 3)
	{
		int localNodes[6] = {29, 37, 40, 14, 15, 16};
		int nLocalNodes = 6;

		for(int i = 0; i < nLocalNodes; i ++)
		{
			status = graph.addLocalNode(localNodes[i]);
			BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
		}
	}

	int nEdges;

	// Add Edges
	if(comm.rank == 0)
	{
		int edgeNode1[13] = {1, 2, 1, 6, 1, 28, 36, 7, 22, 32, 12, 28, 21};
		int edgeNode2[13] = {3, 8, 9, 29, 16, 5, 20, 11, 40, 18, 19, 23, 23};
		nEdges = 13;

		for(int i = 0; i < nEdges; i++)
		{
			status = graph.addUndirectedEdge(edgeNode1[i], edgeNode2[i]);
			BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
		}
	}
	else if(comm.rank == 1)
	{
		int edgeNode1[8] = {28, 18, 5, 32, 26, 5, 5, 32};
		int edgeNode2[8] = {5, 32, 27, 5, 27, 38, 37, 15};
		nEdges = 8;

		for(int i = 0; i < nEdges; i++)
		{
			status = graph.addUndirectedEdge(edgeNode1[i], edgeNode2[i]);
			BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
		}
	}
	else if(comm.rank == 2)
	{
		int edgeNode1[11] = {36, 12, 28, 20, 23, 24, 5, 23, 21, 14, 12};
		int edgeNode2[11] = {20, 19, 23, 23, 24, 25, 38, 40, 23, 23, 14};
		nEdges = 11;

		for(int i = 0; i < nEdges; i++)
		{
			status = graph.addUndirectedEdge(edgeNode1[i], edgeNode2[i]);
			BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
		}
	}
	else if(comm.rank == 3)
	{
		int edgeNode1[11] = {6, 1, 22, 37, 14, 16, 5, 32, 23, 14, 12};
		int edgeNode2[11] = {29, 16, 40, 40, 16, 14, 37, 15, 40, 23, 14};
		nEdges = 11;

		for(int i = 0; i < nEdges; i++)
		{
			status = graph.addUndirectedEdge(edgeNode1[i], edgeNode2[i]);
			BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
		}
	}

	// Finalize before use
	status = graph.finalize();
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	PartitionerNaive<int, int> * partitioner = new PartitionerNaive<int,int>(graph, 4);
	status = partitioner->partition();
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	int * results;
	int nResults;

	status = partitioner->assignRankNodes(&results, &nResults);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	//for(int i = 0; i < nResults; i++)
	//{
	//	std::cout << "Rank,i = (" << comm.rank << "," << results[i] << ")\n";
	//}

	delete(partitioner);
}

// Test 2: Test that a correctly setup PartitionerNaive can partition the data
// appropriately from a structured mesh source.
// This version has a remainder where it is not quite evenly distributed (1 extra cell on
// rank 0 and 1)
BOOST_AUTO_TEST_CASE(partition_test2)
{
	cupcfd::error::eCodes status;
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);
	cupcfd::data_structures::DistributedAdjacencyList<int, int> graph(comm);

	// Build a test distributed graph to serve as input
	// Add Local Nodes
	if(comm.rank == 0)
	{
		int localNodes[20] = {1, 2, 3, 6, 7, 8, 9, 10, 11, 13, 18, 19,
							  21, 22, 28, 30, 31, 33, 36, 39};
		int nLocalNodes = 20;

		for(int i = 0; i < nLocalNodes; i ++)
		{
			status = graph.addLocalNode(localNodes[i]);
			BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
		}
	}
	else if(comm.rank == 1)
	{
		int localNodes[7] = {4, 5, 26, 27, 32, 34, 35};
		int nLocalNodes = 7;

		for(int i = 0; i < nLocalNodes; i ++)
		{
			status = graph.addLocalNode(localNodes[i]);
			BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
		}
	}
	else if(comm.rank == 2)
	{
		int localNodes[7] = {12, 17, 20, 23, 24, 25, 38};
		int nLocalNodes = 7;

		for(int i = 0; i < nLocalNodes; i ++)
		{
			status = graph.addLocalNode(localNodes[i]);
			BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
		}
	}
	else if(comm.rank == 3)
	{
		int localNodes[8] = {14, 15, 16, 29, 37, 40, 101, 201};
		int nLocalNodes = 8;

		for(int i = 0; i < nLocalNodes; i ++)
		{
			status = graph.addLocalNode(localNodes[i]);
			BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
		}
	}

	// Edges don't matter for naive partitioner

	// Finalize before use
	status = graph.finalize();
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	PartitionerNaive<int, int> * partitioner = new PartitionerNaive<int,int>(graph, 4);

	status = partitioner->partition();
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	int * results;
	int nResults;

	status = partitioner->assignRankNodes(&results, &nResults);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	// Check Assignments are correct:

	if(comm.rank == 0)
	{
		int assignedNodeCmp[11] = {1, 2, 3, 6, 7, 8, 9, 10, 11, 13, 18};
		BOOST_CHECK_EQUAL(nResults, 11);
		BOOST_CHECK_EQUAL_COLLECTIONS(assignedNodeCmp, assignedNodeCmp + 11, results, results + 11);
	}
	else if(comm.rank == 1)
	{
		int assignedNodeCmp[11] = {19, 21, 22, 28, 30, 31, 33, 36, 39, 4, 5};
		BOOST_CHECK_EQUAL(nResults, 11);
		BOOST_CHECK_EQUAL_COLLECTIONS(assignedNodeCmp, assignedNodeCmp + 11, results, results + 11);
	}
	else if(comm.rank == 2)
	{
		int assignedNodeCmp[10] = {26, 27, 32, 34, 35, 12, 17, 20, 23, 24};
		BOOST_CHECK_EQUAL(nResults, 10);
		BOOST_CHECK_EQUAL_COLLECTIONS(assignedNodeCmp, assignedNodeCmp + 10, results, results + 10);
	}
	else if(comm.rank == 3)
	{
		int assignedNodeCmp[10] = {25, 38, 14, 15, 16, 29, 37, 40, 101, 201};
		BOOST_CHECK_EQUAL(nResults, 10);
		BOOST_CHECK_EQUAL_COLLECTIONS(assignedNodeCmp, assignedNodeCmp + 10, results, results + 10);
	}

	free(results);
	delete(partitioner);
}

// === reset ===
BOOST_AUTO_TEST_CASE(reset_test1)
{

}

// ======= Cleanup ========
// Finalize MPI
BOOST_AUTO_TEST_CASE(cleanup)
{
    // Cleanup MPI Environment
    MPI_Finalize();
}

max_stars_repo_name: thorbenlouw/CUP-CFD

max_issues_repo_name: thorbenlouw/CUP-CFD

max_forks_repo_name: thorbenlouw/CUP-CFD


text:
/* boost random/detail/integer_log2.hpp header file
 *
 * Copyright Steven Watanabe 2011
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * See http://www.boost.org for most recent version including documentation.
 *
 * $Id$
 *
 */

#ifndef BOB_BOOST_RANDOM_DETAIL_INTEGER_LOG2_HPP
#define BOB_BOOST_RANDOM_DETAIL_INTEGER_LOG2_HPP

#include <boost/config.hpp>
#include <boost/limits.hpp>
#include <boost/pending/integer_log2.hpp>

namespace bob {
namespace core {
namespace random {
namespace detail {

#ifndef BOOST_RANDOM_DETAIL_CONSTEXPR
#if !defined(BOOST_NO_CXX11_CONSTEXPR)
#define BOOST_RANDOM_DETAIL_CONSTEXPR constexpr
#elif defined(BOOST_MSVC)
#define BOOST_RANDOM_DETAIL_CONSTEXPR __forceinline
#elif defined(__GNUC__) && __GNUC__ >= 4
#define BOOST_RANDOM_DETAIL_CONSTEXPR inline __attribute__((__const__)) __attribute__((__always_inline__))
#else
#define BOOST_RANDOM_DETAIL_CONSTEXPR inline
#endif
#endif

template<int Shift>
struct integer_log2_impl
{
#if defined(BOOST_NO_CXX11_CONSTEXPR)
    template<class T>
    BOOST_RANDOM_DETAIL_CONSTEXPR static int apply(T t, int accum)
    {
        int update = ((t >> Shift) != 0) * Shift;
        return integer_log2_impl<Shift / 2>::apply(t >> update, accum + update);
    }
#else
    template<class T>
    BOOST_RANDOM_DETAIL_CONSTEXPR static int apply2(T t, int accum, int update)
    {
        return integer_log2_impl<Shift / 2>::apply(t >> update, accum + update);
    }

    template<class T>
    BOOST_RANDOM_DETAIL_CONSTEXPR static int apply(T t, int accum)
    {
        return apply2(t, accum, ((t >> Shift) != 0) * Shift);
    }
#endif
};

template<>
struct integer_log2_impl<1>
{
    template<class T>
    BOOST_RANDOM_DETAIL_CONSTEXPR static int apply(T t, int accum)
    {
        return int(t >> 1) + accum;
    }
};

template<class T>
BOOST_RANDOM_DETAIL_CONSTEXPR int integer_log2(T t)
{
    return integer_log2_impl<
        ::boost::detail::max_pow2_less<
            ::std::numeric_limits<T>::digits, 4
        >::value
    >::apply(t, 0);
}

} // namespace detail
} // namespace random
} // namespace core
} // namespace bob

#endif // BOB_BOOST_RANDOM_DETAIL_INTEGER_LOG2_HPP

ext: hpp


text:
// Boost.Geometry Index
//
// Quickbook Examples
//
// Copyright (c) 2011-2014 Adam Wulkiewicz, Lodz, Poland.
//
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

//[rtree_range_adaptors

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>

#include <boost/geometry/index/rtree.hpp>

// Boost.Range
#include <boost/range.hpp>
// adaptors
#include <boost/range/adaptor/indexed.hpp>
#include <boost/range/adaptor/transformed.hpp>

// a container
#include <vector>

// just for output
#include <iostream>

namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;

// Define a function object converting a value_type of indexed Range into std::pair<>.
// This is a generic implementation but of course it'd be possible to use some
// specific types. One could also take Value as template parameter and access
// first_type and second_type members, etc.
template <typename First, typename Second>
struct pair_maker
{
    typedef std::pair<First, Second> result_type;
    template<typename T>
    inline result_type operator()(T const& v) const
    {
        return result_type(v.value(), v.index());
    }
};

int main()
{
    typedef bg::model::point<float, 2, bg::cs::cartesian> point;
    typedef bg::model::box<point> box;

    typedef std::vector<box> container;
    typedef container::size_type size_type;

    typedef std::pair<box, size_type> value;

    // create a container of boxes
    container boxes;
    for ( size_type i = 0 ; i < 10 ; ++i )
    {
        // add a box into the container
        box b(point(i + 0.0f, i + 0.0f), point(i + 0.5f, i + 0.5f));
        boxes.push_back(b);
    }

    // create the rtree passing a Range
    bgi::rtree< value, bgi::quadratic<16> >
        rtree(boxes | boost::adaptors::indexed()
                    | boost::adaptors::transformed(pair_maker<box, size_type>()));

    // print the number of values using boxes[0] as indexable
    std::cout << rtree.count(boxes[0]) << std::endl;

    return 0;
}

//]


text:
#include "knight.h"
#include "loading.h"
#include <boost/lexical_cast.hpp>

pla::Knight::Knight()
{
	free();
}

pla::Knight::~Knight()
{
	free();
}

void pla::Knight::free()
{
	screen_w = screen_h = 0;

	if (!sprites.empty())
	{
		for (auto &it : sprites)
		{
			delete it;
			it = nullptr;
		}

		sprites.clear();
	}

	reset();
}

void pla::Knight::reset()
{
	resetState();
	coxing.reset();

#ifdef __TEST__
	collisionMode = false;
#endif
}

void pla::Knight::load(const float &screen_w, const float &screen_h)
{
	free();

	this->screen_w = screen_w;
	this->screen_h = screen_h;

	// Load Sprites.
	for (int i = 0; i < STATES::SIZE; ++i)
	{
		sprites.push_back(new cmm::Sprite);
		Loading::add(sprites[i]->load("images/platform/knight/" + std::to_string(i) + ".png", offset_max));
		if (Loading::isError())	return;
		sprites[i]->setScale(specs.scale, specs.scale);
	}

	// Set rects size
	rect.width = static_cast<int>(sprites[IDLE]->getWidth() * 0.75);
	rect.height = static_cast<int>(sprites[IDLE]->getHeight() * 0.9);
	attackRect.width = static_cast<int>(sprites[IDLE]->getWidth() * 0.7);
	attackRect.height = static_cast<int>(sprites[IDLE]->getHeight() * 0.6);

#ifdef __TEST__
	sf::Color color = cmm::GREEN_COLOR;
	color.a = MAX_ALPHA / 3;
	shape.setFillColor(color);
	shape.setSize(sf::Vector2f(static_cast<float>(rect.width), static_cast<float>(rect.height)));
	color = cmm::RED_COLOR;
	color.a = MAX_ALPHA / 3;
	attackShape.setFillColor(color);
	attackShape.setSize(sf::Vector2f(static_cast<float>(attackRect.width), static_cast<float>(attackRect.height)));
#endif
}

void pla::Knight::handle(const sf::Event &event)
{
	coxing.releaseJumping(event);
}

void pla::Knight::draw(sf::RenderTexture &rt)
{
	if (state == STATES::VOID)
		return;

	sprites[state]->setOffset(static_cast<int>(offset));
	rt.draw(*sprites[state]);

#ifdef __TEST__
	if (collisionMode)
	{
		rt.draw(shape);
		if (isAttack())
		{
			rt.draw(attackShape);
		}
	}
#endif
}

void pla::Knight::mechanics(const float &elapsedTime)
{
	offset += elapsedTime * OPS;
	if (static_cast<int>(offset) >= offset_max)
	{
		offset = 0;
		coxing.attack = false;
		coxing.jump = false;
	}
}

#ifdef __TEST__
void pla::Knight::switchCollision()
{
	collisionMode = !collisionMode;
}
#endif

sf::IntRect& pla::Knight::getRect()
{
	return rect;
}

const sf::IntRect& pla::Knight::getAttackRect()
{
	return attackRect;
}

//const int& Knight::getX() const
//{
//	return rect.left;
//}
//
//const int& Knight::getY() const
//{
//	return rect.top;
//}

bool pla::Knight::isAlive()
{
	return !coxing.die && static_cast<int>(offset) == offset_max;
}

bool pla::Knight::isAttack()
{
	return coxing.attack && offset > 3 && offset < 8;
}

bool pla::Knight::isLeftAligned() const
{
	return !align;
}

const bool &pla::Knight::isRightAligned() const
{
	return align;
}

bool pla::Knight::moveLeft(const float &elapsedTime)
{
	if (coxing.isMovingLeft() && (!coxing.attack || coxing.jump))
	{
		coxing.attack = false;
		align = ALIGN::LEFT;
		walk(elapsedTime);
		xy.x -= (coxing.walkTimer > coxing.walkLine ? specs.velocity : specs.hvelocity) * elapsedTime;
		return true;
	}

	return false;
}

void pla::Knight::undoMoveLeft(const float &elapsedTime)
{
	xy.x += (coxing.walkTimer >= coxing.walkLine ? specs.velocity : specs.hvelocity) * elapsedTime;
}

bool pla::Knight::moveRight(const float &elapsedTime)
{
	if (coxing.isMovingRight() && (!coxing.attack || coxing.jump))
	{
		align = ALIGN::RIGHT;
		walk(elapsedTime);
		xy.x += (coxing.walkTimer > coxing.walkLine ? specs.velocity : specs.hvelocity) * elapsedTime;
		return true;
	}

	return false;
}

void pla::Knight::undoMoveRight(const float &elapsedTime)
{
	xy.x -= (coxing.walkTimer >= coxing.walkLine ? specs.velocity : specs.hvelocity) * elapsedTime;
}

void pla::Knight::idle(const float &elapsedTime)
{
	if (!coxing.attack && !coxing.jump)
	{
		state = coxing.falling ? STATES::FALL : STATES::IDLE;
	}

	if (coxing.walkTimer > 0)
	{
		coxing.walkTimer -= elapsedTime;
		if (coxing.walkTimer < 0)
			coxing.walkTimer = 0;
	}
}

bool pla::Knight::jump(const float &elapsedTime)
{
	if (coxing.isJumping())
	{
		++coxing.jumpCounter;
		state = STATES::JUMP;
		offset = 0;
		coxing.jump = true;
		coxing.jumpReleased = false;
	}

	if (state == STATES::JUMP_ATTACK)
	{
		xy.y -= specs.gravity * elapsedTime; // keep the level
		return false;
	}
	else if ((state == STATES::JUMP) && offset < 5) // go up
	{
		xy.y -= specs.gravity * 2 * elapsedTime;
		return true;
	}

	return false;
}

void pla::Knight::undoJump(const float &elapsedTime)
{
	xy.y += specs.gravity * 2 * elapsedTime;
}

void pla::Knight::attack()
{
	if (coxing.isAttacking())
	{
		if (coxing.jump && static_cast<int>(offset) < 5)
		{
			state = STATES::JUMP_ATTACK;
			coxing.attack = true;
		}
		else if(state != STATES::JUMP_ATTACK && coxing.jumpCounter == 0) // able to attack only if knight is on the ground
		{
			state = STATES::ATTACK;
			if (!coxing.attack)
			{
				coxing.attack = true;
				offset = 0;
			}
		}
	}
}

void pla::Knight::rest()
{
#ifdef __TEST__
	if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Q))
	{
		state = STATES::DIE;
		offset = 0;
		coxing.die = true;
	}
#endif

	setAlign();
	setPosition();
	setRect();
	setAttackRect();
}

void pla::Knight::gravity(const float &elapsedTime)
{
	coxing.falling = true;
	xy.y += specs.gravity * elapsedTime;
}

void pla::Knight::undoGravity(const float &elapsedTime)
{
	coxing.jumpCounter = 0;
	coxing.jumpReleased = true;
	coxing.falling = false;
	xy.y -= specs.gravity * elapsedTime;
}

void pla::Knight::read(std::string &str)
{
	xy.x = boost::lexical_cast<int>(cmm::extractFromString(str, "x:", cmm::CSPACE)) + sprites[IDLE]->getWidth() / 2;
	xy.y = (boost::lexical_cast<int>(cmm::extractFromString(str, "y:", cmm::CSPACE)) * -1) + screen_h;
	if (cmm::extractFromString(str, "ai:", cmm::CNEWLINE) != cmm::SEMPTY)
	{
		specs.set(specs.HEART_POINTS, boost::lexical_cast<int>(cmm::extractFromString(str, "hp:", ',')));
		specs.set(specs.MAGIC_POINTS, boost::lexical_cast<int>(cmm::extractFromString(str, "mp:", ',')));
		specs.set(specs.ARMOUR, boost::lexical_cast<int>(cmm::extractFromString(str, "armour:", ',')));
		specs.set(specs.MAGIC_RESISTANT, boost::lexical_cast<int>(cmm::extractFromString(str, "mr:", ',')));
		specs.set(specs.MOVEMENT_SPEED, boost::lexical_cast<int>(cmm::extractFromString(str, "ms:", ',')));
		specs.set(specs.DAMAGE, boost::lexical_cast<int>(cmm::extractFromString(str, "dmg:", ',')));
		specs.set(specs.MAGIC_DAMAGE, boost::lexical_cast<int>(cmm::extractFromString(str, "mdmg:", ',')));
		specs.set(specs.LUCK, boost::lexical_cast<int>(cmm::extractFromString(str, "luck:", ')')));
	}

	specs.prepare();

	setAlign();
	setPosition();
	setRect();
	setAttackRect();
}

void pla::Knight::walk(const double &elapsedTime)
{
	if (!coxing.jump /*&& state != STATES::ATTACK*/)
	{
		coxing.walkTimer < coxing.walkLine ? state = STATES::WALK : state = STATES::RUN;
	}

	if (coxing.walkTimer < coxing.walkLine * 3)
	{
		coxing.walkTimer += static_cast<float>(elapsedTime);
	}
}

void pla::Knight::setPosition()
{
	// set y
	float newX = xy.x;
	float newY = xy.y - sprites[IDLE]->getHeight();
	float cw = sprites[state]->getWidth(); // current width

	if (align == ALIGN::RIGHT)
	{
		switch (state)
		{
		case STATES::IDLE:
		case STATES::FALL:
		case STATES::WALK:
		case STATES::RUN: newX -= cw / 2.0f; break;
		case STATES::JUMP: newX -= cw / 1.9f; break;
		case STATES::ATTACK: newX -= cw / 2.18f; break;
		case STATES::JUMP_ATTACK: newX -= cw / 2.2f; break;
		}
	}
	else
	{
		switch (state)
		{
		case STATES::IDLE:
		case STATES::FALL:
		case STATES::WALK:
		case STATES::RUN: newX += cw / 2.0f; break;
		case STATES::JUMP: newX += cw / 1.9f; break;
		case STATES::ATTACK: newX += cw / 2.18f; break;
		case STATES::JUMP_ATTACK: newX += cw / 2.2f; break;
		}
	}

	sprites[state]->setPosition(newX, newY + 3);
}

void pla::Knight::setAlign()
{
	if (align == ALIGN::LEFT)
		sprites[state]->setScale(-specs.scale, specs.scale);
	else
		sprites[state]->setScale(specs.scale, specs.scale);
}

void pla::Knight::setRect()
{
	rect.left = static_cast<int>(xy.x - rect.width / 2);
	rect.top = static_cast<int>(xy.y - rect.height);

#ifdef __TEST__
	if (collisionMode)
	{
		shape.setPosition(static_cast<float>(rect.left), static_cast<float>(rect.top));
	}
#endif
}

void pla::Knight::setAttackRect()
{
	if (align == ALIGN::RIGHT)
		attackRect.left = static_cast<int>(xy.x);
	else
		attackRect.left = static_cast<int>(xy.x) - attackRect.width;
	attackRect.top = static_cast<int>(xy.y) -static_cast<int>(rect.height / 1.8f);

#ifdef __TEST__
	if (collisionMode)
	{
		if (isAttack())
		{
			attackShape.setPosition(static_cast<float>(attackRect.left), static_cast<float>(attackRect.top));
		}
	}
#endif
}


text:
/*
 *          Copyright Andrey Semashev 2007 - 2013.
 * Distributed under the Boost Software License, Version 1.0.
 *    (See accompanying file LICENSE_1_0.txt or copy at
 *          http://www.boost.org/LICENSE_1_0.txt)
 */
/*!
 * \file   date_time_types.hpp
 * \author Andrey Semashev
 * \date   13.03.2008
 *
 * The header contains definition of date and time-related types supported by the library by default.
 */

#ifndef BOOST_LOG_DATE_TIME_TYPES_HPP_INCLUDED_
#define BOOST_LOG_DATE_TIME_TYPES_HPP_INCLUDED_

#include <ctime>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/copy.hpp>
#include <boost/mpl/back_inserter.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/date_time/gregorian/gregorian_types.hpp>
#include <boost/date_time/local_time/local_time_types.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/log/detail/config.hpp>
#include <boost/log/detail/header.hpp>

#ifdef BOOST_HAS_PRAGMA_ONCE
#pragma once
#endif

namespace boost {

BOOST_LOG_OPEN_NAMESPACE

/*!
 * An MPL-sequence of natively supported date and time types of attributes
 */
typedef mpl::vector<
    std::time_t,
    std::tm
> native_date_time_types;

/*!
 * An MPL-sequence of Boost date and time types of attributes
 */
typedef mpl::vector<
    posix_time::ptime,
    local_time::local_date_time
> boost_date_time_types;

/*!
 * An MPL-sequence with the complete list of the supported date and time types
 */
typedef mpl::copy<
    boost_date_time_types,
    mpl::back_inserter< native_date_time_types >
>::type date_time_types;

/*!
 * An MPL-sequence of natively supported date types of attributes
 */
typedef native_date_time_types native_date_types;

/*!
 * An MPL-sequence of Boost date types of attributes
 */
typedef mpl::push_back<
    boost_date_time_types,
    gregorian::date
>::type boost_date_types;

/*!
 * An MPL-sequence with the complete list of the supported date types
 */
typedef mpl::copy<
    boost_date_types,
    mpl::back_inserter< native_date_types >
>::type date_types;

/*!
 * An MPL-sequence of natively supported time types
 */
typedef native_date_time_types native_time_types;

//! An MPL-sequence of Boost time types
typedef boost_date_time_types boost_time_types;

/*!
 * An MPL-sequence with the complete list of the supported time types
 */
typedef date_time_types time_types;

/*!
 * An MPL-sequence of natively supported time duration types of attributes
 */
typedef mpl::vector<
    double // result of difftime
> native_time_duration_types;

/*!
 * An MPL-sequence of Boost time duration types of attributes
 */
typedef mpl::vector<
    posix_time::time_duration,
    gregorian::date_duration
> boost_time_duration_types;

/*!
 * An MPL-sequence with the complete list of the supported time duration types
 */
typedef mpl::copy<
    boost_time_duration_types,
    mpl::back_inserter< native_time_duration_types >
>::type time_duration_types;

/*!
 * An MPL-sequence of Boost time duration types of attributes
 */
typedef mpl::vector<
    posix_time::time_period,
    local_time::local_time_period,
    gregorian::date_period
> boost_time_period_types;

/*!
 * An MPL-sequence with the complete list of the supported time period types
 */
typedef boost_time_period_types time_period_types;

BOOST_LOG_CLOSE_NAMESPACE // namespace log

} // namespace boost

#include <boost/log/detail/footer.hpp>

#endif // BOOST_LOG_DATE_TIME_TYPES_HPP_INCLUDED_

max_stars_repo_name: ballisticwhisper/boost

max_issues_repo_name: ballisticwhisper/boost

max_forks_repo_name: ballisticwhisper/boost


text:
// (C) Copyright 2012 Howard Hinnant
// (C) Copyright 2012 Vicente Botet
//
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

// adapted from the example given by Howard Hinnant in

#define BOOST_THREAD_VERSION 4

#include <iostream>
#include <boost/thread/scoped_thread.hpp>
#include <boost/thread/ostream_buffer.hpp>

void use_cerr()
{
  using namespace boost;
  chrono::steady_clock::time_point tf = chrono::steady_clock::now() + chrono::seconds(5);
  int i = 0;
  while (chrono::steady_clock::now() < tf)
  {
    ostream_buffer<std::ostream> mcerr(std::cerr);
    mcerr.stream() << "logging data to cerr " << i++ << "\n";
    this_thread::sleep_for(chrono::milliseconds(250));
  }
}

void use_cout()
{
  using namespace boost;
  chrono::steady_clock::time_point tf = chrono::steady_clock::now() + chrono::seconds(5);
  int i = 0;
  while (chrono::steady_clock::now() < tf)
  {
    ostream_buffer<std::ostream> mcout(std::cout);
    mcout.stream() << "logging data to cout " << i++ << "\n";
    this_thread::sleep_for(chrono::milliseconds(500));
  }
}

int main()
{
  using namespace boost;

  scoped_thread<> t1(&use_cerr);
  scoped_thread<> t2(&use_cout);
  this_thread::sleep_for(chrono::seconds(2));
  std::string nm = "he, he\n";
  {
    ostream_buffer<std::ostream> mcout(std::cout);
    mcout.stream() << "Enter name: \n";
  }
  t1.join();
  t2.join();
  {
    ostream_buffer<std::ostream> mcout(std::cout);
    mcout.stream() << nm;
  }
  return 0;
}


text:
//  (C) Copyright Raffi Enficiaud 2018.
//  Distributed under the Boost Software License, Version 1.0.
//  (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/test for the library home page.

// checks that the master test suite is not copyable (compilation failure)

#define BOOST_TEST_MODULE "master test suite non copyable"
#include <boost/test/included/unit_test.hpp>
#include <exception>
#include <iostream>

BOOST_AUTO_TEST_CASE( check )
{
  using namespace boost::unit_test;
  master_test_suite_t master_copy = framework::master_test_suite();
  std::cerr << "Test called with " << master_copy.argc << " arguments" << std::endl; // to prevent optimization, just in case it compiles...
  throw std::runtime_error("Should not reach here ");
}