<commit_before>/*
 *
 *    Copyright (c) 2021 Project CHIP Authors
 *    All rights reserved.
 *
 *    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.
 */

/**
 *    @file
 *      Implementation of CHIP Device Controller Factory, a utility/manager class
 *      that vends Controller objects
 */

#include <controller/CHIPDeviceControllerFactory.h>

#include <app/OperationalDeviceProxy.h>
#include <app/util/DataModelHandler.h>
#include <lib/support/ErrorStr.h>
#include <messaging/ReliableMessageProtocolConfig.h>

#if CONFIG_DEVICE_LAYER
#include <platform/CHIPDeviceLayer.h>
#include <platform/ConfigurationManager.h>
#endif

#include <app/server/Dnssd.h>
#include <protocols/secure_channel/CASEServer.h>
#include <protocols/secure_channel/SimpleSessionResumptionStorage.h>

using namespace chip::Inet;
using namespace chip::System;
using namespace chip::Credentials;

namespace chip {
namespace Controller {

CHIP_ERROR DeviceControllerFactory::Init(FactoryInitParams params)
{

    // SystemState is only set the first time init is called, after that it is managed
    // internally. If SystemState is set then init has already completed.
    if (mSystemState != nullptr)
    {
        ChipLogError(Controller, "Device Controller Factory already initialized...");
        return CHIP_NO_ERROR;
    }

    // Save our initialization state that we can't recover later from a
    // created-but-shut-down system state.
    mListenPort               = params.listenPort;
    mFabricIndependentStorage = params.fabricIndependentStorage;
    mEnableServerInteractions = params.enableServerInteractions;

    CHIP_ERROR err = InitSystemState(params);

    return err;
}

CHIP_ERROR DeviceControllerFactory::InitSystemState()
{
    FactoryInitParams params;
    if (mSystemState != nullptr)
    {
        params.systemLayer        = mSystemState->SystemLayer();
        params.tcpEndPointManager = mSystemState->TCPEndPointManager();
        params.udpEndPointManager = mSystemState->UDPEndPointManager();
#if CONFIG_NETWORK_LAYER_BLE
        params.bleLayer = mSystemState->BleLayer();
#endif
        params.listenPort               = mListenPort;
        params.fabricIndependentStorage = mFabricIndependentStorage;
        params.enableServerInteractions = mEnableServerInteractions;
        params.groupDataProvider        = mSystemState->GetGroupDataProvider();
    }

    return InitSystemState(params);
}

CHIP_ERROR DeviceControllerFactory::InitSystemState(FactoryInitParams params)
{
    if (mSystemState != nullptr && mSystemState->IsInitialized())
    {
        return CHIP_NO_ERROR;
    }

    if (mSystemState != nullptr)
    {
        mSystemState->Release();
        chip::Platform::Delete(mSystemState);
        mSystemState = nullptr;
    }

    DeviceControllerSystemStateParams stateParams;
#if CONFIG_DEVICE_LAYER
    ReturnErrorOnFailure(DeviceLayer::PlatformMgr().InitChipStack());

    stateParams.systemLayer        = &DeviceLayer::SystemLayer();
    stateParams.tcpEndPointManager = DeviceLayer::TCPEndPointManager();
    stateParams.udpEndPointManager = DeviceLayer::UDPEndPointManager();
#else
    stateParams.systemLayer        = params.systemLayer;
    stateParams.tcpEndPointManager = params.tcpEndPointManager;
    stateParams.udpEndPointManager = params.udpEndPointManager;
    ChipLogError(Controller, "Warning: Device Controller Factory should be with a CHIP Device Layer...");
#endif // CONFIG_DEVICE_LAYER

    VerifyOrReturnError(stateParams.systemLayer != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
    VerifyOrReturnError(stateParams.udpEndPointManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT);

#if CONFIG_NETWORK_LAYER_BLE
#if CONFIG_DEVICE_LAYER
    stateParams.bleLayer = DeviceLayer::ConnectivityMgr().GetBleLayer();
#else
    stateParams.bleLayer = params.bleLayer;
#endif // CONFIG_DEVICE_LAYER
    VerifyOrReturnError(stateParams.bleLayer != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
#endif

    stateParams.transportMgr = chip::Platform::New<DeviceTransportMgr>();

    //
    // The logic below expects IPv6 to be at index 0 of this tuple. Please do not alter that.
    //
    ReturnErrorOnFailure(stateParams.transportMgr->Init(Transport::UdpListenParameters(stateParams.udpEndPointManager)
                                                            .SetAddressType(Inet::IPAddressType::kIPv6)
                                                            .SetListenPort(params.listenPort)
#if INET_CONFIG_ENABLE_IPV4
                                                            ,
                                                        Transport::UdpListenParameters(stateParams.udpEndPointManager)
                                                            .SetAddressType(Inet::IPAddressType::kIPv4)
                                                            .SetListenPort(params.listenPort)
#endif
#if CONFIG_NETWORK_LAYER_BLE
                                                            ,
                                                        Transport::BleListenParameters(stateParams.bleLayer)
#endif
                                                            ));

    stateParams.sessionMgr                                    = chip::Platform::New<SessionManager>();
    SimpleSessionResumptionStorage * sessionResumptionStorage = chip::Platform::New<SimpleSessionResumptionStorage>();
    stateParams.sessionResumptionStorage                      = sessionResumptionStorage;
    stateParams.exchangeMgr                                   = chip::Platform::New<Messaging::ExchangeManager>();
    stateParams.messageCounterManager                         = chip::Platform::New<secure_channel::MessageCounterManager>();
    stateParams.groupDataProvider                             = params.groupDataProvider;

    // if no fabricTable was provided, create one and track it in stateParams for cleanup
    FabricTable * tempFabricTable = nullptr;
    stateParams.fabricTable       = params.fabricTable;
    if (stateParams.fabricTable == nullptr)
    {
        stateParams.fabricTable = tempFabricTable = chip::Platform::New<FabricTable>();
        ReturnErrorOnFailure(stateParams.fabricTable->Init(params.fabricIndependentStorage));
    }
    ReturnErrorOnFailure(sessionResumptionStorage->Init(params.fabricIndependentStorage));

    auto delegate = chip::Platform::MakeUnique<ControllerFabricDelegate>();
    ReturnErrorOnFailure(delegate->Init(stateParams.sessionMgr, stateParams.groupDataProvider));
    ReturnErrorOnFailure(stateParams.fabricTable->AddFabricDelegate(delegate.get()));
    delegate.release();

    ReturnErrorOnFailure(stateParams.sessionMgr->Init(stateParams.systemLayer, stateParams.transportMgr,
                                                      stateParams.messageCounterManager, params.fabricIndependentStorage,
                                                      stateParams.fabricTable));
    ReturnErrorOnFailure(stateParams.exchangeMgr->Init(stateParams.sessionMgr));
    ReturnErrorOnFailure(stateParams.messageCounterManager->Init(stateParams.exchangeMgr));

    InitDataModelHandler(stateParams.exchangeMgr);

    ReturnErrorOnFailure(chip::app::InteractionModelEngine::GetInstance()->Init(stateParams.exchangeMgr, stateParams.fabricTable));

    ReturnErrorOnFailure(Dnssd::Resolver::Instance().Init(stateParams.udpEndPointManager));

    if (params.enableServerInteractions)
    {
        stateParams.caseServer = chip::Platform::New<CASEServer>();

        // Enable listening for session establishment messages.
        ReturnErrorOnFailure(stateParams.caseServer->ListenForSessionEstablishment(
            stateParams.exchangeMgr, stateParams.sessionMgr, stateParams.fabricTable, stateParams.sessionResumptionStorage,
            stateParams.groupDataProvider));

        //
        // We need to advertise the port that we're listening to for unsolicited messages over UDP. However, we have both a IPv4
        // and IPv6 endpoint to pick from. Given that the listen port passed in may be set to 0 (which then has the kernel select
        // a valid port at bind time), that will result in two possible ports being provided back from the resultant endpoint
        // initializations. Since IPv6 is POR for Matter, let's go ahead and pick that port.
        //
        app::DnssdServer::Instance().SetSecuredPort(stateParams.transportMgr->GetTransport().GetImplAtIndex<0>().GetBoundPort());

        //
        // TODO: This is a hack to workaround the fact that we have a bi-polar stack that has controller and server modalities that
        // are mutually exclusive in terms of initialization of key stack singletons. Consequently, DnssdServer accesses
        // Server::GetInstance().GetFabricTable() to access the fabric table, but we don't want to do that when we're initializing
        // the controller logic since the factory here has its own fabric table.
        //
        // Consequently, reach in set the fabric table pointer to point to the right version.
        //
        app::DnssdServer::Instance().SetFabricTable(stateParams.fabricTable);

        //
        // Start up the DNS-SD server.  We are not giving it a
        // CommissioningModeProvider, so it will not claim we are in
        // commissioning mode.
        //
        chip::app::DnssdServer::Instance().StartServer();
    }

    stateParams.operationalDevicePool = Platform::New<DeviceControllerSystemStateParams::OperationalDevicePool>();
    stateParams.caseClientPool        = Platform::New<DeviceControllerSystemStateParams::CASEClientPool>();

    DeviceProxyInitParams deviceInitParams = {
        .sessionManager           = stateParams.sessionMgr,
        .sessionResumptionStorage = stateParams.sessionResumptionStorage,
        .exchangeMgr              = stateParams.exchangeMgr,
        .fabricTable              = stateParams.fabricTable,
        .clientPool               = stateParams.caseClientPool,
        .groupDataProvider        = stateParams.groupDataProvider,
        .mrpLocalConfig           = Optional<ReliableMessageProtocolConfig>::Value(GetLocalMRPConfig()),
    };

    CASESessionManagerConfig sessionManagerConfig = {
        .sessionInitParams = deviceInitParams,
        .devicePool        = stateParams.operationalDevicePool,
    };

    // TODO: Need to be able to create a CASESessionManagerConfig here!
    stateParams.caseSessionManager = Platform::New<CASESessionManager>();
    ReturnErrorOnFailure(stateParams.caseSessionManager->Init(stateParams.systemLayer, sessionManagerConfig));

    // store the system state
    mSystemState = chip::Platform::New<DeviceControllerSystemState>(stateParams);
    mSystemState->SetTempFabricTable(tempFabricTable);
    ChipLogDetail(Controller, "System State Initialized...");
    return CHIP_NO_ERROR;
}

void DeviceControllerFactory::PopulateInitParams(ControllerInitParams & controllerParams, const SetupParams & params)
{
    controllerParams.operationalCredentialsDelegate = params.operationalCredentialsDelegate;
    controllerParams.operationalKeypair             = params.operationalKeypair;
    controllerParams.controllerNOC                  = params.controllerNOC;
    controllerParams.controllerICAC                 = params.controllerICAC;
    controllerParams.controllerRCAC                 = params.controllerRCAC;

    controllerParams.systemState        = mSystemState;
    controllerParams.controllerVendorId = params.controllerVendorId;

    controllerParams.enableServerInteractions = params.enableServerInteractions;
}

CHIP_ERROR DeviceControllerFactory::SetupController(SetupParams params, DeviceController & controller)
{
    VerifyOrReturnError(mSystemState != nullptr, CHIP_ERROR_INCORRECT_STATE);
    ReturnErrorOnFailure(InitSystemState());

    ControllerInitParams controllerParams;
    PopulateInitParams(controllerParams, params);

    CHIP_ERROR err = controller.Init(controllerParams);
    return err;
}

CHIP_ERROR DeviceControllerFactory::SetupCommissioner(SetupParams params, DeviceCommissioner & commissioner)
{
    VerifyOrReturnError(mSystemState != nullptr, CHIP_ERROR_INCORRECT_STATE);
    ReturnErrorOnFailure(InitSystemState());

    CommissionerInitParams commissionerParams;
    // PopulateInitParams works against ControllerInitParams base class of CommissionerInitParams only
    PopulateInitParams(commissionerParams, params);

    // Set commissioner-specific fields not in ControllerInitParams
    commissionerParams.pairingDelegate     = params.pairingDelegate;
    commissionerParams.defaultCommissioner = params.defaultCommissioner;

    CHIP_ERROR err = commissioner.Init(commissionerParams);
    return err;
}

CHIP_ERROR DeviceControllerFactory::ServiceEvents()
{
    VerifyOrReturnError(mSystemState != nullptr, CHIP_ERROR_INCORRECT_STATE);

#if CONFIG_DEVICE_LAYER
    ReturnErrorOnFailure(DeviceLayer::PlatformMgr().StartEventLoopTask());
#endif // CONFIG_DEVICE_LAYER

    return CHIP_NO_ERROR;
}

DeviceControllerFactory::~DeviceControllerFactory()
{
    Shutdown();
}

void DeviceControllerFactory::Shutdown()
{
    if (mSystemState != nullptr)
    {
        mSystemState->Release();
        chip::Platform::Delete(mSystemState);
        mSystemState = nullptr;
    }
    mFabricIndependentStorage = nullptr;
}

CHIP_ERROR DeviceControllerSystemState::Shutdown()
{
    VerifyOrReturnError(mRefCount == 1, CHIP_ERROR_INCORRECT_STATE);

    ChipLogDetail(Controller, "Shutting down the System State, this will teardown the CHIP Stack");

    if (mCASEServer != nullptr)
    {
        chip::Platform::Delete(mCASEServer);
        mCASEServer = nullptr;
    }

    if (mCASESessionManager != nullptr)
    {
        mCASESessionManager->Shutdown();
        Platform::Delete(mCASESessionManager);
        mCASESessionManager = nullptr;
    }

    // mCASEClientPool and mDevicePool must be deallocated
    // after mCASESessionManager, which uses them.

    if (mOperationalDevicePool != nullptr)
    {
        Platform::Delete(mOperationalDevicePool);
        mOperationalDevicePool = nullptr;
    }

    if (mCASEClientPool != nullptr)
    {
        Platform::Delete(mCASEClientPool);
        mCASEClientPool = nullptr;
    }

    Dnssd::Resolver::Instance().Shutdown();

    // Shut down the interaction model
    app::InteractionModelEngine::GetInstance()->Shutdown();

    // Shut down the TransportMgr. This holds Inet::UDPEndPoints so it must be shut down
    // before PlatformMgr().Shutdown() shuts down Inet.
    if (mTransportMgr != nullptr)
    {
        mTransportMgr->Close();
        chip::Platform::Delete(mTransportMgr);
        mTransportMgr = nullptr;
    }

#if CONFIG_DEVICE_LAYER
    //
    // We can safely call PlatformMgr().Shutdown(), which like DeviceController::Shutdown(),
    // expects to be called with external thread synchronization and will not try to acquire the
    // stack lock.
    //
    // Actually stopping the event queue is a separable call that applications will have to sequence.
    // Consumers are expected to call PlaformMgr().StopEventLoopTask() before calling
    // DeviceController::Shutdown() in the CONFIG_DEVICE_LAYER configuration
    //
    ReturnErrorOnFailure(DeviceLayer::PlatformMgr().Shutdown());
#endif

    if (mExchangeMgr != nullptr)
    {
        mExchangeMgr->Shutdown();
    }
    if (mSessionMgr != nullptr)
    {
        mSessionMgr->Shutdown();
    }

    mSystemLayer        = nullptr;
    mTCPEndPointManager = nullptr;
    mUDPEndPointManager = nullptr;
#if CONFIG_NETWORK_LAYER_BLE
    mBleLayer = nullptr;
#endif // CONFIG_NETWORK_LAYER_BLE

    if (mMessageCounterManager != nullptr)
    {
        chip::Platform::Delete(mMessageCounterManager);
        mMessageCounterManager = nullptr;
    }

    if (mExchangeMgr != nullptr)
    {
        chip::Platform::Delete(mExchangeMgr);
        mExchangeMgr = nullptr;
    }

    if (mSessionMgr != nullptr)
    {
        chip::Platform::Delete(mSessionMgr);
        mSessionMgr = nullptr;
    }

    if (mTempFabricTable != nullptr)
    {
        chip::Platform::Delete(mTempFabricTable);
        mTempFabricTable = nullptr;
    }

    return CHIP_NO_ERROR;
}

} // namespace Controller
} // namespace chip
<commit_msg>Make sure DeviceController can be Setup after Shutdown and use the expected FabricTable (#18337)<commit_after>/*
 *
 *    Copyright (c) 2021 Project CHIP Authors
 *    All rights reserved.
 *
 *    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.
 */

/**
 *    @file
 *      Implementation of CHIP Device Controller Factory, a utility/manager class
 *      that vends Controller objects
 */

#include <controller/CHIPDeviceControllerFactory.h>

#include <app/OperationalDeviceProxy.h>
#include <app/util/DataModelHandler.h>
#include <lib/support/ErrorStr.h>
#include <messaging/ReliableMessageProtocolConfig.h>

#if CONFIG_DEVICE_LAYER
#include <platform/CHIPDeviceLayer.h>
#include <platform/ConfigurationManager.h>
#endif

#include <app/server/Dnssd.h>
#include <protocols/secure_channel/CASEServer.h>
#include <protocols/secure_channel/SimpleSessionResumptionStorage.h>

using namespace chip::Inet;
using namespace chip::System;
using namespace chip::Credentials;

namespace chip {
namespace Controller {

CHIP_ERROR DeviceControllerFactory::Init(FactoryInitParams params)
{

    // SystemState is only set the first time init is called, after that it is managed
    // internally. If SystemState is set then init has already completed.
    if (mSystemState != nullptr)
    {
        ChipLogError(Controller, "Device Controller Factory already initialized...");
        return CHIP_NO_ERROR;
    }

    // Save our initialization state that we can't recover later from a
    // created-but-shut-down system state.
    mListenPort               = params.listenPort;
    mFabricIndependentStorage = params.fabricIndependentStorage;
    mEnableServerInteractions = params.enableServerInteractions;

    CHIP_ERROR err = InitSystemState(params);

    return err;
}

CHIP_ERROR DeviceControllerFactory::InitSystemState()
{
    FactoryInitParams params;
    if (mSystemState != nullptr)
    {
        params.systemLayer        = mSystemState->SystemLayer();
        params.tcpEndPointManager = mSystemState->TCPEndPointManager();
        params.udpEndPointManager = mSystemState->UDPEndPointManager();
#if CONFIG_NETWORK_LAYER_BLE
        params.bleLayer = mSystemState->BleLayer();
#endif
        params.listenPort               = mListenPort;
        params.fabricIndependentStorage = mFabricIndependentStorage;
        params.enableServerInteractions = mEnableServerInteractions;
        params.groupDataProvider        = mSystemState->GetGroupDataProvider();
        params.fabricTable              = mSystemState->Fabrics();
    }

    return InitSystemState(params);
}

CHIP_ERROR DeviceControllerFactory::InitSystemState(FactoryInitParams params)
{
    if (mSystemState != nullptr && mSystemState->IsInitialized())
    {
        return CHIP_NO_ERROR;
    }

    if (mSystemState != nullptr)
    {
        mSystemState->Release();
        chip::Platform::Delete(mSystemState);
        mSystemState = nullptr;
    }

    DeviceControllerSystemStateParams stateParams;
#if CONFIG_DEVICE_LAYER
    ReturnErrorOnFailure(DeviceLayer::PlatformMgr().InitChipStack());

    stateParams.systemLayer        = &DeviceLayer::SystemLayer();
    stateParams.tcpEndPointManager = DeviceLayer::TCPEndPointManager();
    stateParams.udpEndPointManager = DeviceLayer::UDPEndPointManager();
#else
    stateParams.systemLayer        = params.systemLayer;
    stateParams.tcpEndPointManager = params.tcpEndPointManager;
    stateParams.udpEndPointManager = params.udpEndPointManager;
    ChipLogError(Controller, "Warning: Device Controller Factory should be with a CHIP Device Layer...");
#endif // CONFIG_DEVICE_LAYER

    VerifyOrReturnError(stateParams.systemLayer != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
    VerifyOrReturnError(stateParams.udpEndPointManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT);

#if CONFIG_NETWORK_LAYER_BLE
#if CONFIG_DEVICE_LAYER
    stateParams.bleLayer = DeviceLayer::ConnectivityMgr().GetBleLayer();
#else
    stateParams.bleLayer = params.bleLayer;
#endif // CONFIG_DEVICE_LAYER
    VerifyOrReturnError(stateParams.bleLayer != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
#endif

    stateParams.transportMgr = chip::Platform::New<DeviceTransportMgr>();

    //
    // The logic below expects IPv6 to be at index 0 of this tuple. Please do not alter that.
    //
    ReturnErrorOnFailure(stateParams.transportMgr->Init(Transport::UdpListenParameters(stateParams.udpEndPointManager)
                                                            .SetAddressType(Inet::IPAddressType::kIPv6)
                                                            .SetListenPort(params.listenPort)
#if INET_CONFIG_ENABLE_IPV4
                                                            ,
                                                        Transport::UdpListenParameters(stateParams.udpEndPointManager)
                                                            .SetAddressType(Inet::IPAddressType::kIPv4)
                                                            .SetListenPort(params.listenPort)
#endif
#if CONFIG_NETWORK_LAYER_BLE
                                                            ,
                                                        Transport::BleListenParameters(stateParams.bleLayer)
#endif
                                                            ));

    stateParams.sessionMgr                                    = chip::Platform::New<SessionManager>();
    SimpleSessionResumptionStorage * sessionResumptionStorage = chip::Platform::New<SimpleSessionResumptionStorage>();
    stateParams.sessionResumptionStorage                      = sessionResumptionStorage;
    stateParams.exchangeMgr                                   = chip::Platform::New<Messaging::ExchangeManager>();
    stateParams.messageCounterManager                         = chip::Platform::New<secure_channel::MessageCounterManager>();
    stateParams.groupDataProvider                             = params.groupDataProvider;

    // if no fabricTable was provided, create one and track it in stateParams for cleanup
    FabricTable * tempFabricTable = nullptr;
    stateParams.fabricTable       = params.fabricTable;
    if (stateParams.fabricTable == nullptr)
    {
        stateParams.fabricTable = tempFabricTable = chip::Platform::New<FabricTable>();
        ReturnErrorOnFailure(stateParams.fabricTable->Init(params.fabricIndependentStorage));
    }
    ReturnErrorOnFailure(sessionResumptionStorage->Init(params.fabricIndependentStorage));

    auto delegate = chip::Platform::MakeUnique<ControllerFabricDelegate>();
    ReturnErrorOnFailure(delegate->Init(stateParams.sessionMgr, stateParams.groupDataProvider));
    ReturnErrorOnFailure(stateParams.fabricTable->AddFabricDelegate(delegate.get()));
    delegate.release();

    ReturnErrorOnFailure(stateParams.sessionMgr->Init(stateParams.systemLayer, stateParams.transportMgr,
                                                      stateParams.messageCounterManager, params.fabricIndependentStorage,
                                                      stateParams.fabricTable));
    ReturnErrorOnFailure(stateParams.exchangeMgr->Init(stateParams.sessionMgr));
    ReturnErrorOnFailure(stateParams.messageCounterManager->Init(stateParams.exchangeMgr));

    InitDataModelHandler(stateParams.exchangeMgr);

    ReturnErrorOnFailure(chip::app::InteractionModelEngine::GetInstance()->Init(stateParams.exchangeMgr, stateParams.fabricTable));

    ReturnErrorOnFailure(Dnssd::Resolver::Instance().Init(stateParams.udpEndPointManager));

    if (params.enableServerInteractions)
    {
        stateParams.caseServer = chip::Platform::New<CASEServer>();

        // Enable listening for session establishment messages.
        ReturnErrorOnFailure(stateParams.caseServer->ListenForSessionEstablishment(
            stateParams.exchangeMgr, stateParams.sessionMgr, stateParams.fabricTable, stateParams.sessionResumptionStorage,
            stateParams.groupDataProvider));

        //
        // We need to advertise the port that we're listening to for unsolicited messages over UDP. However, we have both a IPv4
        // and IPv6 endpoint to pick from. Given that the listen port passed in may be set to 0 (which then has the kernel select
        // a valid port at bind time), that will result in two possible ports being provided back from the resultant endpoint
        // initializations. Since IPv6 is POR for Matter, let's go ahead and pick that port.
        //
        app::DnssdServer::Instance().SetSecuredPort(stateParams.transportMgr->GetTransport().GetImplAtIndex<0>().GetBoundPort());

        //
        // TODO: This is a hack to workaround the fact that we have a bi-polar stack that has controller and server modalities that
        // are mutually exclusive in terms of initialization of key stack singletons. Consequently, DnssdServer accesses
        // Server::GetInstance().GetFabricTable() to access the fabric table, but we don't want to do that when we're initializing
        // the controller logic since the factory here has its own fabric table.
        //
        // Consequently, reach in set the fabric table pointer to point to the right version.
        //
        app::DnssdServer::Instance().SetFabricTable(stateParams.fabricTable);

        //
        // Start up the DNS-SD server.  We are not giving it a
        // CommissioningModeProvider, so it will not claim we are in
        // commissioning mode.
        //
        chip::app::DnssdServer::Instance().StartServer();
    }

    stateParams.operationalDevicePool = Platform::New<DeviceControllerSystemStateParams::OperationalDevicePool>();
    stateParams.caseClientPool        = Platform::New<DeviceControllerSystemStateParams::CASEClientPool>();

    DeviceProxyInitParams deviceInitParams = {
        .sessionManager           = stateParams.sessionMgr,
        .sessionResumptionStorage = stateParams.sessionResumptionStorage,
        .exchangeMgr              = stateParams.exchangeMgr,
        .fabricTable              = stateParams.fabricTable,
        .clientPool               = stateParams.caseClientPool,
        .groupDataProvider        = stateParams.groupDataProvider,
        .mrpLocalConfig           = Optional<ReliableMessageProtocolConfig>::Value(GetLocalMRPConfig()),
    };

    CASESessionManagerConfig sessionManagerConfig = {
        .sessionInitParams = deviceInitParams,
        .devicePool        = stateParams.operationalDevicePool,
    };

    // TODO: Need to be able to create a CASESessionManagerConfig here!
    stateParams.caseSessionManager = Platform::New<CASESessionManager>();
    ReturnErrorOnFailure(stateParams.caseSessionManager->Init(stateParams.systemLayer, sessionManagerConfig));

    // store the system state
    mSystemState = chip::Platform::New<DeviceControllerSystemState>(stateParams);
    mSystemState->SetTempFabricTable(tempFabricTable);
    ChipLogDetail(Controller, "System State Initialized...");
    return CHIP_NO_ERROR;
}

void DeviceControllerFactory::PopulateInitParams(ControllerInitParams & controllerParams, const SetupParams & params)
{
    controllerParams.operationalCredentialsDelegate = params.operationalCredentialsDelegate;
    controllerParams.operationalKeypair             = params.operationalKeypair;
    controllerParams.controllerNOC                  = params.controllerNOC;
    controllerParams.controllerICAC                 = params.controllerICAC;
    controllerParams.controllerRCAC                 = params.controllerRCAC;

    controllerParams.systemState        = mSystemState;
    controllerParams.controllerVendorId = params.controllerVendorId;

    controllerParams.enableServerInteractions = params.enableServerInteractions;
}

CHIP_ERROR DeviceControllerFactory::SetupController(SetupParams params, DeviceController & controller)
{
    VerifyOrReturnError(mSystemState != nullptr, CHIP_ERROR_INCORRECT_STATE);
    ReturnErrorOnFailure(InitSystemState());

    ControllerInitParams controllerParams;
    PopulateInitParams(controllerParams, params);

    CHIP_ERROR err = controller.Init(controllerParams);
    return err;
}

CHIP_ERROR DeviceControllerFactory::SetupCommissioner(SetupParams params, DeviceCommissioner & commissioner)
{
    VerifyOrReturnError(mSystemState != nullptr, CHIP_ERROR_INCORRECT_STATE);
    ReturnErrorOnFailure(InitSystemState());

    CommissionerInitParams commissionerParams;
    // PopulateInitParams works against ControllerInitParams base class of CommissionerInitParams only
    PopulateInitParams(commissionerParams, params);

    // Set commissioner-specific fields not in ControllerInitParams
    commissionerParams.pairingDelegate     = params.pairingDelegate;
    commissionerParams.defaultCommissioner = params.defaultCommissioner;

    CHIP_ERROR err = commissioner.Init(commissionerParams);
    return err;
}

CHIP_ERROR DeviceControllerFactory::ServiceEvents()
{
    VerifyOrReturnError(mSystemState != nullptr, CHIP_ERROR_INCORRECT_STATE);

#if CONFIG_DEVICE_LAYER
    ReturnErrorOnFailure(DeviceLayer::PlatformMgr().StartEventLoopTask());
#endif // CONFIG_DEVICE_LAYER

    return CHIP_NO_ERROR;
}

DeviceControllerFactory::~DeviceControllerFactory()
{
    Shutdown();
}

void DeviceControllerFactory::Shutdown()
{
    if (mSystemState != nullptr)
    {
        mSystemState->Release();
        chip::Platform::Delete(mSystemState);
        mSystemState = nullptr;
    }
    mFabricIndependentStorage = nullptr;
}

CHIP_ERROR DeviceControllerSystemState::Shutdown()
{
    VerifyOrReturnError(mRefCount == 1, CHIP_ERROR_INCORRECT_STATE);

    ChipLogDetail(Controller, "Shutting down the System State, this will teardown the CHIP Stack");

    if (mCASEServer != nullptr)
    {
        chip::Platform::Delete(mCASEServer);
        mCASEServer = nullptr;
    }

    if (mCASESessionManager != nullptr)
    {
        mCASESessionManager->Shutdown();
        Platform::Delete(mCASESessionManager);
        mCASESessionManager = nullptr;
    }

    // mCASEClientPool and mDevicePool must be deallocated
    // after mCASESessionManager, which uses them.

    if (mOperationalDevicePool != nullptr)
    {
        Platform::Delete(mOperationalDevicePool);
        mOperationalDevicePool = nullptr;
    }

    if (mCASEClientPool != nullptr)
    {
        Platform::Delete(mCASEClientPool);
        mCASEClientPool = nullptr;
    }

    Dnssd::Resolver::Instance().Shutdown();

    // Shut down the interaction model
    app::InteractionModelEngine::GetInstance()->Shutdown();

    // Shut down the TransportMgr. This holds Inet::UDPEndPoints so it must be shut down
    // before PlatformMgr().Shutdown() shuts down Inet.
    if (mTransportMgr != nullptr)
    {
        mTransportMgr->Close();
        chip::Platform::Delete(mTransportMgr);
        mTransportMgr = nullptr;
    }

#if CONFIG_DEVICE_LAYER
    //
    // We can safely call PlatformMgr().Shutdown(), which like DeviceController::Shutdown(),
    // expects to be called with external thread synchronization and will not try to acquire the
    // stack lock.
    //
    // Actually stopping the event queue is a separable call that applications will have to sequence.
    // Consumers are expected to call PlaformMgr().StopEventLoopTask() before calling
    // DeviceController::Shutdown() in the CONFIG_DEVICE_LAYER configuration
    //
    ReturnErrorOnFailure(DeviceLayer::PlatformMgr().Shutdown());
#endif

    if (mExchangeMgr != nullptr)
    {
        mExchangeMgr->Shutdown();
    }
    if (mSessionMgr != nullptr)
    {
        mSessionMgr->Shutdown();
    }

    mSystemLayer        = nullptr;
    mTCPEndPointManager = nullptr;
    mUDPEndPointManager = nullptr;
#if CONFIG_NETWORK_LAYER_BLE
    mBleLayer = nullptr;
#endif // CONFIG_NETWORK_LAYER_BLE

    if (mMessageCounterManager != nullptr)
    {
        chip::Platform::Delete(mMessageCounterManager);
        mMessageCounterManager = nullptr;
    }

    if (mExchangeMgr != nullptr)
    {
        chip::Platform::Delete(mExchangeMgr);
        mExchangeMgr = nullptr;
    }

    if (mSessionMgr != nullptr)
    {
        chip::Platform::Delete(mSessionMgr);
        mSessionMgr = nullptr;
    }

    if (mTempFabricTable != nullptr)
    {
        chip::Platform::Delete(mTempFabricTable);
        mTempFabricTable = nullptr;
        // if we created a temp fabric table, then mFabrics points to it.
        // if we did not create a temp fabric table, then keep the reference
        // so that SetupController/Commissioner can use it
        mFabrics = nullptr;
    }

    return CHIP_NO_ERROR;
}

} // namespace Controller
} // namespace chip
<|endoftext|>


<commit_before>#include <tests/Base.hh>

#include <aleph/containers/PointCloud.hh>

#include <aleph/geometry/TangentSpace.hh>

#include <iostream>

using namespace aleph;
using namespace containers;
using namespace geometry;

template <class T> void test()
{
#ifdef ALEPH_WITH_EIGEN
  using PointCloud = PointCloud<T>;

  PointCloud pc = load<T>( CMAKE_SOURCE_DIR + std::string( "/tests/input/Iris_colon_separated.txt" ) );

  TangentSpace ts;
  ts( pc );
#endif
}

int main( int, char** )
{
  test<float> ();
  test<double>();
}
<commit_msg>Extended tangent space test for circles<commit_after>#include <tests/Base.hh>

#include <aleph/containers/PointCloud.hh>

#include <aleph/geometry/TangentSpace.hh>

#include <iostream>

#include <cmath>

using namespace aleph;
using namespace containers;
using namespace geometry;

template <class T> void testCircle()
{
  auto n = 100;
  auto k = 5;

  // FIXME: use for tangent space estimation
  (void) k;

  using PointCloud = PointCloud<T>;
  PointCloud pc( n, 2 );

  for( int i = 0; i < n; i++ )
  {
    auto phi = T(2*M_PI*i/n);
    auto r   = T(1.0);
    auto x   = r * std::cos( phi );
    auto y   = r * std::sin( phi );

    pc.set(i, {x,y} );
  }

#ifdef ALEPH_WITH_EIGEN
  TangentSpace ts;
  ts( pc );
#endif
}

template <class T> void test()
{
#ifdef ALEPH_WITH_EIGEN
  using PointCloud = PointCloud<T>;

  PointCloud pc = load<T>( CMAKE_SOURCE_DIR + std::string( "/tests/input/Iris_colon_separated.txt" ) );

  TangentSpace ts;
  ts( pc );
#endif
}

int main( int, char** )
{
  testCircle<float> ();
  testCircle<double>();

  //test<float> ();
  //test<double>();
}
<|endoftext|>


<commit_before>/**
  * Touhou Community Reliant Automatic Patcher
  * Tasogare Frontier support plugin
  *
  * ----
  *
  * On-the-fly th145 TFCS patcher (*.csv)
  * Most CSV files can also be provided as TFCS files (and are provided by the game as TFCS files).
  * These CSV files aren't supported for now because the ULiL TFCS parser
  * is way more reliable than the ULiL CSV parser for these files.
  * If we add CSV support here, I think we'd better do it in the form of CSV=>TFCS conversion,
  * followed by TFCS patching.
  */

#include <thcrap.h>
#include "thcrap_tasofro.h"
#include "tfcs.h"
#include "pl.h"
#include <zlib.h>
#include <string>
#include <vector>
#include <list>
#include <algorithm>

static int inflate_bytes(BYTE* file_in, size_t size_in, BYTE* file_out, size_t size_out)
{
	int ret;
	z_stream strm;

	strm.zalloc = NULL;
	strm.zfree = NULL;
	strm.opaque = NULL;
	strm.next_in = file_in;
	strm.avail_in = size_in;
	strm.next_out = file_out;
	strm.avail_out = size_out;
	ret = inflateInit(&strm);
	if (ret != Z_OK) {
		return ret;
	}
	do {
		ret = inflate(&strm, Z_NO_FLUSH);
		if (ret != Z_OK) {
			break;
		}
	} while (ret != Z_STREAM_END);
	inflateEnd(&strm);
	return ret == Z_STREAM_END ? Z_OK : ret;
}

static int deflate_bytes(BYTE* file_in, size_t size_in, BYTE* file_out, size_t* size_out)
{
	int ret;
	z_stream strm;

	strm.zalloc = NULL;
	strm.zfree = NULL;
	strm.opaque = NULL;
	strm.next_in = file_in;
	strm.avail_in = size_in;
	strm.next_out = file_out;
	strm.avail_out = *size_out;
	ret = deflateInit(&strm, Z_BEST_COMPRESSION);
	if (ret != Z_OK) {
		return ret;
	}
	do {
		ret = deflate(&strm, Z_FINISH);
		if (ret != Z_OK) {
			break;
		}
	} while (ret != Z_STREAM_END);
	deflateEnd(&strm);
	*size_out -= strm.avail_out;
	return ret == Z_STREAM_END ? Z_OK : ret;
}

std::string patch_ruby(std::string str)
{
	const std::string ruby_begin = "{{ruby|";
	const std::string ruby_end = "}}";

	std::string::iterator pattern_begin;
	std::string::iterator pattern_end;
	while ((pattern_begin = std::search(str.begin(), str.end(), ruby_begin.begin(), ruby_begin.end())) != str.end()) {
		pattern_end = std::search(str.begin(), str.end(), ruby_end.begin(), ruby_end.end());
		if (pattern_end == str.end()) {
			break;
		}

		std::string rep = "\\R[";
		rep.append(pattern_begin + ruby_begin.length(), pattern_end);
		rep += "]";

		pattern_end += ruby_end.length();
		str.replace(pattern_begin, pattern_end, rep.begin(), rep.end());
	}

	return str;
}

void patch_line(BYTE *&in, BYTE *&out, DWORD nb_col, json_t *patch_row)
{
	// Read input
	std::vector<std::string> line;
	for (DWORD col = 0; col < nb_col; col++) {
		DWORD field_size = *(DWORD*)in;
		in += sizeof(DWORD);
		line.push_back(std::string((const char*)in, field_size));
		in += field_size;
	}

	// Patch as data/win/message/*.csv
	unsigned int start;
	if (game_id <= TH145) {
		start = 1;
	}
	else {
		start = 9;
	}
	json_t *patch_lines = json_object_get(patch_row, "lines");
	if (patch_lines && line.size() <= start + 12 && line[start + 3].empty() == false) {
		std::list<TasofroPl::ALine*> texts;
		// We want to overwrite all the balloons with the user-provided ones,
		// so we only need to put the 1st one, we can ignore the others.
		TasofroPl::AText *text = new TasofroPl::WinText(std::vector<std::string>({
			line[start + 3],
			line[start + 2]
		}));
		texts.push_back(text);
		text->patch(texts, texts.begin(), "", patch_lines);

		size_t i = 0;
		for (TasofroPl::ALine* it : texts) {
			if (i == 3) {
				log_print("TFCS: warning: trying to put more than 3 balloons in a win line.\n");
				break;
			}
			line[start + 4 * i + 3] = it->get(0);
			line[start + 4 * i + 2] = it->get(1);
			i++;
		}
	}

	// Patch each column independently
	json_t *patch_col;
	for (DWORD col = 0; col < nb_col; col++) {
		patch_col = json_object_numkey_get(patch_row, col);
		if (patch_col) {
			if (json_is_string(patch_col)) {
				line[col] = patch_ruby(json_string_value(patch_col));
			}
			else if (json_is_array(patch_col)) {
				line[col].clear();
				bool add_eol = false;
				size_t i;
				json_t *it;
				json_array_foreach(patch_col, i, it) {
					if (add_eol) {
						line[col] += "\n";
					}
					if (json_is_string(it)) {
						line[col] += patch_ruby(json_string_value(it));
						add_eol = true;
					}
				}
			}
		}
	}

	// Write output
	for (const std::string& col : line) {
		DWORD field_size = col.length();
		*(DWORD*)out = field_size;
		out += sizeof(DWORD);
		memcpy(out, col.c_str(), field_size);
		out += field_size;
	}
}

void skip_line(BYTE *&in, BYTE *&out, DWORD nb_col)
{
	for (DWORD col = 0; col < nb_col; col++) {
		DWORD field_size = *(DWORD*)in; in += sizeof(DWORD);
		*(DWORD*)out = field_size;      out += sizeof(DWORD);

		memcpy(out, in, field_size);
		in  += field_size;
		out += field_size;
	}
}

int patch_tfcs(void *file_inout, size_t size_out, size_t size_in, const char *fn, json_t *patch)
{
	if (!patch) {
		return 0;
	}

	tfcs_header_t *header;

	// Read TFCS header
	header = (tfcs_header_t*)file_inout;
	if (size_in < sizeof(header) || memcmp(header->magic, "TFCS\0", 5) != 0) {
		// Invalid TFCS file (probably a regular CSV file)
		return patch_csv(file_inout, size_out, size_in, fn, patch);
	}

	BYTE *file_in_uncomp = (BYTE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, header->uncomp_size);
	inflate_bytes(header->data, header->comp_size, file_in_uncomp, header->uncomp_size);

	// We don't have patch_size here, but it is equal to size_out - size_in.
	DWORD file_out_uncomp_size = header->uncomp_size + (size_out - size_in);
	BYTE *file_out_uncomp = (BYTE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, file_out_uncomp_size);

	BYTE *ptr_in  = file_in_uncomp;
	BYTE *ptr_out = file_out_uncomp;

	DWORD nb_row;
	DWORD nb_col;
	DWORD row;

	nb_row = *(DWORD*)ptr_in;  ptr_in  += sizeof(DWORD);
	*(DWORD*)ptr_out = nb_row; ptr_out += sizeof(DWORD);

	for (row = 0; row < nb_row; row++) {
		nb_col = *(DWORD*)ptr_in;  ptr_in  += sizeof(DWORD);
		*(DWORD*)ptr_out = nb_col; ptr_out += sizeof(DWORD);
		json_t *patch_row = json_object_numkey_get(patch, row);

		if (patch_row) {
			patch_line(ptr_in, ptr_out, nb_col, patch_row);
		}
		else {
			skip_line(ptr_in, ptr_out, nb_col);
		}
	}

	// Write result
	file_out_uncomp_size = ptr_out - file_out_uncomp;
	header->uncomp_size = file_out_uncomp_size;
	size_out -= sizeof(header) - 1;
	int ret = deflate_bytes(file_out_uncomp, file_out_uncomp_size, header->data, &size_out);
	header->comp_size = size_out;
	if (ret != Z_OK) {
		log_printf("WARNING: tasofro CSV patching: compression failed with zlib error %d\n", ret);
	}

	HeapFree(GetProcessHeap(), 0, file_in_uncomp);
	HeapFree(GetProcessHeap(), 0, file_out_uncomp);

	return 1;
}

size_t get_tfcs_size(const char*, json_t*, size_t patch_size)
{
	// Because a lot of these files are zipped, guessing their exact patched size is hard. We'll add a few more bytes.
	if (patch_size) {
		return (size_t)(patch_size * 1.2) + 2048 + 1;
	}
	else {
		return 0;
	}
}
<commit_msg>thcrap_tasofro: TFCS: fix some buffer overflow checks.<commit_after>/**
  * Touhou Community Reliant Automatic Patcher
  * Tasogare Frontier support plugin
  *
  * ----
  *
  * On-the-fly th145 TFCS patcher (*.csv)
  * Most CSV files can also be provided as TFCS files (and are provided by the game as TFCS files).
  * These CSV files aren't supported for now because the ULiL TFCS parser
  * is way more reliable than the ULiL CSV parser for these files.
  * If we add CSV support here, I think we'd better do it in the form of CSV=>TFCS conversion,
  * followed by TFCS patching.
  */

#include <thcrap.h>
#include "thcrap_tasofro.h"
#include "tfcs.h"
#include "pl.h"
#include <zlib.h>
#include <string>
#include <vector>
#include <list>
#include <algorithm>

static int inflate_bytes(BYTE* file_in, size_t size_in, BYTE* file_out, size_t size_out)
{
	int ret;
	z_stream strm;

	strm.zalloc = NULL;
	strm.zfree = NULL;
	strm.opaque = NULL;
	strm.next_in = file_in;
	strm.avail_in = size_in;
	strm.next_out = file_out;
	strm.avail_out = size_out;
	ret = inflateInit(&strm);
	if (ret != Z_OK) {
		return ret;
	}
	do {
		ret = inflate(&strm, Z_NO_FLUSH);
		if (ret != Z_OK) {
			break;
		}
	} while (ret != Z_STREAM_END);
	inflateEnd(&strm);
	return ret == Z_STREAM_END ? Z_OK : ret;
}

static int deflate_bytes(BYTE* file_in, size_t size_in, BYTE* file_out, size_t* size_out)
{
	int ret;
	z_stream strm;

	strm.zalloc = NULL;
	strm.zfree = NULL;
	strm.opaque = NULL;
	strm.next_in = file_in;
	strm.avail_in = size_in;
	strm.next_out = file_out;
	strm.avail_out = *size_out;
	ret = deflateInit(&strm, Z_BEST_COMPRESSION);
	if (ret != Z_OK) {
		return ret;
	}
	do {
		ret = deflate(&strm, Z_FINISH);
		if (ret != Z_OK) {
			break;
		}
	} while (ret != Z_STREAM_END);
	deflateEnd(&strm);
	*size_out -= strm.avail_out;
	return ret == Z_STREAM_END ? Z_OK : ret;
}

std::string patch_ruby(std::string str)
{
	const std::string ruby_begin = "{{ruby|";
	const std::string ruby_end = "}}";

	std::string::iterator pattern_begin;
	std::string::iterator pattern_end;
	while ((pattern_begin = std::search(str.begin(), str.end(), ruby_begin.begin(), ruby_begin.end())) != str.end()) {
		pattern_end = std::search(str.begin(), str.end(), ruby_end.begin(), ruby_end.end());
		if (pattern_end == str.end()) {
			break;
		}

		std::string rep = "\\R[";
		rep.append(pattern_begin + ruby_begin.length(), pattern_end);
		rep += "]";

		pattern_end += ruby_end.length();
		str.replace(pattern_begin, pattern_end, rep.begin(), rep.end());
	}

	return str;
}

void patch_line(BYTE *&in, BYTE *&out, DWORD nb_col, json_t *patch_row)
{
	// Read input
	std::vector<std::string> line;
	for (DWORD col = 0; col < nb_col; col++) {
		DWORD field_size = *(DWORD*)in;
		in += sizeof(DWORD);
		line.push_back(std::string((const char*)in, field_size));
		in += field_size;
	}

	// Patch as data/win/message/*.csv
	unsigned int start;
	if (game_id <= TH145) {
		start = 1;
	}
	else {
		start = 9;
	}
	json_t *patch_lines = json_object_get(patch_row, "lines");
	if (patch_lines && line.size() <= start + 12 && line[start + 3].empty() == false) {
		std::list<TasofroPl::ALine*> texts;
		// We want to overwrite all the balloons with the user-provided ones,
		// so we only need to put the 1st one, we can ignore the others.
		TasofroPl::AText *text = new TasofroPl::WinText(std::vector<std::string>({
			line[start + 3],
			line[start + 2]
		}));
		texts.push_back(text);
		text->patch(texts, texts.begin(), "", patch_lines);

		size_t i = 0;
		for (TasofroPl::ALine* it : texts) {
			if (i == 3) {
				log_print("TFCS: warning: trying to put more than 3 balloons in a win line.\n");
				break;
			}
			line[start + 4 * i + 3] = it->get(0);
			line[start + 4 * i + 2] = it->get(1);
			i++;
		}
	}

	// Patch each column independently
	json_t *patch_col;
	for (DWORD col = 0; col < nb_col; col++) {
		patch_col = json_object_numkey_get(patch_row, col);
		if (patch_col) {
			if (json_is_string(patch_col)) {
				line[col] = patch_ruby(json_string_value(patch_col));
			}
			else if (json_is_array(patch_col)) {
				line[col].clear();
				bool add_eol = false;
				size_t i;
				json_t *it;
				json_array_foreach(patch_col, i, it) {
					if (add_eol) {
						line[col] += "\n";
					}
					if (json_is_string(it)) {
						line[col] += patch_ruby(json_string_value(it));
						add_eol = true;
					}
				}
			}
		}
	}

	// Write output
	for (const std::string& col : line) {
		DWORD field_size = col.length();
		*(DWORD*)out = field_size;
		out += sizeof(DWORD);
		memcpy(out, col.c_str(), field_size);
		out += field_size;
	}
}

void skip_line(BYTE *&in, BYTE *&out, DWORD nb_col)
{
	for (DWORD col = 0; col < nb_col; col++) {
		DWORD field_size = *(DWORD*)in; in += sizeof(DWORD);
		*(DWORD*)out = field_size;      out += sizeof(DWORD);

		memcpy(out, in, field_size);
		in  += field_size;
		out += field_size;
	}
}

int patch_tfcs(void *file_inout, size_t size_out, size_t size_in, const char *fn, json_t *patch)
{
	if (!patch) {
		return 0;
	}

	tfcs_header_t *header;

	// Read TFCS header
	header = (tfcs_header_t*)file_inout;
	if (size_in < sizeof(*header) || memcmp(header->magic, "TFCS\0", 5) != 0) {
		// Invalid TFCS file (probably a regular CSV file)
		return patch_csv(file_inout, size_out, size_in, fn, patch);
	}

	BYTE *file_in_uncomp = (BYTE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, header->uncomp_size);
	inflate_bytes(header->data, header->comp_size, file_in_uncomp, header->uncomp_size);

	// We don't have patch_size here, but it is equal to size_out - size_in.
	DWORD file_out_uncomp_size = header->uncomp_size + (size_out - size_in);
	BYTE *file_out_uncomp = (BYTE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, file_out_uncomp_size);

	BYTE *ptr_in  = file_in_uncomp;
	BYTE *ptr_out = file_out_uncomp;

	DWORD nb_row;
	DWORD nb_col;
	DWORD row;

	nb_row = *(DWORD*)ptr_in;  ptr_in  += sizeof(DWORD);
	*(DWORD*)ptr_out = nb_row; ptr_out += sizeof(DWORD);

	for (row = 0; row < nb_row; row++) {
		nb_col = *(DWORD*)ptr_in;  ptr_in  += sizeof(DWORD);
		*(DWORD*)ptr_out = nb_col; ptr_out += sizeof(DWORD);
		json_t *patch_row = json_object_numkey_get(patch, row);

		if (patch_row) {
			patch_line(ptr_in, ptr_out, nb_col, patch_row);
		}
		else {
			skip_line(ptr_in, ptr_out, nb_col);
		}
	}

	// Write result
	file_out_uncomp_size = ptr_out - file_out_uncomp;
	header->uncomp_size = file_out_uncomp_size;
	size_out -= sizeof(*header) - 1;
	int ret = deflate_bytes(file_out_uncomp, file_out_uncomp_size, header->data, &size_out);
	header->comp_size = size_out;
	if (ret != Z_OK) {
		log_printf("WARNING: tasofro CSV patching: compression failed with zlib error %d\n", ret);
	}

	HeapFree(GetProcessHeap(), 0, file_in_uncomp);
	HeapFree(GetProcessHeap(), 0, file_out_uncomp);

	return 1;
}

size_t get_tfcs_size(const char*, json_t*, size_t patch_size)
{
	// Because a lot of these files are zipped, guessing their exact patched size is hard. We'll add a few more bytes.
	if (patch_size) {
		return (size_t)(patch_size * 1.2) + 2048 + 1;
	}
	else {
		return 0;
	}
}
<|endoftext|>


<commit_before>/*************************************************************************
 *   Copyright (C) 2011-2014 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of Teapotnet.                                     *
 *                                                                       *
 *   Teapotnet is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   Teapotnet is distributed in the hope that it will be useful, but    *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
 *   GNU Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with Teapotnet.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "tpn/store.h"
#include "tpn/config.h"
#include "tpn/network.h"
#include "tpn/cache.h"

#include "pla/directory.h"
#include "pla/crypto.h"

namespace tpn
{

Store *Store::Instance = NULL;

BinaryString Store::Hash(const String &str)
{
	return Sha256().compute(str);
}

Store::Store(void)
{
	mDatabase = new Database("store.db");

	mDatabase->execute("CREATE TABLE IF NOT EXISTS blocks\
		(id INTEGER PRIMARY KEY AUTOINCREMENT,\
		digest BLOB,\
		file_id INTEGER,\
		offset INTEGER(8),\
		size INTEGER(8))");
	mDatabase->execute("CREATE INDEX IF NOT EXISTS digest ON blocks (digest)");
	mDatabase->execute("CREATE UNIQUE INDEX IF NOT EXISTS location ON blocks (file_id, offset)");

	mDatabase->execute("CREATE TABLE IF NOT EXISTS files\
		(id INTEGER PRIMARY KEY AUTOINCREMENT,\
		name TEXT UNIQUE)");
	mDatabase->execute("CREATE INDEX IF NOT EXISTS name ON files (name)");

	mDatabase->execute("CREATE TABLE IF NOT EXISTS map\
		(key BLOB,\
		value BLOB,\
		time INTEGER(8),\
		type INTEGER(1))");
	mDatabase->execute("CREATE UNIQUE INDEX IF NOT EXISTS pair ON map (key, value)");

	// Store is scheduled by Overlay on first connection
	const double period = 3600.;	// 1h
	Scheduler::Global->repeat(this, period);
}

Store::~Store(void)
{

}

bool Store::push(const BinaryString &digest, Fountain::Combination &input)
{
	Synchronize(this);

	if(hasBlock(digest)) return true;

	Fountain::Sink &sink = mSinks[digest];
	sink.solve(input);
	if(!sink.isDecoded()) return false;

	BinaryString sinkDigest;
	sink.hash(sinkDigest);

	LogDebug("Store::push", "Block is complete, digest is " + sinkDigest.toString());

	if(sinkDigest != digest)
	{
		LogWarn("Store::push", "Block digest is invalid (expected " + digest.toString() + ")");
		mSinks.erase(digest);
		return false;
	}

	String path = Cache::Instance->path(digest);

	File file(path, File::Write);
	int64_t size = sink.dump(file);
	file.close();

	notifyBlock(digest, path, 0, size);
	mSinks.erase(digest);
	return true;
}

bool Store::pull(const BinaryString &digest, Fountain::Combination &output, unsigned *tokens)
{
	Synchronize(this);

	int64_t size;
	File *file = getBlock(digest, size);
	if(!file) return false;

	Fountain::FileSource source(file, file->tellRead(), size);
	source.generate(output, tokens);
	return true;
}

bool Store::hasBlock(const BinaryString &digest)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT 1 FROM blocks WHERE digest = ?1");
	statement.bind(1, digest);
	if(statement.step())
	{
		statement.finalize();
		return true;
	}

	statement.finalize();
	return false;
}

void Store::waitBlock(const BinaryString &digest)
{
	if(!waitBlock(digest, 60.))	// TODO
		throw Timeout();
}

bool Store::waitBlock(const BinaryString &digest, double &timeout)
{
	Synchronize(this);

	if(!hasBlock(digest))
	{
		Desynchronize(this);
		Network::Caller caller(digest);		// Block is missing locally, call it

		LogDebug("Store::waitBlock", "Waiting for block: " + digest.toString());

		{
			Synchronize(this);

			while(!hasBlock(digest))
				if(!wait(timeout))
					return false;
		}

		LogDebug("Store::waitBlock", "Block is now available: " + digest.toString());
	}

	return true;
}

bool Store::waitBlock(const BinaryString &digest, const double &timeout)
{
	double dummy = timeout;
	return waitBlock(digest, dummy);
}

File *Store::getBlock(const BinaryString &digest, int64_t &size)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT f.name, b.offset, b.size FROM blocks b LEFT JOIN files f ON f.id = b.file_id WHERE b.digest = ?1 LIMIT 1");
	statement.bind(1, digest);
	if(statement.step())
	{
		String filename;
		int64_t offset;
		statement.value(0, filename);
		statement.value(1, offset);
		statement.value(2, size);
		statement.finalize();

		try {
			File *file = new File(filename);
			file->seekRead(offset);
			return file;
		}
		catch(...)
		{
			notifyFileErasure(filename);
		}

		return NULL;
	}

	statement.finalize();
	return NULL;
}

void Store::notifyBlock(const BinaryString &digest, const String &filename, int64_t offset, int64_t size)
{
	Synchronize(this);

	//LogDebug("Store::notifyBlock", "Block notified: " + digest.toString());

	Database::Statement statement = mDatabase->prepare("INSERT OR IGNORE INTO files (name) VALUES (?1)");
	statement.bind(1, filename);
	statement.execute();

	statement = mDatabase->prepare("INSERT OR REPLACE INTO blocks (file_id, digest, offset, size) VALUES ((SELECT id FROM files WHERE name = ?1 LIMIT 1), ?2, ?3, ?4)");
	statement.bind(1, filename);
	statement.bind(2, digest);
	statement.bind(3, offset);
	statement.bind(4, size);
	statement.execute();

	notifyAll();

	// Publish into DHT
	DesynchronizeStatement(this, Network::Instance->storeValue(digest, Network::Instance->overlay()->localNode()));
}

void Store::notifyFileErasure(const String &filename)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("DELETE FROM blocks WHERE file_id = (SELECT id FROM files WHERE name = ?1)");
	statement.bind(1, filename);
	statement.execute();

	statement = mDatabase->prepare("DELETE FROM files WHERE name = ?1");
	statement.bind(1, filename);
	statement.execute();
}

void Store::storeValue(const BinaryString &key, const BinaryString &value, Store::ValueType type)
{
	Synchronize(this);

	if(type == Permanent)
	{
		Database::Statement statement = mDatabase->prepare("DELETE FROM map WHERE key = ?1 AND type = ?2");
		statement.bind(1, key);
		statement.bind(2, static_cast<int>(Permanent));
		statement.execute();
	}

	Database::Statement statement = mDatabase->prepare("INSERT OR REPLACE INTO map (key, value, time, type) VALUES (?1, ?2, ?3, ?4)");
	statement.bind(1, key);
	statement.bind(2, value);
	statement.bind(3, Time::Now());
	statement.bind(4, static_cast<int>(type));
	statement.execute();
}

bool Store::retrieveValue(const BinaryString &key, Set<BinaryString> &values)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT value FROM map WHERE key = ?1");
	statement.bind(1, key);
	while(statement.step())
	{
		BinaryString v;
		statement.value(0, v);
		values.insert(v);
	}
	statement.finalize();

	// Also look for digest in blocks in case map is not up-to-date
	statement = mDatabase->prepare("SELECT 1 FROM blocks WHERE digest = ?1");
	statement.bind(1, key);
	if(statement.step())
		DesynchronizeStatement(this, values.insert(Network::Instance->overlay()->localNode()));
	statement.finalize();

	return !values.empty();
}

bool Store::hasValue(const BinaryString &key, const BinaryString &value) const
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT 1 FROM map WHERE key = ?1 AND value = ?2");
	statement.bind(1, key);
	statement.bind(2, value);

	bool found = statement.step();
	statement.finalize();
	return found;
}

void Store::run(void)
{
	Synchronize(this);

	const double maxAge = Config::Get("store_max_age").toDouble();
	const double delay = 0.1;	// TODO
	const int batch = 10;		// TODO

	BinaryString node;
	DesynchronizeStatement(this, node = Network::Instance->overlay()->localNode());

	LogDebug("Store::run", "Started");

	// Delete old non-permanent values
	Database::Statement statement = mDatabase->prepare("DELETE FROM map WHERE type != ?1 AND time < ?2");
	statement.bind(1, static_cast<int>(Permanent));
	statement.bind(2, Time::Now() - maxAge);
	statement.execute();

	try {
		// Publish everything into DHT periodically
		int offset = 0;
		while(true)
		{
			int n;
			DesynchronizeStatement(this, n = Network::Instance->overlay()->connectionsCount());
			if(n == 0)
			{
				LogDebug("Store::run", "Interrupted");
				return;
			}

			// Select DHT values
			Database::Statement statement = mDatabase->prepare("SELECT digest FROM blocks WHERE digest IS NOT NULL ORDER BY digest LIMIT ?1 OFFSET ?2");
			statement.bind(1, batch);
			statement.bind(2, offset);

			List<BinaryString> result;
			statement.fetchColumn(0, result);
			statement.finalize();

			if(result.empty()) break;
			else {
				Desynchronize(this);

				for(List<BinaryString>::iterator it = result.begin(); it != result.end(); ++it)
					Network::Instance->storeValue(*it, node);

				Thread::Sleep(delay);
			}

			offset+= result.size();
		}

		LogDebug("Store::run", "Finished, " + String::number(offset) + " values published");
	}
	catch(const std::exception &e)
	{
		LogWarn("Store::run", e.what());
	}
}

}
<commit_msg>Order blocks by time in Store::run()<commit_after>/*************************************************************************
 *   Copyright (C) 2011-2014 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of Teapotnet.                                     *
 *                                                                       *
 *   Teapotnet is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   Teapotnet is distributed in the hope that it will be useful, but    *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
 *   GNU Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with Teapotnet.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "tpn/store.h"
#include "tpn/config.h"
#include "tpn/network.h"
#include "tpn/cache.h"

#include "pla/directory.h"
#include "pla/crypto.h"

namespace tpn
{

Store *Store::Instance = NULL;

BinaryString Store::Hash(const String &str)
{
	return Sha256().compute(str);
}

Store::Store(void)
{
	mDatabase = new Database("store.db");

	mDatabase->execute("CREATE TABLE IF NOT EXISTS blocks\
		(id INTEGER PRIMARY KEY AUTOINCREMENT,\
		digest BLOB,\
		file_id INTEGER,\
		offset INTEGER(8),\
		size INTEGER(8))");
	mDatabase->execute("CREATE INDEX IF NOT EXISTS digest ON blocks (digest)");
	mDatabase->execute("CREATE UNIQUE INDEX IF NOT EXISTS location ON blocks (file_id, offset)");

	mDatabase->execute("CREATE TABLE IF NOT EXISTS files\
		(id INTEGER PRIMARY KEY AUTOINCREMENT,\
		name TEXT UNIQUE)");
	mDatabase->execute("CREATE INDEX IF NOT EXISTS name ON files (name)");

	mDatabase->execute("CREATE TABLE IF NOT EXISTS map\
		(key BLOB,\
		value BLOB,\
		time INTEGER(8),\
		type INTEGER(1))");
	mDatabase->execute("CREATE UNIQUE INDEX IF NOT EXISTS pair ON map (key, value)");

	// Store is scheduled by Overlay on first connection
	const double period = 3600.;	// 1h
	Scheduler::Global->repeat(this, period);
}

Store::~Store(void)
{

}

bool Store::push(const BinaryString &digest, Fountain::Combination &input)
{
	Synchronize(this);

	if(hasBlock(digest)) return true;

	Fountain::Sink &sink = mSinks[digest];
	sink.solve(input);
	if(!sink.isDecoded()) return false;

	BinaryString sinkDigest;
	sink.hash(sinkDigest);

	LogDebug("Store::push", "Block is complete, digest is " + sinkDigest.toString());

	if(sinkDigest != digest)
	{
		LogWarn("Store::push", "Block digest is invalid (expected " + digest.toString() + ")");
		mSinks.erase(digest);
		return false;
	}

	String path = Cache::Instance->path(digest);

	File file(path, File::Write);
	int64_t size = sink.dump(file);
	file.close();

	notifyBlock(digest, path, 0, size);
	mSinks.erase(digest);
	return true;
}

bool Store::pull(const BinaryString &digest, Fountain::Combination &output, unsigned *tokens)
{
	Synchronize(this);

	int64_t size;
	File *file = getBlock(digest, size);
	if(!file) return false;

	Fountain::FileSource source(file, file->tellRead(), size);
	source.generate(output, tokens);
	return true;
}

bool Store::hasBlock(const BinaryString &digest)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT 1 FROM blocks WHERE digest = ?1");
	statement.bind(1, digest);
	if(statement.step())
	{
		statement.finalize();
		return true;
	}

	statement.finalize();
	return false;
}

void Store::waitBlock(const BinaryString &digest)
{
	if(!waitBlock(digest, 60.))	// TODO
		throw Timeout();
}

bool Store::waitBlock(const BinaryString &digest, double &timeout)
{
	Synchronize(this);

	if(!hasBlock(digest))
	{
		Desynchronize(this);
		Network::Caller caller(digest);		// Block is missing locally, call it

		LogDebug("Store::waitBlock", "Waiting for block: " + digest.toString());

		{
			Synchronize(this);

			while(!hasBlock(digest))
				if(!wait(timeout))
					return false;
		}

		LogDebug("Store::waitBlock", "Block is now available: " + digest.toString());
	}

	return true;
}

bool Store::waitBlock(const BinaryString &digest, const double &timeout)
{
	double dummy = timeout;
	return waitBlock(digest, dummy);
}

File *Store::getBlock(const BinaryString &digest, int64_t &size)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT f.name, b.offset, b.size FROM blocks b LEFT JOIN files f ON f.id = b.file_id WHERE b.digest = ?1 LIMIT 1");
	statement.bind(1, digest);
	if(statement.step())
	{
		String filename;
		int64_t offset;
		statement.value(0, filename);
		statement.value(1, offset);
		statement.value(2, size);
		statement.finalize();

		try {
			File *file = new File(filename);
			file->seekRead(offset);
			return file;
		}
		catch(...)
		{
			notifyFileErasure(filename);
		}

		return NULL;
	}

	statement.finalize();
	return NULL;
}

void Store::notifyBlock(const BinaryString &digest, const String &filename, int64_t offset, int64_t size)
{
	Synchronize(this);

	//LogDebug("Store::notifyBlock", "Block notified: " + digest.toString());

	Database::Statement statement = mDatabase->prepare("INSERT OR IGNORE INTO files (name) VALUES (?1)");
	statement.bind(1, filename);
	statement.execute();

	statement = mDatabase->prepare("INSERT OR REPLACE INTO blocks (file_id, digest, offset, size) VALUES ((SELECT id FROM files WHERE name = ?1 LIMIT 1), ?2, ?3, ?4)");
	statement.bind(1, filename);
	statement.bind(2, digest);
	statement.bind(3, offset);
	statement.bind(4, size);
	statement.execute();

	notifyAll();

	// Publish into DHT
	DesynchronizeStatement(this, Network::Instance->storeValue(digest, Network::Instance->overlay()->localNode()));
}

void Store::notifyFileErasure(const String &filename)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("DELETE FROM blocks WHERE file_id = (SELECT id FROM files WHERE name = ?1)");
	statement.bind(1, filename);
	statement.execute();

	statement = mDatabase->prepare("DELETE FROM files WHERE name = ?1");
	statement.bind(1, filename);
	statement.execute();
}

void Store::storeValue(const BinaryString &key, const BinaryString &value, Store::ValueType type)
{
	Synchronize(this);

	if(type == Permanent)
	{
		Database::Statement statement = mDatabase->prepare("DELETE FROM map WHERE key = ?1 AND type = ?2");
		statement.bind(1, key);
		statement.bind(2, static_cast<int>(Permanent));
		statement.execute();
	}

	Database::Statement statement = mDatabase->prepare("INSERT OR REPLACE INTO map (key, value, time, type) VALUES (?1, ?2, ?3, ?4)");
	statement.bind(1, key);
	statement.bind(2, value);
	statement.bind(3, Time::Now());
	statement.bind(4, static_cast<int>(type));
	statement.execute();
}

bool Store::retrieveValue(const BinaryString &key, Set<BinaryString> &values)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT value FROM map WHERE key = ?1");
	statement.bind(1, key);
	while(statement.step())
	{
		BinaryString v;
		statement.value(0, v);
		values.insert(v);
	}
	statement.finalize();

	// Also look for digest in blocks in case map is not up-to-date
	statement = mDatabase->prepare("SELECT 1 FROM blocks WHERE digest = ?1");
	statement.bind(1, key);
	if(statement.step())
		DesynchronizeStatement(this, values.insert(Network::Instance->overlay()->localNode()));
	statement.finalize();

	return !values.empty();
}

bool Store::hasValue(const BinaryString &key, const BinaryString &value) const
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT 1 FROM map WHERE key = ?1 AND value = ?2");
	statement.bind(1, key);
	statement.bind(2, value);

	bool found = statement.step();
	statement.finalize();
	return found;
}

void Store::run(void)
{
	Synchronize(this);

	const double maxAge = Config::Get("store_max_age").toDouble();
	const double delay = 0.1;	// TODO
	const int batch = 10;		// TODO

	BinaryString node;
	DesynchronizeStatement(this, node = Network::Instance->overlay()->localNode());

	LogDebug("Store::run", "Started");

	// Delete old non-permanent values
	Database::Statement statement = mDatabase->prepare("DELETE FROM map WHERE type != ?1 AND time < ?2");
	statement.bind(1, static_cast<int>(Permanent));
	statement.bind(2, Time::Now() - maxAge);
	statement.execute();

	try {
		// Publish everything into DHT periodically
		int offset = 0;
		while(true)
		{
			int n;
			DesynchronizeStatement(this, n = Network::Instance->overlay()->connectionsCount());
			if(n == 0)
			{
				LogDebug("Store::run", "Interrupted");
				return;
			}

			// Select DHT values
			Database::Statement statement = mDatabase->prepare("SELECT digest FROM blocks WHERE digest IS NOT NULL ORDER BY time DESC LIMIT ?1 OFFSET ?2");
			statement.bind(1, batch);
			statement.bind(2, offset);

			List<BinaryString> result;
			statement.fetchColumn(0, result);
			statement.finalize();

			if(result.empty()) break;
			else {
				Desynchronize(this);

				for(List<BinaryString>::iterator it = result.begin(); it != result.end(); ++it)
					Network::Instance->storeValue(*it, node);

				Thread::Sleep(delay);
			}

			offset+= result.size();
		}

		LogDebug("Store::run", "Finished, " + String::number(offset) + " values published");
	}
	catch(const std::exception &e)
	{
		LogWarn("Store::run", e.what());
	}
}

}
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "config-handler.h"
#include <vespa/config/common/exceptions.h>
#include <vespa/vespalib/util/signalhandler.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/defaults.h>
#include <chrono>
#include <string>
#include <unistd.h>
#include <sys/time.h>

#include <vespa/log/log.h>
LOG_SETUP("config-sentinel");

using namespace config;

constexpr std::chrono::milliseconds CONFIG_TIMEOUT_MS(3 * 60 * 1000);

static bool stop()
{
    return (vespalib::SignalHandler::INT.check() ||
            vespalib::SignalHandler::TERM.check());
}

int
main(int argc, char **argv)
{
    int c = getopt(argc, argv, "c:");
    if (c != 'c') {
        LOG(error, "Usage: %s -c <config-id>", argv[0]);
        EV_STOPPING("config-sentinel", "Bad arguments on command line");
        return EXIT_FAILURE;
    }

    std::string configId(optarg);

    const char *rootDir = getenv("ROOT");
    if (!rootDir) {
        rootDir = vespa::Defaults::vespaHome();
        LOG(warning, "ROOT is not set, using %s", rootDir);
        setenv("ROOT", rootDir, 1);
    }

    if (chdir(rootDir) == -1) {
        LOG(error, "Fatal: Cannot cd to $ROOT (%s)", rootDir);
        EV_STOPPING("config-sentinel", "Cannot cd to $ROOT");
        return EXIT_FAILURE;
    }

    EV_STARTED("config-sentinel");

    vespalib::SignalHandler::PIPE.ignore();
    vespalib::SignalHandler::TERM.hook();
    vespalib::SignalHandler::INT.hook();
    vespalib::SignalHandler::CHLD.hook();

    if (setenv("LC_ALL", "C", 1) != 0) {
        LOG(error, "Unable to set locale");
        return EXIT_FAILURE;
    }
    setlocale(LC_ALL, "C");

    sentinel::ConfigHandler handler;

    LOG(debug, "Reading configuration");
    try {
        handler.subscribe(configId, CONFIG_TIMEOUT_MS);
    } catch (ConfigTimeoutException & ex) {
        LOG(warning, "Timeout getting config, please check your setup. Will exit and restart: %s", ex.getMessage().c_str());
        EV_STOPPING("config-sentinel", ex.what());
        return EXIT_FAILURE;
    } catch (InvalidConfigException& ex) {
        LOG(error, "Fatal: Invalid configuration, please check your setup: %s", ex.getMessage().c_str());
        EV_STOPPING("config-sentinel", ex.what());
        return EXIT_FAILURE;
    } catch (ConfigRuntimeException& ex) {
        LOG(error, "Fatal: Could not get config, please check your setup: %s", ex.getMessage().c_str());
        EV_STOPPING("config-sentinel", ex.what());
        return EXIT_FAILURE;
    }

    struct timeval lastTv;
    gettimeofday(&lastTv, nullptr);
    while (!stop()) {
        try {
            vespalib::SignalHandler::CHLD.clear();
            handler.doWork();       // Check for child procs & commands
        } catch (InvalidConfigException& ex) {
            LOG(warning, "Configuration problem: (ignoring): %s", ex.what());
        } catch (vespalib::PortListenException& ex) {
            LOG(error, "Fatal: %s", ex.getMessage().c_str());
            EV_STOPPING("config-sentinel", ex.what());
            return EXIT_FAILURE;
        } catch (vespalib::FatalException& ex) {
            LOG(error, "Fatal: %s", ex.getMessage().c_str());
            EV_STOPPING("config-sentinel", ex.what());
            return EXIT_FAILURE;
        }
        if (vespalib::SignalHandler::CHLD.check()) {
            continue;
        }
        int maxNum = 0;
        fd_set fds;
        FD_ZERO(&fds);
        handler.updateActiveFdset(&fds, &maxNum);

        struct timeval tv;
        tv.tv_sec = 0;
        tv.tv_usec = 100000;  //0.1s

        select(maxNum, &fds, nullptr, nullptr, &tv);

        gettimeofday(&tv, nullptr);
        double delta = tv.tv_sec - lastTv.tv_sec
                       + 1e-6 * (tv.tv_usec - lastTv.tv_usec);
        if (delta < 0.01) {
            usleep(12500); // Avoid busy looping;
        }
        lastTv = tv;
    }

    EV_STOPPING("config-sentinel", "normal exit");
    int rv = handler.terminate();
    return rv;
}
<commit_msg>Use simpler c++11 time constructions.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "config-handler.h"
#include <vespa/config/common/exceptions.h>
#include <vespa/vespalib/util/signalhandler.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/defaults.h>
#include <chrono>
#include <string>
#include <unistd.h>
#include <sys/time.h>

#include <vespa/log/log.h>
LOG_SETUP("config-sentinel");

using namespace config;

constexpr std::chrono::milliseconds CONFIG_TIMEOUT_MS(3 * 60 * 1000);

static bool stop()
{
    return (vespalib::SignalHandler::INT.check() ||
            vespalib::SignalHandler::TERM.check());
}

int
main(int argc, char **argv)
{
    int c = getopt(argc, argv, "c:");
    if (c != 'c') {
        LOG(error, "Usage: %s -c <config-id>", argv[0]);
        EV_STOPPING("config-sentinel", "Bad arguments on command line");
        return EXIT_FAILURE;
    }

    std::string configId(optarg);

    const char *rootDir = getenv("ROOT");
    if (!rootDir) {
        rootDir = vespa::Defaults::vespaHome();
        LOG(warning, "ROOT is not set, using %s", rootDir);
        setenv("ROOT", rootDir, 1);
    }

    if (chdir(rootDir) == -1) {
        LOG(error, "Fatal: Cannot cd to $ROOT (%s)", rootDir);
        EV_STOPPING("config-sentinel", "Cannot cd to $ROOT");
        return EXIT_FAILURE;
    }

    EV_STARTED("config-sentinel");

    vespalib::SignalHandler::PIPE.ignore();
    vespalib::SignalHandler::TERM.hook();
    vespalib::SignalHandler::INT.hook();
    vespalib::SignalHandler::CHLD.hook();

    if (setenv("LC_ALL", "C", 1) != 0) {
        LOG(error, "Unable to set locale");
        return EXIT_FAILURE;
    }
    setlocale(LC_ALL, "C");

    sentinel::ConfigHandler handler;

    LOG(debug, "Reading configuration");
    try {
        handler.subscribe(configId, CONFIG_TIMEOUT_MS);
    } catch (ConfigTimeoutException & ex) {
        LOG(warning, "Timeout getting config, please check your setup. Will exit and restart: %s", ex.getMessage().c_str());
        EV_STOPPING("config-sentinel", ex.what());
        return EXIT_FAILURE;
    } catch (InvalidConfigException& ex) {
        LOG(error, "Fatal: Invalid configuration, please check your setup: %s", ex.getMessage().c_str());
        EV_STOPPING("config-sentinel", ex.what());
        return EXIT_FAILURE;
    } catch (ConfigRuntimeException& ex) {
        LOG(error, "Fatal: Could not get config, please check your setup: %s", ex.getMessage().c_str());
        EV_STOPPING("config-sentinel", ex.what());
        return EXIT_FAILURE;
    }

    vespalib::steady_time lastTime = vespalib::steady_clock::now();
    while (!stop()) {
        try {
            vespalib::SignalHandler::CHLD.clear();
            handler.doWork();       // Check for child procs & commands
        } catch (InvalidConfigException& ex) {
            LOG(warning, "Configuration problem: (ignoring): %s", ex.what());
        } catch (vespalib::PortListenException& ex) {
            LOG(error, "Fatal: %s", ex.getMessage().c_str());
            EV_STOPPING("config-sentinel", ex.what());
            return EXIT_FAILURE;
        } catch (vespalib::FatalException& ex) {
            LOG(error, "Fatal: %s", ex.getMessage().c_str());
            EV_STOPPING("config-sentinel", ex.what());
            return EXIT_FAILURE;
        }
        if (vespalib::SignalHandler::CHLD.check()) {
            continue;
        }
        int maxNum = 0;
        fd_set fds;
        FD_ZERO(&fds);
        handler.updateActiveFdset(&fds, &maxNum);

        struct timeval tv;
        tv.tv_sec = 0;
        tv.tv_usec = 100000;  //0.1s

        select(maxNum, &fds, nullptr, nullptr, &tv);

        vespalib::steady_time now = vespalib::steady_clock::now();
        if ((now - lastTime) < 10ms) {
            std::this_thread::sleep_for(12ms); // Avoid busy looping;
        }
        lastTime = now;
    }

    EV_STOPPING("config-sentinel", "normal exit");
    int rv = handler.terminate();
    return rv;
}
<|endoftext|>