<commit_before>// Copyright (c) 2018 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "bls.h"

#include "hash.h"
#include "random.h"
#include "tinyformat.h"

#ifndef BUILD_BITCOIN_INTERNAL
#include "support/allocators/secure.h"
#include <boost/pool/pool_alloc.hpp>
#endif

#include <assert.h>
#include <string.h>

bool CBLSId::InternalSetBuf(const void* buf)
{
    memcpy(impl.begin(), buf, sizeof(uint256));
    return true;
}

bool CBLSId::InternalGetBuf(void* buf) const
{
    memcpy(buf, impl.begin(), sizeof(uint256));
    return true;
}

void CBLSId::SetInt(int x)
{
    impl.SetHex(strprintf("%x", x));
    fValid = true;
    UpdateHash();
}

void CBLSId::SetHash(const uint256& hash)
{
    impl = hash;
    fValid = true;
    UpdateHash();
}

CBLSId CBLSId::FromInt(int64_t i)
{
    CBLSId id;
    id.SetInt(i);
    return id;
}

CBLSId CBLSId::FromHash(const uint256& hash)
{
    CBLSId id;
    id.SetHash(hash);
    return id;
}

bool CBLSSecretKey::InternalSetBuf(const void* buf)
{
    try {
        impl = bls::PrivateKey::FromBytes((const uint8_t*)buf);
        return true;
    } catch (...) {
        return false;
    }
}

bool CBLSSecretKey::InternalGetBuf(void* buf) const
{
    impl.Serialize((uint8_t*)buf);
    return true;
}

void CBLSSecretKey::AggregateInsecure(const CBLSSecretKey& o)
{
    assert(IsValid() && o.IsValid());
    impl = bls::PrivateKey::AggregateInsecure({impl, o.impl});
    UpdateHash();
}

CBLSSecretKey CBLSSecretKey::AggregateInsecure(const std::vector<CBLSSecretKey>& sks)
{
    if (sks.empty()) {
        return CBLSSecretKey();
    }

    std::vector<bls::PrivateKey> v;
    v.reserve(sks.size());
    for (auto& sk : sks) {
        v.emplace_back(sk.impl);
    }

    auto agg = bls::PrivateKey::AggregateInsecure(v);
    CBLSSecretKey ret;
    ret.impl = agg;
    ret.fValid = true;
    ret.UpdateHash();
    return ret;
}

#ifndef BUILD_BITCOIN_INTERNAL
void CBLSSecretKey::MakeNewKey()
{
    unsigned char buf[32];
    while (true) {
        GetStrongRandBytes(buf, sizeof(buf));
        try {
            impl = bls::PrivateKey::FromBytes((const uint8_t*)buf);
            break;
        } catch (...) {
        }
    }
    fValid = true;
    UpdateHash();
}
#endif

bool CBLSSecretKey::SecretKeyShare(const std::vector<CBLSSecretKey>& msk, const CBLSId& _id)
{
    fValid = false;
    UpdateHash();

    if (!_id.IsValid()) {
        return false;
    }

    std::vector<bls::PrivateKey> mskVec;
    mskVec.reserve(msk.size());
    for (const CBLSSecretKey& sk : msk) {
        if (!sk.IsValid()) {
            return false;
        }
        mskVec.emplace_back(sk.impl);
    }

    try {
        impl = bls::BLS::PrivateKeyShare(mskVec, (const uint8_t*)_id.impl.begin());
    } catch (...) {
        return false;
    }

    fValid = true;
    UpdateHash();
    return true;
}

CBLSPublicKey CBLSSecretKey::GetPublicKey() const
{
    if (!IsValid()) {
        return CBLSPublicKey();
    }

    CBLSPublicKey pubKey;
    pubKey.impl = impl.GetPublicKey();
    pubKey.fValid = true;
    pubKey.UpdateHash();
    return pubKey;
}

CBLSSignature CBLSSecretKey::Sign(const uint256& hash) const
{
    if (!IsValid()) {
        return CBLSSignature();
    }

    CBLSSignature sigRet;
    sigRet.impl = impl.SignInsecurePrehashed((const uint8_t*)hash.begin());

    sigRet.fValid = true;
    sigRet.UpdateHash();

    return sigRet;
}

bool CBLSPublicKey::InternalSetBuf(const void* buf)
{
    try {
        impl = bls::PublicKey::FromBytes((const uint8_t*)buf);
        return true;
    } catch (...) {
        return false;
    }
}

bool CBLSPublicKey::InternalGetBuf(void* buf) const
{
    impl.Serialize((uint8_t*)buf);
    return true;
}

void CBLSPublicKey::AggregateInsecure(const CBLSPublicKey& o)
{
    assert(IsValid() && o.IsValid());
    impl = bls::PublicKey::AggregateInsecure({impl, o.impl});
    UpdateHash();
}

CBLSPublicKey CBLSPublicKey::AggregateInsecure(const std::vector<CBLSPublicKey>& pks)
{
    if (pks.empty()) {
        return CBLSPublicKey();
    }

    std::vector<bls::PublicKey> v;
    v.reserve(pks.size());
    for (auto& pk : pks) {
        v.emplace_back(pk.impl);
    }

    auto agg = bls::PublicKey::AggregateInsecure(v);
    CBLSPublicKey ret;
    ret.impl = agg;
    ret.fValid = true;
    ret.UpdateHash();
    return ret;
}

bool CBLSPublicKey::PublicKeyShare(const std::vector<CBLSPublicKey>& mpk, const CBLSId& _id)
{
    fValid = false;
    UpdateHash();

    if (!_id.IsValid()) {
        return false;
    }

    std::vector<bls::PublicKey> mpkVec;
    mpkVec.reserve(mpk.size());
    for (const CBLSPublicKey& pk : mpk) {
        if (!pk.IsValid()) {
            return false;
        }
        mpkVec.emplace_back(pk.impl);
    }

    try {
        impl = bls::BLS::PublicKeyShare(mpkVec, (const uint8_t*)_id.impl.begin());
    } catch (...) {
        return false;
    }

    fValid = true;
    UpdateHash();
    return true;
}

bool CBLSPublicKey::DHKeyExchange(const CBLSSecretKey& sk, const CBLSPublicKey& pk)
{
    fValid = false;
    UpdateHash();

    if (!sk.IsValid() || !pk.IsValid()) {
        return false;
    }
    impl = bls::BLS::DHKeyExchange(sk.impl, pk.impl);
    fValid = true;
    UpdateHash();
    return true;
}

bool CBLSSignature::InternalSetBuf(const void* buf)
{
    try {
        impl = bls::InsecureSignature::FromBytes((const uint8_t*)buf);
        return true;
    } catch (...) {
        return false;
    }
}

bool CBLSSignature::InternalGetBuf(void* buf) const
{
    impl.Serialize((uint8_t*)buf);
    return true;
}

void CBLSSignature::AggregateInsecure(const CBLSSignature& o)
{
    assert(IsValid() && o.IsValid());
    impl = bls::InsecureSignature::Aggregate({impl, o.impl});
    UpdateHash();
}

CBLSSignature CBLSSignature::AggregateInsecure(const std::vector<CBLSSignature>& sigs)
{
    if (sigs.empty()) {
        return CBLSSignature();
    }

    std::vector<bls::InsecureSignature> v;
    v.reserve(sigs.size());
    for (auto& pk : sigs) {
        v.emplace_back(pk.impl);
    }

    auto agg = bls::InsecureSignature::Aggregate(v);
    CBLSSignature ret;
    ret.impl = agg;
    ret.fValid = true;
    ret.UpdateHash();
    return ret;
}

CBLSSignature CBLSSignature::AggregateSecure(const std::vector<CBLSSignature>& sigs,
                                             const std::vector<CBLSPublicKey>& pks,
                                             const uint256& hash)
{
    if (sigs.size() != pks.size() || sigs.empty()) {
        return CBLSSignature();
    }

    std::vector<bls::Signature> v;
    v.reserve(sigs.size());

    for (size_t i = 0; i < sigs.size(); i++) {
        bls::AggregationInfo aggInfo = bls::AggregationInfo::FromMsgHash(pks[i].impl, hash.begin());
        v.emplace_back(bls::Signature::FromInsecureSig(sigs[i].impl, aggInfo));
    }

    auto aggSig = bls::Signature::AggregateSigs(v);
    CBLSSignature ret;
    ret.impl = aggSig.GetInsecureSig();
    ret.fValid = true;
    ret.UpdateHash();
    return ret;
}

void CBLSSignature::SubInsecure(const CBLSSignature& o)
{
    assert(IsValid() && o.IsValid());
    impl = impl.DivideBy({o.impl});
    UpdateHash();
}

bool CBLSSignature::VerifyInsecure(const CBLSPublicKey& pubKey, const uint256& hash) const
{
    if (!IsValid() || !pubKey.IsValid()) {
        return false;
    }

    try {
        return impl.Verify({(const uint8_t*)hash.begin()}, {pubKey.impl});
    } catch (...) {
        return false;
    }
}

bool CBLSSignature::VerifyInsecureAggregated(const std::vector<CBLSPublicKey>& pubKeys, const std::vector<uint256>& hashes) const
{
    if (!IsValid()) {
        return false;
    }
    assert(!pubKeys.empty() && !hashes.empty() && pubKeys.size() == hashes.size());

    std::vector<bls::PublicKey> pubKeyVec;
    std::vector<const uint8_t*> hashes2;
    hashes2.reserve(hashes.size());
    pubKeyVec.reserve(pubKeys.size());
    for (size_t i = 0; i < pubKeys.size(); i++) {
        auto& p = pubKeys[i];
        if (!p.IsValid()) {
            return false;
        }
        pubKeyVec.push_back(p.impl);
        hashes2.push_back((uint8_t*)hashes[i].begin());
    }

    try {
        return impl.Verify(hashes2, pubKeyVec);
    } catch (...) {
        return false;
    }
}

bool CBLSSignature::VerifySecureAggregated(const std::vector<CBLSPublicKey>& pks, const uint256& hash) const
{
    if (pks.empty()) {
        return false;
    }

    std::vector<bls::AggregationInfo> v;
    v.reserve(pks.size());
    for (auto& pk : pks) {
        auto aggInfo = bls::AggregationInfo::FromMsgHash(pk.impl, hash.begin());
        v.emplace_back(aggInfo);
    }

    bls::AggregationInfo aggInfo = bls::AggregationInfo::MergeInfos(v);
    bls::Signature aggSig = bls::Signature::FromInsecureSig(impl, aggInfo);
    return aggSig.Verify();
}

bool CBLSSignature::Recover(const std::vector<CBLSSignature>& sigs, const std::vector<CBLSId>& ids)
{
    fValid = false;
    UpdateHash();

    if (sigs.empty() || ids.empty() || sigs.size() != ids.size()) {
        return false;
    }

    std::vector<bls::InsecureSignature> sigsVec;
    std::vector<const uint8_t*> idsVec;
    sigsVec.reserve(sigs.size());
    idsVec.reserve(sigs.size());

    for (size_t i = 0; i < sigs.size(); i++) {
        if (!sigs[i].IsValid() || !ids[i].IsValid()) {
            return false;
        }
        sigsVec.emplace_back(sigs[i].impl);
        idsVec.emplace_back(ids[i].impl.begin());
    }

    try {
        impl = bls::BLS::RecoverSig(sigsVec, idsVec);
    } catch (...) {
        return false;
    }

    fValid = true;
    UpdateHash();
    return true;
}

#ifndef BUILD_BITCOIN_INTERNAL
struct secure_user_allocator {
    typedef std::size_t size_type;
    typedef std::ptrdiff_t difference_type;

    static char* malloc(const size_type bytes)
    {
        return static_cast<char*>(LockedPoolManager::Instance().alloc(bytes));
    }

    static void free(char* const block)
    {
        LockedPoolManager::Instance().free(block);
    }
};

// every thread has it's own pool allocator for secure data to speed things up
// otherwise locking of mutexes slows down the system at places were you'd never expect it
// downside is that we must make sure that all threads have destroyed their copy of the pool before the global
// LockedPool is destroyed. This means that all worker threads must finish before static destruction begins
// we use sizeof(bn_t) as the pool request size as this is what Chia's BLS library will request in most cases
// In case something larger is requested, we directly call into LockedPool and accept the slowness
thread_local static boost::pool<secure_user_allocator> securePool(sizeof(bn_t) + sizeof(size_t));

static void* secure_allocate(size_t n)
{
    void* p;
    if (n <= securePool.get_requested_size() - sizeof(size_t)) {
        p = securePool.ordered_malloc();
    } else {
        p = secure_user_allocator::malloc(n + sizeof(size_t));
    }
    *(size_t*)p = n;
    p = (uint8_t*)p + sizeof(size_t);
    return p;
}

static void secure_free(void* p)
{
    if (!p) {
        return;
    }
    p = (uint8_t*)p - sizeof(size_t);
    size_t n = *(size_t*)p;
    memory_cleanse(p, n + sizeof(size_t));
    if (n <= securePool.get_requested_size() - sizeof(size_t)) {
        securePool.ordered_free(p);
    } else {
        secure_user_allocator::free((char*)p);
    }
}
#endif

bool BLSInit()
{
    if (!bls::BLS::Init()) {
        return false;
    }
#ifndef BUILD_BITCOIN_INTERNAL
    bls::BLS::SetSecureAllocator(secure_allocate, secure_free);
#endif
    return true;
}
<commit_msg>Implement mt_pooled_secure_allocator and use it for BLS secure allocation (#2375)<commit_after>// Copyright (c) 2018 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "bls.h"

#include "hash.h"
#include "random.h"
#include "tinyformat.h"

#ifndef BUILD_BITCOIN_INTERNAL
#include "support/allocators/mt_pooled_secure.h"
#endif

#include <assert.h>
#include <string.h>

bool CBLSId::InternalSetBuf(const void* buf)
{
    memcpy(impl.begin(), buf, sizeof(uint256));
    return true;
}

bool CBLSId::InternalGetBuf(void* buf) const
{
    memcpy(buf, impl.begin(), sizeof(uint256));
    return true;
}

void CBLSId::SetInt(int x)
{
    impl.SetHex(strprintf("%x", x));
    fValid = true;
    UpdateHash();
}

void CBLSId::SetHash(const uint256& hash)
{
    impl = hash;
    fValid = true;
    UpdateHash();
}

CBLSId CBLSId::FromInt(int64_t i)
{
    CBLSId id;
    id.SetInt(i);
    return id;
}

CBLSId CBLSId::FromHash(const uint256& hash)
{
    CBLSId id;
    id.SetHash(hash);
    return id;
}

bool CBLSSecretKey::InternalSetBuf(const void* buf)
{
    try {
        impl = bls::PrivateKey::FromBytes((const uint8_t*)buf);
        return true;
    } catch (...) {
        return false;
    }
}

bool CBLSSecretKey::InternalGetBuf(void* buf) const
{
    impl.Serialize((uint8_t*)buf);
    return true;
}

void CBLSSecretKey::AggregateInsecure(const CBLSSecretKey& o)
{
    assert(IsValid() && o.IsValid());
    impl = bls::PrivateKey::AggregateInsecure({impl, o.impl});
    UpdateHash();
}

CBLSSecretKey CBLSSecretKey::AggregateInsecure(const std::vector<CBLSSecretKey>& sks)
{
    if (sks.empty()) {
        return CBLSSecretKey();
    }

    std::vector<bls::PrivateKey> v;
    v.reserve(sks.size());
    for (auto& sk : sks) {
        v.emplace_back(sk.impl);
    }

    auto agg = bls::PrivateKey::AggregateInsecure(v);
    CBLSSecretKey ret;
    ret.impl = agg;
    ret.fValid = true;
    ret.UpdateHash();
    return ret;
}

#ifndef BUILD_BITCOIN_INTERNAL
void CBLSSecretKey::MakeNewKey()
{
    unsigned char buf[32];
    while (true) {
        GetStrongRandBytes(buf, sizeof(buf));
        try {
            impl = bls::PrivateKey::FromBytes((const uint8_t*)buf);
            break;
        } catch (...) {
        }
    }
    fValid = true;
    UpdateHash();
}
#endif

bool CBLSSecretKey::SecretKeyShare(const std::vector<CBLSSecretKey>& msk, const CBLSId& _id)
{
    fValid = false;
    UpdateHash();

    if (!_id.IsValid()) {
        return false;
    }

    std::vector<bls::PrivateKey> mskVec;
    mskVec.reserve(msk.size());
    for (const CBLSSecretKey& sk : msk) {
        if (!sk.IsValid()) {
            return false;
        }
        mskVec.emplace_back(sk.impl);
    }

    try {
        impl = bls::BLS::PrivateKeyShare(mskVec, (const uint8_t*)_id.impl.begin());
    } catch (...) {
        return false;
    }

    fValid = true;
    UpdateHash();
    return true;
}

CBLSPublicKey CBLSSecretKey::GetPublicKey() const
{
    if (!IsValid()) {
        return CBLSPublicKey();
    }

    CBLSPublicKey pubKey;
    pubKey.impl = impl.GetPublicKey();
    pubKey.fValid = true;
    pubKey.UpdateHash();
    return pubKey;
}

CBLSSignature CBLSSecretKey::Sign(const uint256& hash) const
{
    if (!IsValid()) {
        return CBLSSignature();
    }

    CBLSSignature sigRet;
    sigRet.impl = impl.SignInsecurePrehashed((const uint8_t*)hash.begin());

    sigRet.fValid = true;
    sigRet.UpdateHash();

    return sigRet;
}

bool CBLSPublicKey::InternalSetBuf(const void* buf)
{
    try {
        impl = bls::PublicKey::FromBytes((const uint8_t*)buf);
        return true;
    } catch (...) {
        return false;
    }
}

bool CBLSPublicKey::InternalGetBuf(void* buf) const
{
    impl.Serialize((uint8_t*)buf);
    return true;
}

void CBLSPublicKey::AggregateInsecure(const CBLSPublicKey& o)
{
    assert(IsValid() && o.IsValid());
    impl = bls::PublicKey::AggregateInsecure({impl, o.impl});
    UpdateHash();
}

CBLSPublicKey CBLSPublicKey::AggregateInsecure(const std::vector<CBLSPublicKey>& pks)
{
    if (pks.empty()) {
        return CBLSPublicKey();
    }

    std::vector<bls::PublicKey> v;
    v.reserve(pks.size());
    for (auto& pk : pks) {
        v.emplace_back(pk.impl);
    }

    auto agg = bls::PublicKey::AggregateInsecure(v);
    CBLSPublicKey ret;
    ret.impl = agg;
    ret.fValid = true;
    ret.UpdateHash();
    return ret;
}

bool CBLSPublicKey::PublicKeyShare(const std::vector<CBLSPublicKey>& mpk, const CBLSId& _id)
{
    fValid = false;
    UpdateHash();

    if (!_id.IsValid()) {
        return false;
    }

    std::vector<bls::PublicKey> mpkVec;
    mpkVec.reserve(mpk.size());
    for (const CBLSPublicKey& pk : mpk) {
        if (!pk.IsValid()) {
            return false;
        }
        mpkVec.emplace_back(pk.impl);
    }

    try {
        impl = bls::BLS::PublicKeyShare(mpkVec, (const uint8_t*)_id.impl.begin());
    } catch (...) {
        return false;
    }

    fValid = true;
    UpdateHash();
    return true;
}

bool CBLSPublicKey::DHKeyExchange(const CBLSSecretKey& sk, const CBLSPublicKey& pk)
{
    fValid = false;
    UpdateHash();

    if (!sk.IsValid() || !pk.IsValid()) {
        return false;
    }
    impl = bls::BLS::DHKeyExchange(sk.impl, pk.impl);
    fValid = true;
    UpdateHash();
    return true;
}

bool CBLSSignature::InternalSetBuf(const void* buf)
{
    try {
        impl = bls::InsecureSignature::FromBytes((const uint8_t*)buf);
        return true;
    } catch (...) {
        return false;
    }
}

bool CBLSSignature::InternalGetBuf(void* buf) const
{
    impl.Serialize((uint8_t*)buf);
    return true;
}

void CBLSSignature::AggregateInsecure(const CBLSSignature& o)
{
    assert(IsValid() && o.IsValid());
    impl = bls::InsecureSignature::Aggregate({impl, o.impl});
    UpdateHash();
}

CBLSSignature CBLSSignature::AggregateInsecure(const std::vector<CBLSSignature>& sigs)
{
    if (sigs.empty()) {
        return CBLSSignature();
    }

    std::vector<bls::InsecureSignature> v;
    v.reserve(sigs.size());
    for (auto& pk : sigs) {
        v.emplace_back(pk.impl);
    }

    auto agg = bls::InsecureSignature::Aggregate(v);
    CBLSSignature ret;
    ret.impl = agg;
    ret.fValid = true;
    ret.UpdateHash();
    return ret;
}

CBLSSignature CBLSSignature::AggregateSecure(const std::vector<CBLSSignature>& sigs,
                                             const std::vector<CBLSPublicKey>& pks,
                                             const uint256& hash)
{
    if (sigs.size() != pks.size() || sigs.empty()) {
        return CBLSSignature();
    }

    std::vector<bls::Signature> v;
    v.reserve(sigs.size());

    for (size_t i = 0; i < sigs.size(); i++) {
        bls::AggregationInfo aggInfo = bls::AggregationInfo::FromMsgHash(pks[i].impl, hash.begin());
        v.emplace_back(bls::Signature::FromInsecureSig(sigs[i].impl, aggInfo));
    }

    auto aggSig = bls::Signature::AggregateSigs(v);
    CBLSSignature ret;
    ret.impl = aggSig.GetInsecureSig();
    ret.fValid = true;
    ret.UpdateHash();
    return ret;
}

void CBLSSignature::SubInsecure(const CBLSSignature& o)
{
    assert(IsValid() && o.IsValid());
    impl = impl.DivideBy({o.impl});
    UpdateHash();
}

bool CBLSSignature::VerifyInsecure(const CBLSPublicKey& pubKey, const uint256& hash) const
{
    if (!IsValid() || !pubKey.IsValid()) {
        return false;
    }

    try {
        return impl.Verify({(const uint8_t*)hash.begin()}, {pubKey.impl});
    } catch (...) {
        return false;
    }
}

bool CBLSSignature::VerifyInsecureAggregated(const std::vector<CBLSPublicKey>& pubKeys, const std::vector<uint256>& hashes) const
{
    if (!IsValid()) {
        return false;
    }
    assert(!pubKeys.empty() && !hashes.empty() && pubKeys.size() == hashes.size());

    std::vector<bls::PublicKey> pubKeyVec;
    std::vector<const uint8_t*> hashes2;
    hashes2.reserve(hashes.size());
    pubKeyVec.reserve(pubKeys.size());
    for (size_t i = 0; i < pubKeys.size(); i++) {
        auto& p = pubKeys[i];
        if (!p.IsValid()) {
            return false;
        }
        pubKeyVec.push_back(p.impl);
        hashes2.push_back((uint8_t*)hashes[i].begin());
    }

    try {
        return impl.Verify(hashes2, pubKeyVec);
    } catch (...) {
        return false;
    }
}

bool CBLSSignature::VerifySecureAggregated(const std::vector<CBLSPublicKey>& pks, const uint256& hash) const
{
    if (pks.empty()) {
        return false;
    }

    std::vector<bls::AggregationInfo> v;
    v.reserve(pks.size());
    for (auto& pk : pks) {
        auto aggInfo = bls::AggregationInfo::FromMsgHash(pk.impl, hash.begin());
        v.emplace_back(aggInfo);
    }

    bls::AggregationInfo aggInfo = bls::AggregationInfo::MergeInfos(v);
    bls::Signature aggSig = bls::Signature::FromInsecureSig(impl, aggInfo);
    return aggSig.Verify();
}

bool CBLSSignature::Recover(const std::vector<CBLSSignature>& sigs, const std::vector<CBLSId>& ids)
{
    fValid = false;
    UpdateHash();

    if (sigs.empty() || ids.empty() || sigs.size() != ids.size()) {
        return false;
    }

    std::vector<bls::InsecureSignature> sigsVec;
    std::vector<const uint8_t*> idsVec;
    sigsVec.reserve(sigs.size());
    idsVec.reserve(sigs.size());

    for (size_t i = 0; i < sigs.size(); i++) {
        if (!sigs[i].IsValid() || !ids[i].IsValid()) {
            return false;
        }
        sigsVec.emplace_back(sigs[i].impl);
        idsVec.emplace_back(ids[i].impl.begin());
    }

    try {
        impl = bls::BLS::RecoverSig(sigsVec, idsVec);
    } catch (...) {
        return false;
    }

    fValid = true;
    UpdateHash();
    return true;
}

#ifndef BUILD_BITCOIN_INTERNAL

static mt_pooled_secure_allocator<uint8_t> g_blsSecureAllocator(sizeof(bn_t) + sizeof(size_t));

static void* secure_allocate(size_t n)
{
    uint8_t* ptr = g_blsSecureAllocator.allocate(n + sizeof(size_t));
    *(size_t*)ptr = n;
    return ptr + sizeof(size_t);
}

static void secure_free(void* p)
{
    if (!p) {
        return;
    }

    uint8_t* ptr = (uint8_t*)p - sizeof(size_t);
    size_t n = *(size_t*)ptr;
    return g_blsSecureAllocator.deallocate(ptr, n);
}
#endif

bool BLSInit()
{
    if (!bls::BLS::Init()) {
        return false;
    }
#ifndef BUILD_BITCOIN_INTERNAL
    bls::BLS::SetSecureAllocator(secure_allocate, secure_free);
#endif
    return true;
}
<|endoftext|>


<commit_before>/*
 * The main source of the Beng proxy server.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "direct.hxx"
#include "bp_instance.hxx"
#include "bp_connection.hxx"
#include "bp_worker.hxx"
#include "bp_global.hxx"
#include "crash.hxx"
#include "fb_pool.hxx"
#include "session_manager.hxx"
#include "session_save.hxx"
#include "tstock.hxx"
#include "tcp_stock.hxx"
#include "tcp_balancer.hxx"
#include "memcached/memcached_stock.hxx"
#include "stock/MapStock.hxx"
#include "tcache.hxx"
#include "http_cache.hxx"
#include "lhttp_stock.hxx"
#include "fcgi/Stock.hxx"
#include "was/was_stock.hxx"
#include "delegate/Stock.hxx"
#include "fcache.hxx"
#include "thread_pool.hxx"
#include "failure.hxx"
#include "bulldog.h"
#include "balancer.hxx"
#include "pipe_stock.hxx"
#include "resource_loader.hxx"
#include "bp_control.hxx"
#include "log-glue.h"
#include "ua_classification.hxx"
#include "ssl/ssl_init.hxx"
#include "ssl/ssl_client.hxx"
#include "system/SetupProcess.hxx"
#include "capabilities.hxx"
#include "spawn/Local.hxx"
#include "spawn/Glue.hxx"
#include "spawn/Client.hxx"
#include "net/SocketAddress.hxx"
#include "net/ServerSocket.hxx"
#include "util/Error.hxx"
#include "util/Macros.hxx"
#include "util/PrintException.hxx"

#include <daemon/daemonize.h>
#include <daemon/log.h>

#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>

#ifdef HAVE_LIBNFS
#include "nfs_stock.hxx"
#include "nfs_cache.hxx"
#endif

#ifndef NDEBUG
bool debug_mode = false;
#endif

static constexpr cap_value_t cap_keep_list[] = {
#ifndef USE_SPAWNER
    /* keep the KILL capability to be able to kill child processes
       that have switched to another uid (e.g. via JailCGI) */
    CAP_KILL,
#endif

#ifdef HAVE_LIBNFS
    /* allow libnfs to bind to privileged ports, which in turn allows
       disabling the "insecure" flag on the NFS server */
    CAP_NET_BIND_SERVICE,
#endif
};

static void
free_all_listeners(BpInstance *instance)
{
    instance->listeners.clear();
}

void
all_listeners_event_add(BpInstance *instance)
{
    for (auto &listener : instance->listeners)
        listener.AddEvent();
}

void
all_listeners_event_del(BpInstance *instance)
{
    for (auto &listener : instance->listeners)
        listener.RemoveEvent();
}

inline void
BpInstance::ShutdownCallback()
{
    if (should_exit)
        return;

    should_exit = true;
    deinit_signals(this);
    thread_pool_stop();

#ifdef USE_SPAWNER
    spawn->Disable();
#endif

    free_all_listeners(this);

    while (!list_empty(&connections))
        close_connection((struct client_connection*)connections.next);

    pool_commit();

    spawn_worker_event.Cancel();

    child_process_registry.SetVolatile();

    thread_pool_join();
    thread_pool_deinit();

    KillAllWorkers();

    session_save_deinit();
    session_manager_deinit();

    if (translate_cache != nullptr)
        translate_cache_close(translate_cache);

    if (translate_stock != nullptr)
        tstock_free(translate_stock);

    if (http_cache != nullptr) {
        http_cache_close(http_cache);
        http_cache = nullptr;
    }

    if (filter_cache != nullptr) {
        filter_cache_close(filter_cache);
        filter_cache = nullptr;
    }

    if (lhttp_stock != nullptr) {
        lhttp_stock_free(lhttp_stock);
        lhttp_stock = nullptr;
    }

    if (fcgi_stock != nullptr) {
        fcgi_stock_free(fcgi_stock);
        fcgi_stock = nullptr;
    }

    if (was_stock != nullptr) {
        hstock_free(was_stock);
        was_stock = nullptr;
    }

    if (memcached_stock != nullptr)
        memcached_stock_free(memcached_stock);

    if (tcp_balancer != nullptr)
        tcp_balancer_free(tcp_balancer);

    if (tcp_stock != nullptr)
        hstock_free(tcp_stock);

    if (balancer != nullptr)
        balancer_free(balancer);

    if (delegate_stock != nullptr)
        hstock_free(delegate_stock);

#ifdef HAVE_LIBNFS
    if (nfs_cache != nullptr)
        nfs_cache_free(nfs_cache);

    if (nfs_stock != nullptr)
        nfs_stock_free(nfs_stock);
#endif

    if (pipe_stock != nullptr)
        pipe_stock_free(pipe_stock);

    local_control_handler_deinit(this);
    global_control_handler_deinit(this);

    fb_pool_disable();

    pool_commit();
}

void
BpInstance::ShutdownCallback(void *ctx)
{
    auto &instance = *(BpInstance *)ctx;
    instance.ShutdownCallback();
}

static void
reload_event_callback(int fd gcc_unused, short event gcc_unused,
                      void *ctx)
{
    auto *instance = (BpInstance *)ctx;

    daemon_log(3, "caught signal %d, flushing all caches (pid=%d)\n",
               fd, (int)getpid());

    daemonize_reopen_logfile();

    translate_cache_flush(*instance->translate_cache);
    http_cache_flush(*instance->http_cache);
    filter_cache_flush(instance->filter_cache);
    fb_pool_compress();
}

void
init_signals(BpInstance *instance)
{
    instance->shutdown_listener.Enable();

    instance->sighup_event.Set(SIGHUP, reload_event_callback, instance);
    instance->sighup_event.Add();
}

void
deinit_signals(BpInstance *instance)
{
    instance->shutdown_listener.Disable();
    instance->sighup_event.Delete();
}

static void
add_listener(BpInstance *instance, struct addrinfo *ai, const char *tag)
{
    Error error;

    assert(ai != nullptr);

    do {
        instance->listeners.emplace_front(*instance, tag);
        auto &listener = instance->listeners.front();

        if (!listener.Listen(ai->ai_family, ai->ai_socktype,
                             ai->ai_protocol,
                             SocketAddress(ai->ai_addr, ai->ai_addrlen),
                             error)) {
            fprintf(stderr, "%s\n", error.GetMessage());
            exit(2);
        }

        ai = ai->ai_next;
    } while (ai != nullptr);
}

static void
add_tcp_listener(BpInstance *instance, int port, const char *tag)
{
    Error error;

    instance->listeners.emplace_front(*instance, tag);
    auto &listener = instance->listeners.front();
    if (!listener.ListenTCP(port, error)) {
        fprintf(stderr, "%s\n", error.GetMessage());
        exit(2);
    }
}

int main(int argc, char **argv)
try {
    int ret;
    bool bret;
    int gcc_unused ref;

#ifndef NDEBUG
    if (geteuid() != 0)
        debug_mode = true;
#endif

    BpInstance instance;

    /* configuration */

    parse_cmdline(&instance.config, instance.pool, argc, argv);

    if (instance.config.ports.empty() && instance.config.listen.empty())
        instance.config.ports.push_back(debug_mode ? 8080 : 80);

    /* initialize */

    SetupProcess();

    const ScopeSslGlobalInit ssl_init;
    ssl_client_init();

    direct_global_init();

    init_signals(&instance);

    for (auto i : instance.config.ports)
        add_tcp_listener(&instance, i, nullptr);

    for (const auto &i : instance.config.listen)
        add_listener(&instance, i.address,
                     i.tag.empty() ? nullptr : i.tag.c_str());

    global_control_handler_init(&instance);

    if (instance.config.num_workers == 1)
        /* in single-worker mode with watchdog master process, let
           only the one worker handle control commands */
        global_control_handler_disable(instance);

    /* daemonize */

    ret = daemonize();
    if (ret < 0)
        exit(2);

    /* post-daemon initialization */

    fb_pool_init(true);

#ifdef USE_SPAWNER
    /* note: this function call passes a temporary SpawnConfig copy,
       because the reference will be evaluated in the child process
       after ~BpInstance() has been called */
    instance.spawn = StartSpawnServer(SpawnConfig(instance.config.spawn),
                                      [argc, argv, &instance](){
            /* rename the process */
            size_t name_size = strlen(argv[0]);
            for (int i = 0; i < argc; ++i)
                memset(argv[i], 0, strlen(argv[i]));
            strncpy(argv[0], "spawn", name_size);

            instance.event_base.Reinit();

            global_control_handler_deinit(&instance);
            free_all_listeners(&instance);
            deinit_signals(&instance);

            instance.~BpInstance();
        });
    instance.spawn_service = instance.spawn;
#else
    LocalSpawnService spawn_service(instance.config.spawn,
                                    instance.child_process_registry);
    instance.spawn_service = &spawn_service;
#endif

    if (!crash_global_init()) {
        fprintf(stderr, "crash_global_init() failed\n");
        return EXIT_FAILURE;
    }

    bret = session_manager_init(instance.config.session_idle_timeout,
                                instance.config.cluster_size,
                                instance.config.cluster_node);
    if (!bret) {
        fprintf(stderr, "session_manager_init() failed\n");
        exit(2);
    }

    session_save_init(instance.config.session_save_path);

    local_control_handler_init(&instance);

    try {
        local_control_handler_open(&instance);
    } catch (const std::exception &e) {
        PrintException(e);
    }

    instance.balancer = balancer_new(*instance.pool);
    instance.tcp_stock = tcp_stock_new(instance.config.tcp_stock_limit);
    instance.tcp_balancer = tcp_balancer_new(*instance.tcp_stock,
                                             *instance.balancer);

    if (instance.config.memcached_server != nullptr)
        instance.memcached_stock =
            memcached_stock_new(instance.tcp_balancer,
                                instance.config.memcached_server);

    if (instance.config.translation_socket != nullptr) {
        instance.translate_stock =
            tstock_new(instance.config.translation_socket,
                       instance.config.translate_stock_limit);

        instance.translate_cache = translate_cache_new(*instance.pool,
                                                       *instance.translate_stock,
                                                       instance.config.translate_cache_size,
                                                       false);
    }

    instance.lhttp_stock = lhttp_stock_new(0, 16, *instance.spawn_service);

    instance.fcgi_stock = fcgi_stock_new(instance.config.fcgi_stock_limit,
                                         instance.config.fcgi_stock_max_idle,
                                         *instance.spawn_service);

    instance.was_stock = was_stock_new(instance.config.was_stock_limit,
                                       instance.config.was_stock_max_idle,
                                       *instance.spawn_service);

    instance.delegate_stock = delegate_stock_new(*instance.spawn_service);

#ifdef HAVE_LIBNFS
    instance.nfs_stock = nfs_stock_new(instance.pool);
    instance.nfs_cache = nfs_cache_new(*instance.pool,
                                       instance.config.nfs_cache_size,
                                       *instance.nfs_stock);
#endif

    instance.resource_loader = resource_loader_new(instance.pool,
                                                   instance.tcp_balancer,
                                                   *instance.spawn_service,
                                                   instance.lhttp_stock,
                                                   instance.fcgi_stock,
                                                   instance.was_stock,
                                                   instance.delegate_stock,
                                                   instance.nfs_cache);

    instance.http_cache = http_cache_new(*instance.pool,
                                         instance.config.http_cache_size,
                                         instance.memcached_stock,
                                         *instance.resource_loader);

    instance.pipe_stock = pipe_stock_new();
    instance.filter_cache = filter_cache_new(instance.pool,
                                             instance.config.filter_cache_size,
                                             instance.resource_loader);

    failure_init();
    bulldog_init(instance.config.bulldog_path);

    global_translate_cache = instance.translate_cache;
    global_tcp_stock = instance.tcp_stock;
    global_tcp_balancer = instance.tcp_balancer;
    global_memcached_stock = instance.memcached_stock;
    global_http_cache = instance.http_cache;
    global_lhttp_stock = instance.lhttp_stock;
    global_fcgi_stock = instance.fcgi_stock;
    global_was_stock = instance.was_stock;
    global_delegate_stock = instance.delegate_stock;
    global_nfs_stock = instance.nfs_stock;
    global_nfs_cache = instance.nfs_cache;
    global_filter_cache = instance.filter_cache;
    global_pipe_stock = instance.pipe_stock;

    /* launch the access logger */

    if (!log_global_init(instance.config.access_logger))
        return EXIT_FAILURE;

    /* daemonize II */

    if (daemon_user_defined(&instance.config.user))
        capabilities_pre_setuid();

    if (daemon_user_set(&instance.config.user) < 0)
        return EXIT_FAILURE;

    if (daemon_user_defined(&instance.config.user))
        capabilities_post_setuid(cap_keep_list, ARRAY_SIZE(cap_keep_list));

    /* create worker processes */

    if (instance.config.num_workers > 0) {
        pid_t pid;

        /* the master process shouldn't work */
        all_listeners_event_del(&instance);

        while (instance.workers.size() < instance.config.num_workers) {
            pid = instance.SpawnWorker();
            if (pid <= 0)
                break;
        }
    } else {
        instance.ForkCow(false);
    }

    /* main loop */

    instance.event_base.Dispatch();

    /* cleanup */

    log_global_deinit();

    bulldog_deinit();
    failure_deinit();

    free_all_listeners(&instance);

#ifdef USE_SPAWNER
    delete instance.spawn;
#endif

    fb_pool_deinit();

    ssl_client_deinit();

    crash_global_deinit();

    daemonize_cleanup();

    ua_classification_deinit();
} catch (const std::exception &e) {
    PrintException(e);
    return EXIT_FAILURE;
}
<commit_msg>bp_main: use timer to spawn first worker<commit_after>/*
 * The main source of the Beng proxy server.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "direct.hxx"
#include "bp_instance.hxx"
#include "bp_connection.hxx"
#include "bp_worker.hxx"
#include "bp_global.hxx"
#include "crash.hxx"
#include "fb_pool.hxx"
#include "session_manager.hxx"
#include "session_save.hxx"
#include "tstock.hxx"
#include "tcp_stock.hxx"
#include "tcp_balancer.hxx"
#include "memcached/memcached_stock.hxx"
#include "stock/MapStock.hxx"
#include "tcache.hxx"
#include "http_cache.hxx"
#include "lhttp_stock.hxx"
#include "fcgi/Stock.hxx"
#include "was/was_stock.hxx"
#include "delegate/Stock.hxx"
#include "fcache.hxx"
#include "thread_pool.hxx"
#include "failure.hxx"
#include "bulldog.h"
#include "balancer.hxx"
#include "pipe_stock.hxx"
#include "resource_loader.hxx"
#include "bp_control.hxx"
#include "log-glue.h"
#include "ua_classification.hxx"
#include "ssl/ssl_init.hxx"
#include "ssl/ssl_client.hxx"
#include "system/SetupProcess.hxx"
#include "capabilities.hxx"
#include "spawn/Local.hxx"
#include "spawn/Glue.hxx"
#include "spawn/Client.hxx"
#include "event/Duration.hxx"
#include "net/SocketAddress.hxx"
#include "net/ServerSocket.hxx"
#include "util/Error.hxx"
#include "util/Macros.hxx"
#include "util/PrintException.hxx"

#include <daemon/daemonize.h>
#include <daemon/log.h>

#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>

#ifdef HAVE_LIBNFS
#include "nfs_stock.hxx"
#include "nfs_cache.hxx"
#endif

#ifndef NDEBUG
bool debug_mode = false;
#endif

static constexpr cap_value_t cap_keep_list[] = {
#ifndef USE_SPAWNER
    /* keep the KILL capability to be able to kill child processes
       that have switched to another uid (e.g. via JailCGI) */
    CAP_KILL,
#endif

#ifdef HAVE_LIBNFS
    /* allow libnfs to bind to privileged ports, which in turn allows
       disabling the "insecure" flag on the NFS server */
    CAP_NET_BIND_SERVICE,
#endif
};

static void
free_all_listeners(BpInstance *instance)
{
    instance->listeners.clear();
}

void
all_listeners_event_add(BpInstance *instance)
{
    for (auto &listener : instance->listeners)
        listener.AddEvent();
}

void
all_listeners_event_del(BpInstance *instance)
{
    for (auto &listener : instance->listeners)
        listener.RemoveEvent();
}

inline void
BpInstance::ShutdownCallback()
{
    if (should_exit)
        return;

    should_exit = true;
    deinit_signals(this);
    thread_pool_stop();

#ifdef USE_SPAWNER
    spawn->Disable();
#endif

    free_all_listeners(this);

    while (!list_empty(&connections))
        close_connection((struct client_connection*)connections.next);

    pool_commit();

    spawn_worker_event.Cancel();

    child_process_registry.SetVolatile();

    thread_pool_join();
    thread_pool_deinit();

    KillAllWorkers();

    session_save_deinit();
    session_manager_deinit();

    if (translate_cache != nullptr)
        translate_cache_close(translate_cache);

    if (translate_stock != nullptr)
        tstock_free(translate_stock);

    if (http_cache != nullptr) {
        http_cache_close(http_cache);
        http_cache = nullptr;
    }

    if (filter_cache != nullptr) {
        filter_cache_close(filter_cache);
        filter_cache = nullptr;
    }

    if (lhttp_stock != nullptr) {
        lhttp_stock_free(lhttp_stock);
        lhttp_stock = nullptr;
    }

    if (fcgi_stock != nullptr) {
        fcgi_stock_free(fcgi_stock);
        fcgi_stock = nullptr;
    }

    if (was_stock != nullptr) {
        hstock_free(was_stock);
        was_stock = nullptr;
    }

    if (memcached_stock != nullptr)
        memcached_stock_free(memcached_stock);

    if (tcp_balancer != nullptr)
        tcp_balancer_free(tcp_balancer);

    if (tcp_stock != nullptr)
        hstock_free(tcp_stock);

    if (balancer != nullptr)
        balancer_free(balancer);

    if (delegate_stock != nullptr)
        hstock_free(delegate_stock);

#ifdef HAVE_LIBNFS
    if (nfs_cache != nullptr)
        nfs_cache_free(nfs_cache);

    if (nfs_stock != nullptr)
        nfs_stock_free(nfs_stock);
#endif

    if (pipe_stock != nullptr)
        pipe_stock_free(pipe_stock);

    local_control_handler_deinit(this);
    global_control_handler_deinit(this);

    fb_pool_disable();

    pool_commit();
}

void
BpInstance::ShutdownCallback(void *ctx)
{
    auto &instance = *(BpInstance *)ctx;
    instance.ShutdownCallback();
}

static void
reload_event_callback(int fd gcc_unused, short event gcc_unused,
                      void *ctx)
{
    auto *instance = (BpInstance *)ctx;

    daemon_log(3, "caught signal %d, flushing all caches (pid=%d)\n",
               fd, (int)getpid());

    daemonize_reopen_logfile();

    translate_cache_flush(*instance->translate_cache);
    http_cache_flush(*instance->http_cache);
    filter_cache_flush(instance->filter_cache);
    fb_pool_compress();
}

void
init_signals(BpInstance *instance)
{
    instance->shutdown_listener.Enable();

    instance->sighup_event.Set(SIGHUP, reload_event_callback, instance);
    instance->sighup_event.Add();
}

void
deinit_signals(BpInstance *instance)
{
    instance->shutdown_listener.Disable();
    instance->sighup_event.Delete();
}

static void
add_listener(BpInstance *instance, struct addrinfo *ai, const char *tag)
{
    Error error;

    assert(ai != nullptr);

    do {
        instance->listeners.emplace_front(*instance, tag);
        auto &listener = instance->listeners.front();

        if (!listener.Listen(ai->ai_family, ai->ai_socktype,
                             ai->ai_protocol,
                             SocketAddress(ai->ai_addr, ai->ai_addrlen),
                             error)) {
            fprintf(stderr, "%s\n", error.GetMessage());
            exit(2);
        }

        ai = ai->ai_next;
    } while (ai != nullptr);
}

static void
add_tcp_listener(BpInstance *instance, int port, const char *tag)
{
    Error error;

    instance->listeners.emplace_front(*instance, tag);
    auto &listener = instance->listeners.front();
    if (!listener.ListenTCP(port, error)) {
        fprintf(stderr, "%s\n", error.GetMessage());
        exit(2);
    }
}

int main(int argc, char **argv)
try {
    int ret;
    bool bret;
    int gcc_unused ref;

#ifndef NDEBUG
    if (geteuid() != 0)
        debug_mode = true;
#endif

    BpInstance instance;

    /* configuration */

    parse_cmdline(&instance.config, instance.pool, argc, argv);

    if (instance.config.ports.empty() && instance.config.listen.empty())
        instance.config.ports.push_back(debug_mode ? 8080 : 80);

    /* initialize */

    SetupProcess();

    const ScopeSslGlobalInit ssl_init;
    ssl_client_init();

    direct_global_init();

    init_signals(&instance);

    for (auto i : instance.config.ports)
        add_tcp_listener(&instance, i, nullptr);

    for (const auto &i : instance.config.listen)
        add_listener(&instance, i.address,
                     i.tag.empty() ? nullptr : i.tag.c_str());

    global_control_handler_init(&instance);

    if (instance.config.num_workers == 1)
        /* in single-worker mode with watchdog master process, let
           only the one worker handle control commands */
        global_control_handler_disable(instance);

    /* daemonize */

    ret = daemonize();
    if (ret < 0)
        exit(2);

    /* post-daemon initialization */

    fb_pool_init(true);

#ifdef USE_SPAWNER
    /* note: this function call passes a temporary SpawnConfig copy,
       because the reference will be evaluated in the child process
       after ~BpInstance() has been called */
    instance.spawn = StartSpawnServer(SpawnConfig(instance.config.spawn),
                                      [argc, argv, &instance](){
            /* rename the process */
            size_t name_size = strlen(argv[0]);
            for (int i = 0; i < argc; ++i)
                memset(argv[i], 0, strlen(argv[i]));
            strncpy(argv[0], "spawn", name_size);

            instance.event_base.Reinit();

            global_control_handler_deinit(&instance);
            free_all_listeners(&instance);
            deinit_signals(&instance);

            instance.~BpInstance();
        });
    instance.spawn_service = instance.spawn;
#else
    LocalSpawnService spawn_service(instance.config.spawn,
                                    instance.child_process_registry);
    instance.spawn_service = &spawn_service;
#endif

    if (!crash_global_init()) {
        fprintf(stderr, "crash_global_init() failed\n");
        return EXIT_FAILURE;
    }

    bret = session_manager_init(instance.config.session_idle_timeout,
                                instance.config.cluster_size,
                                instance.config.cluster_node);
    if (!bret) {
        fprintf(stderr, "session_manager_init() failed\n");
        exit(2);
    }

    session_save_init(instance.config.session_save_path);

    local_control_handler_init(&instance);

    try {
        local_control_handler_open(&instance);
    } catch (const std::exception &e) {
        PrintException(e);
    }

    instance.balancer = balancer_new(*instance.pool);
    instance.tcp_stock = tcp_stock_new(instance.config.tcp_stock_limit);
    instance.tcp_balancer = tcp_balancer_new(*instance.tcp_stock,
                                             *instance.balancer);

    if (instance.config.memcached_server != nullptr)
        instance.memcached_stock =
            memcached_stock_new(instance.tcp_balancer,
                                instance.config.memcached_server);

    if (instance.config.translation_socket != nullptr) {
        instance.translate_stock =
            tstock_new(instance.config.translation_socket,
                       instance.config.translate_stock_limit);

        instance.translate_cache = translate_cache_new(*instance.pool,
                                                       *instance.translate_stock,
                                                       instance.config.translate_cache_size,
                                                       false);
    }

    instance.lhttp_stock = lhttp_stock_new(0, 16, *instance.spawn_service);

    instance.fcgi_stock = fcgi_stock_new(instance.config.fcgi_stock_limit,
                                         instance.config.fcgi_stock_max_idle,
                                         *instance.spawn_service);

    instance.was_stock = was_stock_new(instance.config.was_stock_limit,
                                       instance.config.was_stock_max_idle,
                                       *instance.spawn_service);

    instance.delegate_stock = delegate_stock_new(*instance.spawn_service);

#ifdef HAVE_LIBNFS
    instance.nfs_stock = nfs_stock_new(instance.pool);
    instance.nfs_cache = nfs_cache_new(*instance.pool,
                                       instance.config.nfs_cache_size,
                                       *instance.nfs_stock);
#endif

    instance.resource_loader = resource_loader_new(instance.pool,
                                                   instance.tcp_balancer,
                                                   *instance.spawn_service,
                                                   instance.lhttp_stock,
                                                   instance.fcgi_stock,
                                                   instance.was_stock,
                                                   instance.delegate_stock,
                                                   instance.nfs_cache);

    instance.http_cache = http_cache_new(*instance.pool,
                                         instance.config.http_cache_size,
                                         instance.memcached_stock,
                                         *instance.resource_loader);

    instance.pipe_stock = pipe_stock_new();
    instance.filter_cache = filter_cache_new(instance.pool,
                                             instance.config.filter_cache_size,
                                             instance.resource_loader);

    failure_init();
    bulldog_init(instance.config.bulldog_path);

    global_translate_cache = instance.translate_cache;
    global_tcp_stock = instance.tcp_stock;
    global_tcp_balancer = instance.tcp_balancer;
    global_memcached_stock = instance.memcached_stock;
    global_http_cache = instance.http_cache;
    global_lhttp_stock = instance.lhttp_stock;
    global_fcgi_stock = instance.fcgi_stock;
    global_was_stock = instance.was_stock;
    global_delegate_stock = instance.delegate_stock;
    global_nfs_stock = instance.nfs_stock;
    global_nfs_cache = instance.nfs_cache;
    global_filter_cache = instance.filter_cache;
    global_pipe_stock = instance.pipe_stock;

    /* launch the access logger */

    if (!log_global_init(instance.config.access_logger))
        return EXIT_FAILURE;

    /* daemonize II */

    if (daemon_user_defined(&instance.config.user))
        capabilities_pre_setuid();

    if (daemon_user_set(&instance.config.user) < 0)
        return EXIT_FAILURE;

    if (daemon_user_defined(&instance.config.user))
        capabilities_post_setuid(cap_keep_list, ARRAY_SIZE(cap_keep_list));

    /* create worker processes */

    if (instance.config.num_workers > 0) {
        /* the master process shouldn't work */
        all_listeners_event_del(&instance);

        /* spawn the first worker really soon */
        instance.spawn_worker_event.Add(EventDuration<0, 10000>::value);
    } else {
        instance.ForkCow(false);
    }

    /* main loop */

    instance.event_base.Dispatch();

    /* cleanup */

    log_global_deinit();

    bulldog_deinit();
    failure_deinit();

    free_all_listeners(&instance);

#ifdef USE_SPAWNER
    delete instance.spawn;
#endif

    fb_pool_deinit();

    ssl_client_deinit();

    crash_global_deinit();

    daemonize_cleanup();

    ua_classification_deinit();
} catch (const std::exception &e) {
    PrintException(e);
    return EXIT_FAILURE;
}
<|endoftext|>


<commit_before>#include "bxstats.h"

#include "bxcommon.h"

#include <getopt.h>
#include <iostream>
#include <sstream>

#include "SeqLib/BamReader.h"

namespace opt {

  static std::string bam; // the bam to analyze
  static bool verbose = false;
  static char astagtype = ' ';
  static std::string tag = "BX"; // tag to split by
}

static const char* shortopts = "hvt:A:";
static const struct option longopts[] = {
  { "help",                    no_argument, NULL, 'h' },
  { "tag",                     required_argument, NULL, 't' },
  { "bam",                     required_argument, NULL, 'b' },
  { "as-tag-type",             required_argument, NULL, 'A' },
  { NULL, 0, NULL, 0 }
};

static const char *STAT_USAGE_MESSAGE =
"Usage: bxtools stat <BAM/SAM/CRAM> > stats.tsv\n"
"Description: Gather BX-level statistics\n"
"\n"
"  General options\n"
"  -v, --verbose                        Set verbose output\n"
"  -t, --tag                            Collect stats by a tag other than BX (e.g. MI)\n"
"  -A, --as-tag-type                    Alignment info type (Z, i, or f). e.g. AS:Z:10.\n"
"\n";

static void parseOptions(int argc, char** argv);

void runStat(int argc, char** argv) {

  parseOptions(argc, argv);

  // open the BAM
  SeqLib::BamReader reader;
  if (!reader.Open(opt::bam)) {
    std::cerr << "Failed to open bam: " << opt::bam << std::endl;
    exit(EXIT_FAILURE);
  }

  std::unordered_map<std::string, BXStat> bxstats;

  // loop and collect
  SeqLib::BamRecord r;
  size_t count = 0;
  while (reader.GetNextRecord(r)) {
    std::string bx;
    bool tag_present = r.GetZTag(opt::tag, bx);
    BXLOOPCHECK(r, bxstats.size(), opt::tag)
    if (!tag_present)
      continue;

    float as = -1;

    //debug
    std::string as_string = "NA";
    int as_int = -1;
    /*std::cerr << " STRING " << r.GetZTag("AS", as_string);
    std::cerr << " INT " << r.GetIntTag("AS", as_int);
    std::cerr << " FLOAT " << r.GetFloatTag("AS", as);
    std::cerr << " STRINGVAL " << as_string << " INT " << as_int << " FLOAT " << as << std::endl;
    break;
    */

    ++bxstats[bx].count;
    bxstats[bx].bx = bx;
    if (r.PairMappedFlag() && !r.Interchromosomal())
      bxstats[bx].isize.push_back(std::abs(r.InsertSize()));
    if (r.MappedFlag())
      bxstats[bx].mapq.push_back(std::abs(r.MapQuality()));

    switch(opt::astagtype) {
    case ' ':
      std::cerr << std::endl << "!! No AS tag type given. Not calculating AS stats !!" << std::endl << std::endl;
      opt::astagtype = 'X';
      break;
    case 'f':
      if (r.GetFloatTag("AS", as))
	bxstats[bx].as.push_back(as);
      break;
    case 'Z':
      if (r.GetZTag("AS", as_string))
	try {
	  bxstats[bx].as.push_back(std::stod(as_string));
	} catch (...) {
	  std::cerr << "ERROR: Could not convert AS of type Z with value " << as_string << " to double" << std::endl;
	}
      break;
    case 'i':
      if (r.GetIntTag("AS", as_int))
	bxstats[bx].as.push_back(as_int);
      break;
    }

  }

  for (const auto& b : bxstats)
    std::cout << b.second << std::endl;

}

static void parseOptions(int argc, char** argv) {

  bool die = false;
  bool help = false;

  if (argc < 2)
    die = true;
  else
    opt::bam = std::string(argv[1]);

  for (char c; (c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1;) {
    std::istringstream arg(optarg != NULL ? optarg : "");
    switch (c) {
    case 'v': opt::verbose = true; break;
    case 'h': help = true; break;
    case 'A':
      opt::astagtype = arg.str().at(0);
      break;
    }
  }

  if (die || help) {
    std::cerr << "\n" << STAT_USAGE_MESSAGE;
    die ? exit(EXIT_FAILURE) : exit(EXIT_SUCCESS);
  }

}

//http://stackoverflow.com/questions/2114797/compute-median-of-values-stored-in-vector-c
template <class T>
static double CalcMHWScore(std::vector<T> scores) {
  double median;
  size_t size = scores.size();

  std::sort(scores.begin(), scores.end());

  if (size  % 2 == 0)
      median = (scores[size / 2 - 1] + scores[size / 2]) / 2;
  else
      median = scores[size / 2];

  return median;
}

std::ostream& operator<<(std::ostream& out, const BXStat& b) {
  double isize_med = -1;
  double mapq_med = -1;
  double as_med = -1;
  if (b.isize.size())
    isize_med = CalcMHWScore(b.isize);
  if (b.mapq.size())
    mapq_med = CalcMHWScore(b.mapq);
  if (b.as.size())
    as_med = CalcMHWScore(b.as);
  out << b.bx << "\t" << b.count << "\t" << isize_med << "\t" << mapq_med
      << "\t" << as_med;
  return out;
}

<commit_msg>src/bxstats.cpp: Use either the AS:i or AS:f tag<commit_after>#include "bxstats.h"

#include "bxcommon.h"

#include <getopt.h>
#include <iostream>
#include <sstream>

#include "SeqLib/BamReader.h"

namespace opt {

  static std::string bam; // the bam to analyze
  static bool verbose = false;
  static std::string tag = "BX"; // tag to split by
}

static const char* shortopts = "hvt:";
static const struct option longopts[] = {
  { "help",                    no_argument, NULL, 'h' },
  { "tag",                     required_argument, NULL, 't' },
  { "bam",                     required_argument, NULL, 'b' },
  { NULL, 0, NULL, 0 }
};

static const char *STAT_USAGE_MESSAGE =
"Usage: bxtools stat <BAM/SAM/CRAM> > stats.tsv\n"
"Description: Gather BX-level statistics\n"
"\n"
"  General options\n"
"  -v, --verbose                        Set verbose output\n"
"  -t, --tag                            Collect stats by a tag other than BX (e.g. MI)\n"
"\n";

static void parseOptions(int argc, char** argv);

void runStat(int argc, char** argv) {

  parseOptions(argc, argv);

  // open the BAM
  SeqLib::BamReader reader;
  if (!reader.Open(opt::bam)) {
    std::cerr << "Failed to open bam: " << opt::bam << std::endl;
    exit(EXIT_FAILURE);
  }

  std::unordered_map<std::string, BXStat> bxstats;

  // loop and collect
  SeqLib::BamRecord r;
  size_t count = 0;
  while (reader.GetNextRecord(r)) {
    std::string bx;
    bool tag_present = r.GetZTag(opt::tag, bx);
    BXLOOPCHECK(r, bxstats.size(), opt::tag)
    if (!tag_present)
      continue;

    ++bxstats[bx].count;
    bxstats[bx].bx = bx;
    if (r.PairMappedFlag() && !r.Interchromosomal())
      bxstats[bx].isize.push_back(std::abs(r.InsertSize()));
    if (r.MappedFlag())
      bxstats[bx].mapq.push_back(std::abs(r.MapQuality()));

    int as_int = -1;
    float as_float = -1;
    if (r.GetIntTag("AS", as_int))
      bxstats[bx].as.push_back(as_int);
    else if (r.GetFloatTag("AS", as_float))
      bxstats[bx].as.push_back(as_float);
  }

  for (const auto& b : bxstats)
    std::cout << b.second << std::endl;

}

static void parseOptions(int argc, char** argv) {

  bool die = false;
  bool help = false;

  if (argc < 2)
    die = true;
  else
    opt::bam = std::string(argv[1]);

  for (char c; (c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1;) {
    std::istringstream arg(optarg != NULL ? optarg : "");
    switch (c) {
    case 'v': opt::verbose = true; break;
    case 'h': help = true; break;
    }
  }

  if (die || help) {
    std::cerr << "\n" << STAT_USAGE_MESSAGE;
    die ? exit(EXIT_FAILURE) : exit(EXIT_SUCCESS);
  }

}

//http://stackoverflow.com/questions/2114797/compute-median-of-values-stored-in-vector-c
template <class T>
static double CalcMHWScore(std::vector<T> scores) {
  double median;
  size_t size = scores.size();

  std::sort(scores.begin(), scores.end());

  if (size  % 2 == 0)
      median = (scores[size / 2 - 1] + scores[size / 2]) / 2;
  else
      median = scores[size / 2];

  return median;
}

std::ostream& operator<<(std::ostream& out, const BXStat& b) {
  double isize_med = -1;
  double mapq_med = -1;
  double as_med = -1;
  if (b.isize.size())
    isize_med = CalcMHWScore(b.isize);
  if (b.mapq.size())
    mapq_med = CalcMHWScore(b.mapq);
  if (b.as.size())
    as_med = CalcMHWScore(b.as);
  out << b.bx << "\t" << b.count << "\t" << isize_med << "\t" << mapq_med
      << "\t" << as_med;
  return out;
}

<|endoftext|>


<commit_before>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <sys/stat.h>
#include <iostream>
#include <string>
using namespace std;

#include "common/config.h"

#include "mon/MonMap.h"
#include "mon/Monitor.h"
#include "mon/MonitorStore.h"
#include "mon/MonClient.h"

#include "msg/SimpleMessenger.h"

#include "include/CompatSet.h"

#include "common/ceph_argparse.h"
#include "common/pick_address.h"
#include "common/Timer.h"
#include "common/errno.h"

#include "global/global_init.h"

extern CompatSet get_ceph_mon_feature_compat_set();

void usage()
{
  cerr << "usage: ceph-mon -i monid [--mon-data=pathtodata] [flags]" << std::endl;
  cerr << "  --debug_mon n\n";
  cerr << "        debug monitor level (e.g. 10)\n";
  cerr << "  --mkfs\n";
  cerr << "        build fresh monitor fs\n";
  generic_server_usage();
}

int main(int argc, const char **argv)
{
  int err;

  bool mkfs = false;
  std::string osdmapfn, inject_monmap;

  vector<const char*> args;
  argv_to_vec(argc, argv, args);
  env_to_vec(args);

  global_init(args, CEPH_ENTITY_TYPE_MON, CODE_ENVIRONMENT_DAEMON, 0);

  uuid_d fsid;
  std::string val;
  for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
    if (ceph_argparse_double_dash(args, i)) {
      break;
    } else if (ceph_argparse_flag(args, i, "-h", "--help", (char*)NULL)) {
      usage();
      exit(0);
    } else if (ceph_argparse_flag(args, i, "--mkfs", (char*)NULL)) {
      mkfs = true;
    } else if (ceph_argparse_witharg(args, i, &val, "--osdmap", (char*)NULL)) {
      osdmapfn = val;
    } else if (ceph_argparse_witharg(args, i, &val, "--inject_monmap", (char*)NULL)) {
      inject_monmap = val;
    } else if (ceph_argparse_witharg(args, i, &val, "--fsid", (char*)NULL)) {
      if (!fsid.parse(val.c_str())) {
	cerr << "unable to parse fsid '" << val << "'" << std::endl;
	exit(1);
      }
    } else {
      ++i;
    }
  }
  if (!args.empty()) {
    cerr << "too many arguments: " << args << std::endl;
    usage();
  }

  if (g_conf->mon_data.empty()) {
    cerr << "must specify '--mon-data=foo' data path" << std::endl;
    usage();
  }

  // -- mkfs --
  if (mkfs) {
    common_init_finish(g_ceph_context);

    bufferlist monmapbl, osdmapbl;
    std::string error;
    MonMap monmap;

    // load or generate monmap
    if (g_conf->monmap.length()) {
      int err = monmapbl.read_file(g_conf->monmap.c_str(), &error);
      if (err < 0) {
	cerr << argv[0] << ": error reading " << g_conf->monmap << ": " << error << std::endl;
	exit(1);
      }
      try {
	monmap.decode(monmapbl);
      }
      catch (const buffer::error& e) {
	cerr << argv[0] << ": error decoding monmap " << g_conf->monmap << ": " << e.what() << std::endl;
	exit(1);
      }
    } else {
      int err = MonClient::build_initial_monmap(g_ceph_context, monmap);
      if (err < 0) {
	cerr << argv[0] << ": error generating initial monmap: " << cpp_strerror(err) << std::endl;
	usage();
	exit(1);
      }

      pick_addresses(g_ceph_context);

      // am i part of the initial quorum?
      if (monmap.contains(g_conf->name.get_id())) {
	// hmm, make sure the ip listed exists on the current host?
	// maybe later.
      } else if (!g_conf->public_addr.is_blank_ip()) {
	if (monmap.contains(g_conf->public_addr)) {
	  string name;
	  monmap.get_addr_name(g_conf->public_addr, name);
	  monmap.rename(name, g_conf->name.get_id());
	  cout << argv[0] << ": renaming mon." << name << " " << g_conf->public_addr
	       << " to mon." << g_conf->name << std::endl;
	}
      } else {
	// is a local address listed without a name?  if so, name myself.

	// *******

      }
    }

    if (!fsid.is_zero()) {
      cout << argv[0] << ": setting fsid to " << fsid << std::endl;
      monmap.fsid = fsid;
    }

    if (monmap.fsid.is_zero()) {
      cerr << argv[0] << ": generated monmap has no fsid; use '--fsid <uuid>'" << std::endl;
      exit(10);
    }

    //monmap.print(cout);

    // osdmap
    if (osdmapfn.length()) {
      err = osdmapbl.read_file(osdmapfn.c_str(), &error);
      if (err < 0) {
	cerr << argv[0] << ": error reading " << osdmapfn << ": "
	     << error << std::endl;
	exit(1);
      }
    }

    // go
    MonitorStore store(g_conf->mon_data);
    Monitor mon(g_ceph_context, g_conf->name.get_id(), &store, 0, &monmap);
    mon.mkfs(osdmapbl);
    cout << argv[0] << ": created monfs at " << g_conf->mon_data
	 << " for " << g_conf->name << std::endl;
    return 0;
  }

  CompatSet mon_features = get_ceph_mon_feature_compat_set();
  CompatSet ondisk_features;

  MonitorStore store(g_conf->mon_data);
  err = store.mount();
  if (err < 0) {
    cerr << "problem opening monitor store in " << g_conf->mon_data << ": " << cpp_strerror(err) << std::endl;
    exit(1);
  }

  bufferlist magicbl;
  err = store.get_bl_ss(magicbl, "magic", 0);
  if (err < 0) {
    cerr << "unable to read magic from mon data.. did you run mkcephfs?" << std::endl;
    exit(1);
  }
  string magic(magicbl.c_str(), magicbl.length()-1);  // ignore trailing \n
  if (strcmp(magic.c_str(), CEPH_MON_ONDISK_MAGIC)) {
    cerr << "mon fs magic '" << magic << "' != current '" << CEPH_MON_ONDISK_MAGIC << "'" << std::endl;
    exit(1);
  }

  bufferlist features;
  store.get_bl_ss(features, COMPAT_SET_LOC, 0);
  if (features.length() == 0) {
    cerr << "WARNING: mon fs missing feature list.\n"
	 << "Assuming it is old-style and introducing one." << std::endl;
    //we only want the baseline ~v.18 features assumed to be on disk.
    //If new features are introduced this code needs to disappear or
    //be made smarter.
    ondisk_features = get_ceph_mon_feature_compat_set();
  } else {
    bufferlist::iterator it = features.begin();
    ondisk_features.decode(it);
  }

  if (!mon_features.writeable(ondisk_features)) {
    cerr << "monitor executable cannot read disk! Missing features: "
	 << std::endl;
    CompatSet diff = mon_features.unsupported(ondisk_features);
    //NEEDS_COMPATSET_ITER
    exit(1);
  }

  // inject new monmap?
  if (!inject_monmap.empty()) {
    bufferlist bl;
    std::string error;
    int r = bl.read_file(inject_monmap.c_str(), &error);
    if (r) {
      cerr << "unable to read monmap from " << inject_monmap << ": "
	   << error << std::endl;
      exit(1);
    }

    // get next version
    version_t v = store.get_int("monmap", "last_committed");
    cout << "last committed monmap epoch is " << v << ", injected map will be " << (v+1) << std::endl;
    v++;

    // set the version
    MonMap tmp;
    tmp.decode(bl);
    if (tmp.get_epoch() != v) {
      cout << "changing monmap epoch from " << tmp.get_epoch() << " to " << v << std::endl;
      tmp.set_epoch(v);
    }
    bufferlist mapbl;
    tmp.encode(mapbl);
    bufferlist final;
    ::encode(v, final);
    ::encode(mapbl, final);

    // save it
    store.put_bl_sn(mapbl, "monmap", v);
    store.put_bl_ss(final, "monmap", "latest");
    store.put_int(v, "monmap", "last_committed");

    cout << "done." << std::endl;
    exit(0);
  }

  // monmap?
  MonMap monmap;
  {
    bufferlist mapbl;
    bufferlist latest;
    store.get_bl_ss(latest, "monmap", "latest");
    if (latest.length() > 0) {
      bufferlist::iterator p = latest.begin();
      version_t v;
      ::decode(v, p);
      ::decode(mapbl, p);
    } else {
      store.get_bl_ss(mapbl, "mkfs", "monmap");
      if (mapbl.length() == 0) {
	cerr << "mon fs missing 'monmap/latest' and 'mkfs/monmap'" << std::endl;
	exit(1);
      }
    }
    try {
      monmap.decode(mapbl);
    }
    catch (const buffer::error& e) {
      cerr << "can't decode monmap: " << e.what() << std::endl;
    }
  }

  // this is what i will bind to
  entity_addr_t ipaddr;

  if (monmap.contains(g_conf->name.get_id())) {
    ipaddr = monmap.get_addr(g_conf->name.get_id());

    // print helpful warning if the conf file doesn't match
    entity_addr_t conf_addr;
    std::vector <std::string> my_sections;
    g_conf->get_my_sections(my_sections);
    std::string mon_addr_str;
    if (g_conf->get_val_from_conf_file(my_sections, "mon addr",
				       mon_addr_str, true) == 0) {
      if (conf_addr.parse(mon_addr_str.c_str()) && (ipaddr != conf_addr)) {
	cerr << "WARNING: 'mon addr' config option " << conf_addr
	     << " does not match monmap file" << std::endl
	     << "         continuing with monmap configuration" << std::endl;
      }
    }
  } else {
    dout(0) << g_conf->name << " does not exist in monmap, will attempt to join an existing cluster" << dendl;

    pick_addresses(g_ceph_context);
    if (g_conf->public_addr.is_blank_ip()) {
      derr << "no public_addr or public_network specified, and " << g_conf->name
	   << " not present in monmap" << dendl;
      exit(1);
    }
  }

  // bind
  SimpleMessenger *messenger = new SimpleMessenger(g_ceph_context);
  int rank = monmap.get_rank(g_conf->name.get_id());

  cout << "starting " << g_conf->name << " rank " << rank
       << " at " << ipaddr
       << " mon_data " << g_conf->mon_data
       << " fsid " << monmap.get_fsid()
       << std::endl;

  err = messenger->bind(ipaddr, 0);
  if (err < 0)
    return 1;

  // start monitor
  messenger->register_entity(entity_name_t::MON(rank));
  messenger->set_default_send_priority(CEPH_MSG_PRIO_HIGH);
  Monitor *mon = new Monitor(g_ceph_context, g_conf->name.get_id(), &store, messenger, &monmap);

  global_init_daemonize(g_ceph_context, 0);
  common_init_finish(g_ceph_context);
  global_init_chdir(g_ceph_context);
  messenger->start();

  uint64_t supported =
    CEPH_FEATURE_UID |
    CEPH_FEATURE_NOSRCADDR |
    CEPH_FEATURE_MONCLOCKCHECK |
    CEPH_FEATURE_PGID64;
  messenger->set_default_policy(SimpleMessenger::Policy::stateless_server(supported, 0));
  messenger->set_policy(entity_name_t::TYPE_MON,
			SimpleMessenger::Policy::lossless_peer(supported,
							       CEPH_FEATURE_UID |
							       CEPH_FEATURE_PGID64));
  messenger->set_policy(entity_name_t::TYPE_OSD,
			SimpleMessenger::Policy::stateless_server(supported,
								  CEPH_FEATURE_PGID64));
  mon->init();
  messenger->wait();

  store.umount();
  delete mon;
  messenger->destroy();

  // cd on exit, so that gmon.out (if any) goes into a separate directory for each node.
  char s[20];
  snprintf(s, sizeof(s), "gmon/%d", getpid());
  if ((mkdir(s, 0755) == 0) && (chdir(s) == 0)) {
    dout(0) << "ceph-mon: gmon.out should be in " << s << dendl;
  }

  return 0;
}

<commit_msg>mon: set default port if not specified...<commit_after>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <sys/stat.h>
#include <iostream>
#include <string>
using namespace std;

#include "common/config.h"

#include "mon/MonMap.h"
#include "mon/Monitor.h"
#include "mon/MonitorStore.h"
#include "mon/MonClient.h"

#include "msg/SimpleMessenger.h"

#include "include/CompatSet.h"

#include "common/ceph_argparse.h"
#include "common/pick_address.h"
#include "common/Timer.h"
#include "common/errno.h"

#include "global/global_init.h"

extern CompatSet get_ceph_mon_feature_compat_set();

void usage()
{
  cerr << "usage: ceph-mon -i monid [--mon-data=pathtodata] [flags]" << std::endl;
  cerr << "  --debug_mon n\n";
  cerr << "        debug monitor level (e.g. 10)\n";
  cerr << "  --mkfs\n";
  cerr << "        build fresh monitor fs\n";
  generic_server_usage();
}

int main(int argc, const char **argv)
{
  int err;

  bool mkfs = false;
  std::string osdmapfn, inject_monmap;

  vector<const char*> args;
  argv_to_vec(argc, argv, args);
  env_to_vec(args);

  global_init(args, CEPH_ENTITY_TYPE_MON, CODE_ENVIRONMENT_DAEMON, 0);

  uuid_d fsid;
  std::string val;
  for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
    if (ceph_argparse_double_dash(args, i)) {
      break;
    } else if (ceph_argparse_flag(args, i, "-h", "--help", (char*)NULL)) {
      usage();
      exit(0);
    } else if (ceph_argparse_flag(args, i, "--mkfs", (char*)NULL)) {
      mkfs = true;
    } else if (ceph_argparse_witharg(args, i, &val, "--osdmap", (char*)NULL)) {
      osdmapfn = val;
    } else if (ceph_argparse_witharg(args, i, &val, "--inject_monmap", (char*)NULL)) {
      inject_monmap = val;
    } else if (ceph_argparse_witharg(args, i, &val, "--fsid", (char*)NULL)) {
      if (!fsid.parse(val.c_str())) {
	cerr << "unable to parse fsid '" << val << "'" << std::endl;
	exit(1);
      }
    } else {
      ++i;
    }
  }
  if (!args.empty()) {
    cerr << "too many arguments: " << args << std::endl;
    usage();
  }

  if (g_conf->mon_data.empty()) {
    cerr << "must specify '--mon-data=foo' data path" << std::endl;
    usage();
  }

  // -- mkfs --
  if (mkfs) {
    common_init_finish(g_ceph_context);

    bufferlist monmapbl, osdmapbl;
    std::string error;
    MonMap monmap;

    // load or generate monmap
    if (g_conf->monmap.length()) {
      int err = monmapbl.read_file(g_conf->monmap.c_str(), &error);
      if (err < 0) {
	cerr << argv[0] << ": error reading " << g_conf->monmap << ": " << error << std::endl;
	exit(1);
      }
      try {
	monmap.decode(monmapbl);
      }
      catch (const buffer::error& e) {
	cerr << argv[0] << ": error decoding monmap " << g_conf->monmap << ": " << e.what() << std::endl;
	exit(1);
      }
    } else {
      int err = MonClient::build_initial_monmap(g_ceph_context, monmap);
      if (err < 0) {
	cerr << argv[0] << ": error generating initial monmap: " << cpp_strerror(err) << std::endl;
	usage();
	exit(1);
      }

      // resolve public_network -> public_addr
      pick_addresses(g_ceph_context);

      // am i part of the initial quorum?
      if (monmap.contains(g_conf->name.get_id())) {
	// hmm, make sure the ip listed exists on the current host?
	// maybe later.
      } else if (!g_conf->public_addr.is_blank_ip()) {
	entity_addr_t a = g_conf->public_addr;
	if (a.get_port() == 0)
	  a.set_port(CEPH_MON_PORT);
	if (monmap.contains(a)) {
	  string name;
	  monmap.get_addr_name(a, name);
	  monmap.rename(name, g_conf->name.get_id());
	  cout << argv[0] << ": renaming mon." << name << " " << a
	       << " to mon." << g_conf->name.get_id() << std::endl;
	}
      } else {
	// is a local address listed without a name?  if so, name myself.

	// *******

      }
    }

    if (!fsid.is_zero()) {
      cout << argv[0] << ": setting fsid to " << fsid << std::endl;
      monmap.fsid = fsid;
    }

    if (monmap.fsid.is_zero()) {
      cerr << argv[0] << ": generated monmap has no fsid; use '--fsid <uuid>'" << std::endl;
      exit(10);
    }

    //monmap.print(cout);

    // osdmap
    if (osdmapfn.length()) {
      err = osdmapbl.read_file(osdmapfn.c_str(), &error);
      if (err < 0) {
	cerr << argv[0] << ": error reading " << osdmapfn << ": "
	     << error << std::endl;
	exit(1);
      }
    }

    // go
    MonitorStore store(g_conf->mon_data);
    Monitor mon(g_ceph_context, g_conf->name.get_id(), &store, 0, &monmap);
    mon.mkfs(osdmapbl);
    cout << argv[0] << ": created monfs at " << g_conf->mon_data
	 << " for " << g_conf->name << std::endl;
    return 0;
  }

  CompatSet mon_features = get_ceph_mon_feature_compat_set();
  CompatSet ondisk_features;

  MonitorStore store(g_conf->mon_data);
  err = store.mount();
  if (err < 0) {
    cerr << "problem opening monitor store in " << g_conf->mon_data << ": " << cpp_strerror(err) << std::endl;
    exit(1);
  }

  bufferlist magicbl;
  err = store.get_bl_ss(magicbl, "magic", 0);
  if (err < 0) {
    cerr << "unable to read magic from mon data.. did you run mkcephfs?" << std::endl;
    exit(1);
  }
  string magic(magicbl.c_str(), magicbl.length()-1);  // ignore trailing \n
  if (strcmp(magic.c_str(), CEPH_MON_ONDISK_MAGIC)) {
    cerr << "mon fs magic '" << magic << "' != current '" << CEPH_MON_ONDISK_MAGIC << "'" << std::endl;
    exit(1);
  }

  bufferlist features;
  store.get_bl_ss(features, COMPAT_SET_LOC, 0);
  if (features.length() == 0) {
    cerr << "WARNING: mon fs missing feature list.\n"
	 << "Assuming it is old-style and introducing one." << std::endl;
    //we only want the baseline ~v.18 features assumed to be on disk.
    //If new features are introduced this code needs to disappear or
    //be made smarter.
    ondisk_features = get_ceph_mon_feature_compat_set();
  } else {
    bufferlist::iterator it = features.begin();
    ondisk_features.decode(it);
  }

  if (!mon_features.writeable(ondisk_features)) {
    cerr << "monitor executable cannot read disk! Missing features: "
	 << std::endl;
    CompatSet diff = mon_features.unsupported(ondisk_features);
    //NEEDS_COMPATSET_ITER
    exit(1);
  }

  // inject new monmap?
  if (!inject_monmap.empty()) {
    bufferlist bl;
    std::string error;
    int r = bl.read_file(inject_monmap.c_str(), &error);
    if (r) {
      cerr << "unable to read monmap from " << inject_monmap << ": "
	   << error << std::endl;
      exit(1);
    }

    // get next version
    version_t v = store.get_int("monmap", "last_committed");
    cout << "last committed monmap epoch is " << v << ", injected map will be " << (v+1) << std::endl;
    v++;

    // set the version
    MonMap tmp;
    tmp.decode(bl);
    if (tmp.get_epoch() != v) {
      cout << "changing monmap epoch from " << tmp.get_epoch() << " to " << v << std::endl;
      tmp.set_epoch(v);
    }
    bufferlist mapbl;
    tmp.encode(mapbl);
    bufferlist final;
    ::encode(v, final);
    ::encode(mapbl, final);

    // save it
    store.put_bl_sn(mapbl, "monmap", v);
    store.put_bl_ss(final, "monmap", "latest");
    store.put_int(v, "monmap", "last_committed");

    cout << "done." << std::endl;
    exit(0);
  }

  // monmap?
  MonMap monmap;
  {
    bufferlist mapbl;
    bufferlist latest;
    store.get_bl_ss(latest, "monmap", "latest");
    if (latest.length() > 0) {
      bufferlist::iterator p = latest.begin();
      version_t v;
      ::decode(v, p);
      ::decode(mapbl, p);
    } else {
      store.get_bl_ss(mapbl, "mkfs", "monmap");
      if (mapbl.length() == 0) {
	cerr << "mon fs missing 'monmap/latest' and 'mkfs/monmap'" << std::endl;
	exit(1);
      }
    }
    try {
      monmap.decode(mapbl);
    }
    catch (const buffer::error& e) {
      cerr << "can't decode monmap: " << e.what() << std::endl;
    }
  }

  // this is what i will bind to
  entity_addr_t ipaddr;

  if (monmap.contains(g_conf->name.get_id())) {
    ipaddr = monmap.get_addr(g_conf->name.get_id());

    // print helpful warning if the conf file doesn't match
    entity_addr_t conf_addr;
    std::vector <std::string> my_sections;
    g_conf->get_my_sections(my_sections);
    std::string mon_addr_str;
    if (g_conf->get_val_from_conf_file(my_sections, "mon addr",
				       mon_addr_str, true) == 0) {
      if (conf_addr.parse(mon_addr_str.c_str()) && (ipaddr != conf_addr)) {
	cerr << "WARNING: 'mon addr' config option " << conf_addr
	     << " does not match monmap file" << std::endl
	     << "         continuing with monmap configuration" << std::endl;
      }
    }
  } else {
    dout(0) << g_conf->name << " does not exist in monmap, will attempt to join an existing cluster" << dendl;

    pick_addresses(g_ceph_context);
    if (g_conf->public_addr.is_blank_ip()) {
      derr << "no public_addr or public_network specified, and " << g_conf->name
	   << " not present in monmap" << dendl;
      exit(1);
    }
  }

  // bind
  SimpleMessenger *messenger = new SimpleMessenger(g_ceph_context);
  int rank = monmap.get_rank(g_conf->name.get_id());

  cout << "starting " << g_conf->name << " rank " << rank
       << " at " << ipaddr
       << " mon_data " << g_conf->mon_data
       << " fsid " << monmap.get_fsid()
       << std::endl;

  err = messenger->bind(ipaddr, 0);
  if (err < 0)
    return 1;

  // start monitor
  messenger->register_entity(entity_name_t::MON(rank));
  messenger->set_default_send_priority(CEPH_MSG_PRIO_HIGH);
  Monitor *mon = new Monitor(g_ceph_context, g_conf->name.get_id(), &store, messenger, &monmap);

  global_init_daemonize(g_ceph_context, 0);
  common_init_finish(g_ceph_context);
  global_init_chdir(g_ceph_context);
  messenger->start();

  uint64_t supported =
    CEPH_FEATURE_UID |
    CEPH_FEATURE_NOSRCADDR |
    CEPH_FEATURE_MONCLOCKCHECK |
    CEPH_FEATURE_PGID64;
  messenger->set_default_policy(SimpleMessenger::Policy::stateless_server(supported, 0));
  messenger->set_policy(entity_name_t::TYPE_MON,
			SimpleMessenger::Policy::lossless_peer(supported,
							       CEPH_FEATURE_UID |
							       CEPH_FEATURE_PGID64));
  messenger->set_policy(entity_name_t::TYPE_OSD,
			SimpleMessenger::Policy::stateless_server(supported,
								  CEPH_FEATURE_PGID64));
  mon->init();
  messenger->wait();

  store.umount();
  delete mon;
  messenger->destroy();

  // cd on exit, so that gmon.out (if any) goes into a separate directory for each node.
  char s[20];
  snprintf(s, sizeof(s), "gmon/%d", getpid());
  if ((mkdir(s, 0755) == 0) && (chdir(s) == 0)) {
    dout(0) << "ceph-mon: gmon.out should be in " << s << dendl;
  }

  return 0;
}

<|endoftext|>


<commit_before>#include "circuit.h"

namespace qflex {

std::ostream &QflexGate::operator<<(std::ostream &out) const {
  out << "gate_name: " << this->name << std::endl;
  out << "qubits: ";
  for (const auto &q : this->qubits) out << q << " ";
  out << std::endl;
  if (std::size(this->params)) {
    out << "params: ";
    for (const auto &p : this->params) out << p << " ";
    out << std::endl;
  }
  return out;
}
std::ostream &operator<<(std::ostream &out, const QflexGate &gate) {
  return gate.operator<<(out);
}

void QflexCircuit::clear() {
  this->num_active_qubits = 0;
  this->depth = 0;
  gates.clear();
}

void QflexCircuit::load(std::istream &istream) {
  this->load(std::move(istream));
}
void QflexCircuit::load(std::istream &&istream) {
  // Check if token is a number
  auto is_number = [](const std::string &token) {
    try {
      std::stol(token);
    } catch (...) {
      return false;
    }
    return true;
  };

  // Check if token is an integer
  auto is_integer = [&is_number](const std::string &token) {
    return is_number(token) and std::stol(token) == std::stod(token);
  };

  // Given a line, strip everything that doesn't follow the right format
  auto strip_line = [](std::string line) {
    // Remove everything after '#'
    line = std::regex_replace(line, std::regex("#.*"), "");

    // Remove any special character
    line = std::regex_replace(line, std::regex("[^)(\\s\\ta-zA-Z0-9_.,-]"), "");

    // Convert tabs to spaces
    line = std::regex_replace(line, std::regex("[\\t]"), " ");

    // Remove multiple spaces
    line = std::regex_replace(line, std::regex("[\\s]{2,}"), " ");

    // Remove last space
    line = std::regex_replace(line, std::regex("\\s+$"), "");

    // Remove any space between a non-space char and '('
    line = std::regex_replace(line, std::regex("[\\s]+[(]"), "(");

    // Remove spaces between parentheses
    line = std::regex_replace(line, std::regex("\\s+(?=[^()]*\\))"), "");

    // After stripping, line should follow the format
    // 0 gate(p1,p2,...) q1 q2 ...

    return line;
  };

  // Given a line, tokenize it
  auto tokenize = [](const std::string &line,
                     const std::string &regex_expr = "[^\\s]+") {
    std::vector<std::string> tokens;
    auto word_regex = std::regex(regex_expr);
    for (auto w =
             std::sregex_iterator(std::begin(line), std::end(line), word_regex);
         w != std::sregex_iterator(); ++w)
      tokens.push_back(w->str());
    return tokens;
  };

  // Clear this circuit
  this->clear();

  std::size_t line_counter{0}, last_cycle_number{0};
  std::unordered_set<std::size_t> used_qubits;
  std::string line;

  auto error_msg = [&line, &line_counter](const std::string &msg) {
    return ERROR_MSG("[", std::to_string(line_counter + 1), ": ", line,
                     "] ", msg);
  };

  while (std::getline(istream, line)) {
    if (std::size(line = strip_line(line))) {
      // Check number of parentheses
      if (std::size_t n_open =
              std::count(std::begin(line), std::end(line), '('),
          n_close = std::count(std::begin(line), std::end(line), ')');
          n_open != n_close or n_open > 1)
        throw error_msg("Not-matching parentheses.");

      // Tokenize the line
      auto tokens = tokenize(line);

      // Enforce first line to be a single number which correspond to the number
      // of qubits
      if (line_counter == 0) {
        if (std::size(tokens) != 1 or std::stol(tokens[0]) <= 0)
          throw error_msg(
              "First line in circuit must be the number of active qubits.");
        this->num_active_qubits = std::stol(tokens[0]);
      } else {
        // Check the correct number of tokens
        if (std::size(tokens) < 3)
          throw error_msg(
              "Gate must be specified as: cycle gate_name[(p1[,p2,...])] q1 "
              "[q2, ...]");

        // Get gate
        this->gates.emplace_back();
        auto &gate = gates.back();

        // Add raw line to gate
        gate.raw = line;

        // Check the first token is actually a number
        if (not is_integer(tokens[0]))
          throw error_msg("First token must be a valid cycle number.");
        gate.cycle = std::stol(tokens[0]);

        // Check that cycle number is monotonically increasing
        if (gate.cycle < last_cycle_number)
          throw error_msg("Cycle number can only increase.");

        // If cycle number change, reset used qubits
        if (gate.cycle != last_cycle_number) {
          used_qubits.clear();
          last_cycle_number = gate.cycle;
        }

        // Get gate name and params
        if (std::size_t beg = tokens[1].find_first_of('(');
            beg != std::string::npos) {
          gate.name = tokens[1].substr(0, beg);
          std::string params =
              tokens[1].substr(beg + 1, tokens[1].find_first_of(')') - beg - 1);
          for (const auto &p : tokenize(params, "[^,]+")) {
            if (not is_number(p))
              throw ERROR_MSG("Params must be valid numbers.");
            gate.params.push_back(std::stod(p));
          }
        } else {
          gate.name = tokens[1];
        }

        // Add all the qubits
        for (std::size_t i = 2; i < std::size(tokens); ++i) {
          if (not is_integer(tokens[i]))
            throw error_msg("Qubit must be a valid number.");
          gate.qubits.push_back(std::stol(tokens[i]));
        }

        // Check that qubits are not already used in the same cycle
        for (const auto &qubit : gate.qubits)
          if (used_qubits.find(qubit) != std::end(used_qubits))
            throw error_msg("Qubits can only used one for each cycle");
          else
            used_qubits.insert(qubit);
      }
    }

    // Increment line counter
    ++line_counter;
  }

  // Compute circuit depth
  // TODO: have scratch space be allocated not relying on depth.
  {
    std::unordered_map<std::size_t,
                       std::unordered_map<std::size_t, std::size_t>>
        layers;
    for (const auto &gate : this->gates) {
      if (std::size(gate.qubits) > 2)
        throw error_msg(
            "Depth calculation does not handle k-qubit gates with k > 2.");

      if (std::size(gate.qubits) == 2) {
        auto q1 = gate.qubits[0];
        auto q2 = gate.qubits[1];
        if (q2 > q1) std::swap(q1, q2);

        size_t pair_depth_increment = 1;
        if (gate.name == "fsim") pair_depth_increment = 2;
        layers[q1][q2] += pair_depth_increment;
        size_t new_depth = layers[q1][q2];
        if (new_depth > depth) this->depth = new_depth;
      }
    }
  }
}

void QflexCircuit::load(const std::string &filename) {
  if (auto in = std::ifstream(filename); in.good())
    this->load(in);
  else
    throw ERROR_MSG("Cannot open circuit file ", filename, ".");
};

}  // namespace qflex
<commit_msg>fix format<commit_after>#include "circuit.h"

namespace qflex {

std::ostream &QflexGate::operator<<(std::ostream &out) const {
  out << "gate_name: " << this->name << std::endl;
  out << "qubits: ";
  for (const auto &q : this->qubits) out << q << " ";
  out << std::endl;
  if (std::size(this->params)) {
    out << "params: ";
    for (const auto &p : this->params) out << p << " ";
    out << std::endl;
  }
  return out;
}
std::ostream &operator<<(std::ostream &out, const QflexGate &gate) {
  return gate.operator<<(out);
}

void QflexCircuit::clear() {
  this->num_active_qubits = 0;
  this->depth = 0;
  gates.clear();
}

void QflexCircuit::load(std::istream &istream) {
  this->load(std::move(istream));
}
void QflexCircuit::load(std::istream &&istream) {
  // Check if token is a number
  auto is_number = [](const std::string &token) {
    try {
      std::stol(token);
    } catch (...) {
      return false;
    }
    return true;
  };

  // Check if token is an integer
  auto is_integer = [&is_number](const std::string &token) {
    return is_number(token) and std::stol(token) == std::stod(token);
  };

  // Given a line, strip everything that doesn't follow the right format
  auto strip_line = [](std::string line) {
    // Remove everything after '#'
    line = std::regex_replace(line, std::regex("#.*"), "");

    // Remove any special character
    line = std::regex_replace(line, std::regex("[^)(\\s\\ta-zA-Z0-9_.,-]"), "");

    // Convert tabs to spaces
    line = std::regex_replace(line, std::regex("[\\t]"), " ");

    // Remove multiple spaces
    line = std::regex_replace(line, std::regex("[\\s]{2,}"), " ");

    // Remove last space
    line = std::regex_replace(line, std::regex("\\s+$"), "");

    // Remove any space between a non-space char and '('
    line = std::regex_replace(line, std::regex("[\\s]+[(]"), "(");

    // Remove spaces between parentheses
    line = std::regex_replace(line, std::regex("\\s+(?=[^()]*\\))"), "");

    // After stripping, line should follow the format
    // 0 gate(p1,p2,...) q1 q2 ...

    return line;
  };

  // Given a line, tokenize it
  auto tokenize = [](const std::string &line,
                     const std::string &regex_expr = "[^\\s]+") {
    std::vector<std::string> tokens;
    auto word_regex = std::regex(regex_expr);
    for (auto w =
             std::sregex_iterator(std::begin(line), std::end(line), word_regex);
         w != std::sregex_iterator(); ++w)
      tokens.push_back(w->str());
    return tokens;
  };

  // Clear this circuit
  this->clear();

  std::size_t line_counter{0}, last_cycle_number{0};
  std::unordered_set<std::size_t> used_qubits;
  std::string line;

  auto error_msg = [&line, &line_counter](const std::string &msg) {
    return ERROR_MSG("[", std::to_string(line_counter + 1), ": ", line, "] ",
                     msg);
  };

  while (std::getline(istream, line)) {
    if (std::size(line = strip_line(line))) {
      // Check number of parentheses
      if (std::size_t n_open =
              std::count(std::begin(line), std::end(line), '('),
          n_close = std::count(std::begin(line), std::end(line), ')');
          n_open != n_close or n_open > 1)
        throw error_msg("Not-matching parentheses.");

      // Tokenize the line
      auto tokens = tokenize(line);

      // Enforce first line to be a single number which correspond to the number
      // of qubits
      if (line_counter == 0) {
        if (std::size(tokens) != 1 or std::stol(tokens[0]) <= 0)
          throw error_msg(
              "First line in circuit must be the number of active qubits.");
        this->num_active_qubits = std::stol(tokens[0]);
      } else {
        // Check the correct number of tokens
        if (std::size(tokens) < 3)
          throw error_msg(
              "Gate must be specified as: cycle gate_name[(p1[,p2,...])] q1 "
              "[q2, ...]");

        // Get gate
        this->gates.emplace_back();
        auto &gate = gates.back();

        // Add raw line to gate
        gate.raw = line;

        // Check the first token is actually a number
        if (not is_integer(tokens[0]))
          throw error_msg("First token must be a valid cycle number.");
        gate.cycle = std::stol(tokens[0]);

        // Check that cycle number is monotonically increasing
        if (gate.cycle < last_cycle_number)
          throw error_msg("Cycle number can only increase.");

        // If cycle number change, reset used qubits
        if (gate.cycle != last_cycle_number) {
          used_qubits.clear();
          last_cycle_number = gate.cycle;
        }

        // Get gate name and params
        if (std::size_t beg = tokens[1].find_first_of('(');
            beg != std::string::npos) {
          gate.name = tokens[1].substr(0, beg);
          std::string params =
              tokens[1].substr(beg + 1, tokens[1].find_first_of(')') - beg - 1);
          for (const auto &p : tokenize(params, "[^,]+")) {
            if (not is_number(p))
              throw ERROR_MSG("Params must be valid numbers.");
            gate.params.push_back(std::stod(p));
          }
        } else {
          gate.name = tokens[1];
        }

        // Add all the qubits
        for (std::size_t i = 2; i < std::size(tokens); ++i) {
          if (not is_integer(tokens[i]))
            throw error_msg("Qubit must be a valid number.");
          gate.qubits.push_back(std::stol(tokens[i]));
        }

        // Check that qubits are not already used in the same cycle
        for (const auto &qubit : gate.qubits)
          if (used_qubits.find(qubit) != std::end(used_qubits))
            throw error_msg("Qubits can only used one for each cycle");
          else
            used_qubits.insert(qubit);
      }
    }

    // Increment line counter
    ++line_counter;
  }

  // Compute circuit depth
  // TODO: have scratch space be allocated not relying on depth.
  {
    std::unordered_map<std::size_t,
                       std::unordered_map<std::size_t, std::size_t>>
        layers;
    for (const auto &gate : this->gates) {
      if (std::size(gate.qubits) > 2)
        throw error_msg(
            "Depth calculation does not handle k-qubit gates with k > 2.");

      if (std::size(gate.qubits) == 2) {
        auto q1 = gate.qubits[0];
        auto q2 = gate.qubits[1];
        if (q2 > q1) std::swap(q1, q2);

        size_t pair_depth_increment = 1;
        if (gate.name == "fsim") pair_depth_increment = 2;
        layers[q1][q2] += pair_depth_increment;
        size_t new_depth = layers[q1][q2];
        if (new_depth > depth) this->depth = new_depth;
      }
    }
  }
}

void QflexCircuit::load(const std::string &filename) {
  if (auto in = std::ifstream(filename); in.good())
    this->load(in);
  else
    throw ERROR_MSG("Cannot open circuit file ", filename, ".");
};

}  // namespace qflex
<|endoftext|>