From d335527a7000b10b425bc7b7b19f50a084d156d3 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Wed, 5 Oct 2022 15:05:25 -0300 Subject: [PATCH 1/5] Add binary file slurp/dump utility functions We have basically this same bit of code in tons of places; consolidate it into llarp::util::slurp_file/llarp::util::dump_file. Also renames all the extra junk that crept into llarp/util/fs.hpp out of there into llarp/util/file.hpp instead. --- llarp/CMakeLists.txt | 2 +- llarp/config/config.cpp | 35 ++++++----- llarp/config/ini.cpp | 20 +++---- llarp/config/ini.hpp | 6 +- llarp/crypto/types.cpp | 67 ++++++++++----------- llarp/nodedb.cpp | 1 - llarp/router_contact.cpp | 48 +++++++-------- llarp/service/identity.cpp | 40 ++++++------- llarp/util/bencode.hpp | 38 ++++++------ llarp/util/file.cpp | 119 +++++++++++++++++++++++++++++++++++++ llarp/util/file.hpp | 103 ++++++++++++++++++++++++++++++++ llarp/util/fs.cpp | 76 ----------------------- llarp/util/fs.hpp | 71 ---------------------- 13 files changed, 345 insertions(+), 281 deletions(-) create mode 100644 llarp/util/file.cpp create mode 100644 llarp/util/file.hpp delete mode 100644 llarp/util/fs.cpp diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index b794f6ead..d1d0196ef 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -5,7 +5,7 @@ add_library(lokinet-util ${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp util/bencode.cpp util/buffer.cpp - util/fs.cpp + util/file.cpp util/json.cpp util/logging/buffer.cpp util/easter_eggs.cpp diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 3cc0c8004..d0f9f567e 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -19,7 +19,6 @@ #include #include -#include #include #include @@ -1434,18 +1433,19 @@ namespace llarp bool Config::Load(std::optional fname, bool isRelay) { - std::vector ini{}; + std::string ini; if (fname) { - if (not fs::exists(*fname)) + try + { + ini = util::slurp_file(*fname); + } + catch (const std::exception&) + { return false; - fs::ifstream inf{*fname, std::ios::in | std::ios::binary}; - auto sz = inf.seekg(0, std::ios::end).tellg(); - inf.seekg(0, std::ios::beg); - ini.resize(sz); - inf.read(ini.data(), ini.size()); + } } - return LoadConfigData(std::string_view{ini.data(), ini.size()}, fname, isRelay); + return LoadConfigData(ini, fname, isRelay); } bool @@ -1521,12 +1521,15 @@ namespace llarp confStr = config.generateBaseClientConfig(); // open a filestream - auto stream = llarp::util::OpenFileStream(confFile.c_str(), std::ios::binary); - if (not stream or not stream->is_open()) - throw std::runtime_error{fmt::format("Failed to open file {} for writing", confFile)}; - - *stream << confStr; - stream->flush(); + try + { + util::dump_file(confFile, confStr); + } + catch (const std::exception& e) + { + throw std::runtime_error{ + fmt::format("Failed to write config data to {}: {}", confFile, e.what())}; + } llarp::LogInfo("Generated new config ", confFile); } diff --git a/llarp/config/ini.cpp b/llarp/config/ini.cpp index 0769112ba..72cff9f61 100644 --- a/llarp/config/ini.cpp +++ b/llarp/config/ini.cpp @@ -13,19 +13,19 @@ namespace llarp { bool - ConfigParser::LoadFile(const fs::path fname) + ConfigParser::LoadFile(const fs::path& fname) { + try { - std::ifstream f(fname, std::ios::in | std::ios::binary); - if (not f.is_open()) - return false; - f.seekg(0, std::ios::end); - m_Data.resize(f.tellg()); - f.seekg(0, std::ios::beg); - if (m_Data.size() == 0) - return false; - f.read(m_Data.data(), m_Data.size()); + m_Data = util::slurp_file(fname); } + catch (const std::exception& e) + { + return false; + } + if (m_Data.empty()) + return false; + m_FileName = fname; return Parse(); } diff --git a/llarp/config/ini.hpp b/llarp/config/ini.hpp index 9106e30b1..847e77c94 100644 --- a/llarp/config/ini.hpp +++ b/llarp/config/ini.hpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include namespace llarp { @@ -22,7 +22,7 @@ namespace llarp /// return true on success /// return false on error bool - LoadFile(const fs::path fname); + LoadFile(const fs::path& fname); /// load from string /// return true on success @@ -57,7 +57,7 @@ namespace llarp bool Parse(); - std::vector m_Data; + std::string m_Data; Config_impl_t m_Config; std::unordered_map m_Overrides; fs::path m_FileName; diff --git a/llarp/crypto/types.cpp b/llarp/crypto/types.cpp index b2f190b83..5ed89c79c 100644 --- a/llarp/crypto/types.cpp +++ b/llarp/crypto/types.cpp @@ -1,9 +1,7 @@ #include "types.hpp" #include - -#include -#include +#include #include @@ -33,29 +31,25 @@ namespace llarp bool SecretKey::LoadFromFile(const fs::path& fname) { - std::ifstream f(fname.string(), std::ios::in | std::ios::binary); - if (!f.is_open()) + size_t sz; + std::array tmp; + try + { + sz = util::slurp_file(fname, tmp.data(), tmp.size()); + } + catch (const std::exception&) { return false; } - f.seekg(0, std::ios::end); - const size_t sz = f.tellg(); - f.seekg(0, std::ios::beg); - if (sz == size()) { // is raw buffer - std::copy_n(std::istreambuf_iterator(f), sz, begin()); + std::copy_n(tmp.begin(), sz, begin()); return true; } - std::array tmp; + llarp_buffer_t buf(tmp); - if (sz > sizeof(tmp)) - { - return false; - } - f.read(reinterpret_cast(tmp.data()), sz); return BDecode(&buf); } @@ -95,38 +89,43 @@ namespace llarp bool SecretKey::SaveToFile(const fs::path& fname) const { - std::array tmp; + std::string tmp(128, 0); llarp_buffer_t buf(tmp); if (!BEncode(&buf)) - { return false; + + tmp.resize(buf.cur - buf.base); + try + { + util::dump_file(fname, tmp); } - auto optional_f = llarp::util::OpenFileStream(fname, std::ios::binary); - if (!optional_f) - return false; - auto& f = *optional_f; - if (!f.is_open()) + catch (const std::exception&) + { return false; - f.write((char*)buf.base, buf.cur - buf.base); - return f.good(); + } + return true; } bool IdentitySecret::LoadFromFile(const fs::path& fname) { - auto optional = util::OpenFileStream(fname, std::ios::binary | std::ios::in); - if (!optional) + std::array buf; + size_t sz; + try + { + sz = util::slurp_file(fname, buf.data(), buf.size()); + } + catch (const std::exception& e) + { + llarp::LogError("failed to load service node seed: ", e.what()); return false; - auto& f = *optional; - f.seekg(0, std::ios::end); - const size_t sz = f.tellg(); - f.seekg(0, std::ios::beg); - if (sz != 32) + } + if (sz != SIZE) { - llarp::LogError("service node seed size invalid: ", sz, " != 32"); + llarp::LogError("service node seed size invalid: ", sz, " != ", SIZE); return false; } - std::copy_n(std::istreambuf_iterator(f), sz, begin()); + std::copy(buf.begin(), buf.end(), begin()); return true; } diff --git a/llarp/nodedb.cpp b/llarp/nodedb.cpp index ceef0c95e..a9d044807 100644 --- a/llarp/nodedb.cpp +++ b/llarp/nodedb.cpp @@ -12,7 +12,6 @@ #include "dht/kademlia.hpp" #include -#include #include #include diff --git a/llarp/router_contact.cpp b/llarp/router_contact.cpp index e06c8101f..598aa1753 100644 --- a/llarp/router_contact.cpp +++ b/llarp/router_contact.cpp @@ -11,11 +11,12 @@ #include -#include -#include "util/fs.hpp" +#include "util/file.hpp" namespace llarp { + static auto logcat = log::Cat("RC"); + NetID& NetID::DefaultValue() { @@ -290,13 +291,13 @@ namespace llarp } else { - llarp::LogWarn("Received RouterContact with unkown version (", outer_version, ")"); + log::warning(logcat, "Received RouterContact with unkown version ({})", outer_version); return false; } } catch (const std::exception& e) { - llarp::LogDebug("RouterContact::BDecode failed, reason: ", e.what()); + log::debug(logcat, "RouterContact::BDecode failed: {}", e.what()); } return false; @@ -316,14 +317,14 @@ namespace llarp if (not btlist.is_finished()) { - llarp::LogDebug("RouterContact serialized list too long for specified version."); + log::debug(logcat, "RouterContact serialized list too long for specified version."); return false; } llarp_buffer_t sigbuf(signature_string.data(), signature_string.size()); if (not signature.FromBytestring(&sigbuf)) { - llarp::LogDebug("RouterContact serialized signature had invalid length."); + log::debug(logcat, "RouterContact serialized signature had invalid length."); return false; } @@ -477,8 +478,8 @@ namespace llarp { if (netID != NetID::DefaultValue()) { - llarp::LogError( - "netid mismatch: '", netID, "' (theirs) != '", NetID::DefaultValue(), "' (ours)"); + log::error( + logcat, "netid mismatch: '{}' (theirs) != '{}' (ours)", netID, NetID::DefaultValue()); return false; } @@ -491,13 +492,13 @@ namespace llarp { if (net->IsBogon(a.ip) && BlockBogons) { - llarp::LogError("invalid address info: ", a); + log::error(logcat, "invalid address info: {}", a); return false; } } if (!VerifySignature()) { - llarp::LogError("invalid signature: ", *this); + log::error(logcat, "invalid signature: {}", *this); return false; } return true; @@ -515,7 +516,7 @@ namespace llarp llarp_buffer_t buf(tmp); if (!copy.BEncode(&buf)) { - llarp::LogError("bencode failed"); + log::error(logcat, "bencode failed"); return false; } buf.sz = buf.cur - buf.base; @@ -541,18 +542,15 @@ namespace llarp { return false; } - buf.sz = buf.cur - buf.base; - buf.cur = buf.base; - auto f = llarp::util::OpenFileStream(fname, std::ios::binary); - if (!f) + try { - return false; + util::dump_file(fname, tmp.data(), buf.cur - buf.base); } - if (!f->is_open()) + catch (const std::exception& e) { + log::error(logcat, "Failed to write RC to {}: {}", fname, e.what()); return false; } - f->write((char*)buf.base, buf.sz); return true; } @@ -561,21 +559,15 @@ namespace llarp { std::array tmp; llarp_buffer_t buf(tmp); - std::ifstream f; - f.open(fname.string(), std::ios::binary); - if (!f.is_open()) + try { - llarp::LogError("Failed to open ", fname); - return false; + util::slurp_file(fname, tmp.data(), tmp.size()); } - f.seekg(0, std::ios::end); - auto l = f.tellg(); - if (l > static_cast(sizeof tmp)) + catch (const std::exception& e) { + log::error(logcat, "Failed to read RC from {}: {}", fname, e.what()); return false; } - f.seekg(0, std::ios::beg); - f.read((char*)tmp.data(), l); return BDecode(&buf); } diff --git a/llarp/service/identity.cpp b/llarp/service/identity.cpp index 0d106c24a..f54a025a5 100644 --- a/llarp/service/identity.cpp +++ b/llarp/service/identity.cpp @@ -98,17 +98,16 @@ namespace llarp RegenerateKeys(); if (!BEncode(&buf)) throw std::length_error("failed to encode new identity"); - // rewind - buf.sz = buf.cur - buf.base; - buf.cur = buf.base; + const auto sz = buf.cur - buf.base; // write - auto optional_f = util::OpenFileStream(fname, std::ios::binary); - if (!optional_f) - throw std::runtime_error{fmt::format("can not open {}", fname)}; - auto& f = *optional_f; - if (!f.is_open()) - throw std::runtime_error{fmt::format("did not open {}", fname)}; - f.write((char*)buf.cur, buf.sz); + try + { + util::dump_file(fname, tmp.data(), sz); + } + catch (const std::exception& e) + { + throw std::runtime_error{fmt::format("failed to write {}: {}", fname, e.what())}; + } } if (not fs::is_regular_file(fname)) @@ -117,17 +116,18 @@ namespace llarp } // read file - std::ifstream inf(fname, std::ios::binary); - inf.seekg(0, std::ios::end); - size_t sz = inf.tellg(); - inf.seekg(0, std::ios::beg); - - if (sz > sizeof(tmp)) - throw std::length_error("service identity too big"); - // decode - inf.read((char*)buf.base, sz); + try + { + util::slurp_file(fname, tmp.data(), tmp.size()); + } + catch (const std::length_error&) + { + throw std::length_error{"service identity too big"}; + } + // (don't catch io error exceptions) + if (!bencode_decode_dict(*this, &buf)) - throw std::length_error("could not decode service identity"); + throw std::length_error{"could not decode service identity"}; auto crypto = CryptoManager::instance(); diff --git a/llarp/util/bencode.hpp b/llarp/util/bencode.hpp index 24f6a520b..f0344b2d8 100644 --- a/llarp/util/bencode.hpp +++ b/llarp/util/bencode.hpp @@ -2,12 +2,11 @@ #include "buffer.hpp" #include "bencode.h" -#include "fs.hpp" +#include "file.hpp" #include #include "mem.hpp" #include -#include #include #include @@ -338,21 +337,16 @@ namespace llarp bool BDecodeReadFile(const fs::path fpath, T& t) { - std::vector ptr; + std::string content; + try { - std::ifstream f; - f.open(fpath.string()); - if (!f.is_open()) - { - return false; - } - f.seekg(0, std::ios::end); - const std::streampos sz = f.tellg(); - f.seekg(0, std::ios::beg); - ptr.resize(sz); - f.read((char*)ptr.data(), sz); + content = util::slurp_file(fpath); + } + catch (const std::exception&) + { + return false; } - llarp_buffer_t buf(ptr); + llarp_buffer_t buf(content); return t.BDecode(&buf); } @@ -361,16 +355,18 @@ namespace llarp bool BEncodeWriteFile(const fs::path fpath, const T& t) { - std::array tmp; + std::string tmp(bufsz, 0); llarp_buffer_t buf(tmp); if (!t.BEncode(&buf)) return false; - buf.sz = buf.cur - buf.base; + tmp.resize(buf.cur - buf.base); + try { - auto f = llarp::util::OpenFileStream(fpath, std::ios::binary); - if (not f or not f->is_open()) - return false; - f->write((char*)buf.base, buf.sz); + util::dump_file(fpath, tmp); + } + catch (const std::exception& e) + { + return false; } return true; } diff --git a/llarp/util/file.cpp b/llarp/util/file.cpp new file mode 100644 index 000000000..dcfb386cb --- /dev/null +++ b/llarp/util/file.cpp @@ -0,0 +1,119 @@ +#include "file.hpp" +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#ifdef WIN32 +#include +#else +#include +#endif + +namespace llarp::util +{ + static std::pair + slurp_file_open(const fs::path& filename) + { + std::pair f; + auto& [in, size] = f; + in.exceptions(std::ifstream::failbit | std::ifstream::badbit); + in.open(filename, std::ios::binary | std::ios::in); + in.seekg(0, std::ios::end); + size = in.tellg(); + in.seekg(0, std::ios::beg); + return f; + } + + std::string + slurp_file(const fs::path& filename) + { + std::string contents; + auto [in, size] = slurp_file_open(filename); + contents.resize(size); + in.read(contents.data(), size); + return contents; + } + + size_t + slurp_file(const fs::path& filename, char* buffer, size_t buffer_size) + { + auto [in, size] = slurp_file_open(filename); + if (static_cast(size) > buffer_size) + throw std::length_error{"file is too large for buffer"}; + in.read(buffer, size); + return size; + } + + void + dump_file(const fs::path& filename, std::string_view contents) + { + fs::ofstream out; + out.exceptions(std::ifstream::failbit | std::ifstream::badbit); + out.open(filename, std::ios::binary | std::ios::out | std::ios::trunc); + out.write(contents.data(), static_cast(contents.size())); + } + + static std::error_code + errno_error() + { + int e = errno; + errno = 0; + return std::make_error_code(static_cast(e)); + } + + error_code_t + EnsurePrivateFile(fs::path pathname) + { + errno = 0; + error_code_t ec = errno_error(); + const auto str = pathname.string(); + if (fs::exists(pathname, ec)) // file exists + { + auto st = fs::status(pathname); + auto perms = st.permissions(); + if ((perms & fs::perms::others_exec) != fs::perms::none) + perms = perms ^ fs::perms::others_exec; + if ((perms & fs::perms::others_write) != fs::perms::none) + perms = perms ^ fs::perms::others_write; + if ((perms & fs::perms::others_write) != fs::perms::none) + perms = perms ^ fs::perms::others_write; + if ((perms & fs::perms::group_read) != fs::perms::none) + perms = perms ^ fs::perms::group_read; + if ((perms & fs::perms::others_read) != fs::perms::none) + perms = perms ^ fs::perms::others_read; + if ((perms & fs::perms::owner_exec) != fs::perms::none) + perms = perms ^ fs::perms::owner_exec; + + fs::permissions(pathname, perms, ec); + if (ec) + llarp::LogError("failed to set permissions on ", pathname); + } + else // file is not there + { + errno = 0; + int fd = ::open(str.c_str(), O_RDWR | O_CREAT, 0600); + ec = errno_error(); + if (fd != -1) + { + ::close(fd); + } + } + +#ifndef WIN32 + if (ec) + llarp::LogError("failed to ensure ", str, ", ", ec.message()); + return ec; +#else + return {}; +#endif + } + +} // namespace llarp::util diff --git a/llarp/util/file.hpp b/llarp/util/file.hpp new file mode 100644 index 000000000..a66b1555f --- /dev/null +++ b/llarp/util/file.hpp @@ -0,0 +1,103 @@ +#pragma once +#include "fs.hpp" + +#include +#include +#include +#include + +#ifndef _MSC_VER +#include +#endif + +namespace llarp::util +{ + /// Reads a binary file from disk into a string. Throws on error. + std::string + slurp_file(const fs::path& filename); + + /// Reads a binary file from disk directly into a buffer. Throws a std::length_error if the + /// file is bigger than the buffer. Returns the bytes copied on success. + size_t + slurp_file(const fs::path& filename, char* buffer, size_t buffer_size); + + /// Same, but for some non-char but single-byte char type (e.g. byte_t, std::byte, unsigned char). + template < + typename Char, + std::enable_if_t, int> = 1> + inline size_t + slurp_file(const fs::path& filename, Char* buffer, size_t buffer_size) + { + return slurp_file(filename, reinterpret_cast(buffer), buffer_size); + } + + /// Dumps binary string contents to disk. The file is overwritten if it already exists. Throws + /// on error. + void + dump_file(const fs::path& filename, std::string_view contents); + + /// Same as above, but works via char-like buffer + template = 0> + inline void + dump_file(const fs::path& filename, const Char* buffer, size_t buffer_size) + { + return dump_file( + filename, std::string_view{reinterpret_cast(buffer), buffer_size}); + } + + struct FileHash + { + size_t + operator()(const fs::path& f) const + { + std::hash h; + return h(f.string()); + } + }; + + using error_code_t = std::error_code; + + /// Ensure that a file exists and has correct permissions + /// return any error code or success + error_code_t + EnsurePrivateFile(fs::path pathname); + + /// open a stream to a file and ensure it exists before open + /// sets any permissions on creation + template + std::optional + OpenFileStream(fs::path pathname, std::ios::openmode mode) + { + if (EnsurePrivateFile(pathname)) + return {}; + return std::make_optional(pathname, mode); + } + + template + static void + IterDir(const fs::path& path, PathVisitor visit) + { + DIR* d = opendir(path.string().c_str()); + if (d == nullptr) + return; + struct dirent* ent = nullptr; + std::set entries; + do + { + ent = readdir(d); + if (not ent) + break; + if (ent->d_name[0] == '.') + continue; + entries.emplace(path / fs::path{ent->d_name}); + } while (ent); + closedir(d); + + for (const auto& p : entries) + { + if (not visit(p)) + return; + } + } + +} // namespace llarp::util diff --git a/llarp/util/fs.cpp b/llarp/util/fs.cpp deleted file mode 100644 index 95b345a58..000000000 --- a/llarp/util/fs.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "fs.hpp" - -#include -#include - -#include -#include -#include -#include - -#ifdef WIN32 -#include -#else -#include -#endif - -namespace llarp -{ - namespace util - { - static std::error_code - errno_error() - { - int e = errno; - errno = 0; - return std::make_error_code(static_cast(e)); - } - - error_code_t - EnsurePrivateFile(fs::path pathname) - { - errno = 0; - error_code_t ec = errno_error(); - const auto str = pathname.string(); - if (fs::exists(pathname, ec)) // file exists - { - auto st = fs::status(pathname); - auto perms = st.permissions(); - if ((perms & fs::perms::others_exec) != fs::perms::none) - perms = perms ^ fs::perms::others_exec; - if ((perms & fs::perms::others_write) != fs::perms::none) - perms = perms ^ fs::perms::others_write; - if ((perms & fs::perms::others_write) != fs::perms::none) - perms = perms ^ fs::perms::others_write; - if ((perms & fs::perms::group_read) != fs::perms::none) - perms = perms ^ fs::perms::group_read; - if ((perms & fs::perms::others_read) != fs::perms::none) - perms = perms ^ fs::perms::others_read; - if ((perms & fs::perms::owner_exec) != fs::perms::none) - perms = perms ^ fs::perms::owner_exec; - - fs::permissions(pathname, perms, ec); - if (ec) - llarp::LogError("failed to set permissions on ", pathname); - } - else // file is not there - { - errno = 0; - int fd = ::open(str.c_str(), O_RDWR | O_CREAT, 0600); - ec = errno_error(); - if (fd != -1) - { - ::close(fd); - } - } - -#ifndef WIN32 - if (ec) - llarp::LogError("failed to ensure ", str, ", ", ec.message()); - return ec; -#else - return {}; -#endif - } - } // namespace util -} // namespace llarp diff --git a/llarp/util/fs.hpp b/llarp/util/fs.hpp index c62c0766e..09399646d 100644 --- a/llarp/util/fs.hpp +++ b/llarp/util/fs.hpp @@ -1,8 +1,5 @@ #pragma once -#include -#include - #ifdef USE_GHC_FILESYSTEM #include namespace fs = ghc::filesystem; @@ -15,72 +12,4 @@ namespace fs using ofstream = std::ofstream; using fstream = std::fstream; } // namespace fs - -#endif - -#ifndef _MSC_VER -#include #endif - -#include - -namespace llarp -{ - namespace util - { - struct FileHash - { - size_t - operator()(const fs::path& f) const - { - std::hash h; - return h(f.string()); - } - }; - - using error_code_t = std::error_code; - - /// Ensure that a file exists and has correct permissions - /// return any error code or success - error_code_t - EnsurePrivateFile(fs::path pathname); - - /// open a stream to a file and ensure it exists before open - /// sets any permissions on creation - template - std::optional - OpenFileStream(fs::path pathname, std::ios::openmode mode) - { - if (EnsurePrivateFile(pathname)) - return {}; - return std::make_optional(pathname, mode); - } - - template - static void - IterDir(const fs::path& path, PathVisitor visit) - { - DIR* d = opendir(path.string().c_str()); - if (d == nullptr) - return; - struct dirent* ent = nullptr; - std::set entries; - do - { - ent = readdir(d); - if (not ent) - break; - if (ent->d_name[0] == '.') - continue; - entries.emplace(path / fs::path{ent->d_name}); - } while (ent); - closedir(d); - - for (const auto& p : entries) - { - if (not visit(p)) - return; - } - }; - } // namespace util -} // namespace llarp From f641c08e804bfdc2804f6053a070b4e792cf1306 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Wed, 5 Oct 2022 16:35:16 -0300 Subject: [PATCH 2/5] llarp/profiling: refactor to use oxenc producer/consumer No more llarp_buffer_t here! (I was tracking down a segfault which led me in here and it was easier to rewrite this to use bt_dict_{consumer,producer} than to decipher all the cursed llarp_buffer_t and bencode callback nest). --- llarp/profiling.cpp | 213 ++++++++++++++++++++--------------------- llarp/profiling.hpp | 38 +++++--- llarp/util/aligned.hpp | 6 ++ 3 files changed, 136 insertions(+), 121 deletions(-) diff --git a/llarp/profiling.cpp b/llarp/profiling.cpp index ce57f42fd..45af9b9ff 100644 --- a/llarp/profiling.cpp +++ b/llarp/profiling.cpp @@ -1,53 +1,51 @@ #include "profiling.hpp" +#include +#include -#include -#include "util/fs.hpp" +#include "util/file.hpp" +#include "util/logging.hpp" + +using oxenc::bt_dict_consumer; +using oxenc::bt_dict_producer; namespace llarp { - bool - RouterProfile::BEncode(llarp_buffer_t* buf) const - { - if (!bencode_start_dict(buf)) - return false; + static auto logcat = log::Cat("profiling"); - if (!BEncodeWriteDictInt("g", connectGoodCount, buf)) - return false; - if (!BEncodeWriteDictInt("p", pathSuccessCount, buf)) - return false; - if (!BEncodeWriteDictInt("q", pathTimeoutCount, buf)) - return false; - if (!BEncodeWriteDictInt("s", pathFailCount, buf)) - return false; - if (!BEncodeWriteDictInt("t", connectTimeoutCount, buf)) - return false; - if (!BEncodeWriteDictInt("u", lastUpdated.count(), buf)) - return false; - if (!BEncodeWriteDictInt("v", version, buf)) - return false; + RouterProfile::RouterProfile(bt_dict_consumer dict) + { + BDecode(std::move(dict)); + } - return bencode_end(buf); + void + RouterProfile::BEncode(bt_dict_producer& dict) const + { + dict.append("g", connectGoodCount); + dict.append("p", pathSuccessCount); + dict.append("q", pathTimeoutCount); + dict.append("s", pathFailCount); + dict.append("t", connectTimeoutCount); + dict.append("u", lastUpdated.count()); + dict.append("v", version); } - bool - RouterProfile::DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf) + void + RouterProfile::BDecode(bt_dict_consumer dict) { - bool read = false; - if (!BEncodeMaybeReadDictInt("g", connectGoodCount, read, k, buf)) - return false; - if (!BEncodeMaybeReadDictInt("t", connectTimeoutCount, read, k, buf)) - return false; - if (!BEncodeMaybeReadDictInt("u", lastUpdated, read, k, buf)) - return false; - if (!BEncodeMaybeReadDictInt("v", version, read, k, buf)) - return false; - if (!BEncodeMaybeReadDictInt("s", pathFailCount, read, k, buf)) - return false; - if (!BEncodeMaybeReadDictInt("p", pathSuccessCount, read, k, buf)) - return false; - if (!BEncodeMaybeReadDictInt("q", pathTimeoutCount, read, k, buf)) - return false; - return read; + if (dict.skip_until("g")) + connectGoodCount = dict.consume_integer(); + if (dict.skip_until("p")) + pathSuccessCount = dict.consume_integer(); + if (dict.skip_until("q")) + pathTimeoutCount = dict.consume_integer(); + if (dict.skip_until("s")) + pathFailCount = dict.consume_integer(); + if (dict.skip_until("t")) + connectTimeoutCount = dict.consume_integer(); + if (dict.skip_until("u")) + lastUpdated = llarp_time_t{dict.consume_integer()}; + if (dict.skip_until("v")) + version = dict.consume_integer(); } void @@ -156,23 +154,26 @@ namespace llarp Profiling::Tick() { util::Lock lock(m_ProfilesMutex); - std::for_each(m_Profiles.begin(), m_Profiles.end(), [](auto& item) { item.second.Tick(); }); + for (auto& [rid, profile] : m_Profiles) + profile.Tick(); } void Profiling::MarkConnectTimeout(const RouterID& r) { util::Lock lock{m_ProfilesMutex}; - m_Profiles[r].connectTimeoutCount += 1; - m_Profiles[r].lastUpdated = llarp::time_now_ms(); + auto& profile = m_Profiles[r]; + profile.connectTimeoutCount += 1; + profile.lastUpdated = llarp::time_now_ms(); } void Profiling::MarkConnectSuccess(const RouterID& r) { util::Lock lock{m_ProfilesMutex}; - m_Profiles[r].connectGoodCount += 1; - m_Profiles[r].lastUpdated = llarp::time_now_ms(); + auto& profile = m_Profiles[r]; + profile.connectGoodCount += 1; + profile.lastUpdated = llarp::time_now_ms(); } void @@ -186,24 +187,27 @@ namespace llarp Profiling::MarkHopFail(const RouterID& r) { util::Lock lock{m_ProfilesMutex}; - m_Profiles[r].pathFailCount += 1; - m_Profiles[r].lastUpdated = llarp::time_now_ms(); + auto& profile = m_Profiles[r]; + profile.pathFailCount += 1; + profile.lastUpdated = llarp::time_now_ms(); } void Profiling::MarkPathFail(path::Path* p) { util::Lock lock{m_ProfilesMutex}; - size_t idx = 0; + bool first = true; for (const auto& hop : p->hops) { // don't mark first hop as failure because we are connected to it directly - if (idx) + if (first) + first = false; + else { - m_Profiles[hop.rc.pubkey].pathFailCount += 1; - m_Profiles[hop.rc.pubkey].lastUpdated = llarp::time_now_ms(); + auto& profile = m_Profiles[hop.rc.pubkey]; + profile.pathFailCount += 1; + profile.lastUpdated = llarp::time_now_ms(); } - ++idx; } } @@ -213,8 +217,9 @@ namespace llarp util::Lock lock{m_ProfilesMutex}; for (const auto& hop : p->hops) { - m_Profiles[hop.rc.pubkey].pathTimeoutCount += 1; - m_Profiles[hop.rc.pubkey].lastUpdated = llarp::time_now_ms(); + auto& profile = m_Profiles[hop.rc.pubkey]; + profile.pathTimeoutCount += 1; + profile.lastUpdated = llarp::time_now_ms(); } } @@ -225,88 +230,82 @@ namespace llarp const auto sz = p->hops.size(); for (const auto& hop : p->hops) { + auto& profile = m_Profiles[hop.rc.pubkey]; // redeem previous fails by halfing the fail count and setting timeout to zero - m_Profiles[hop.rc.pubkey].pathFailCount /= 2; - m_Profiles[hop.rc.pubkey].pathTimeoutCount = 0; + profile.pathFailCount /= 2; + profile.pathTimeoutCount = 0; // mark success at hop - m_Profiles[hop.rc.pubkey].pathSuccessCount += sz; - m_Profiles[hop.rc.pubkey].lastUpdated = llarp::time_now_ms(); + profile.pathSuccessCount += sz; + profile.lastUpdated = llarp::time_now_ms(); } } bool Profiling::Save(const fs::path fpath) { - const size_t sz = (m_Profiles.size() * (RouterProfile::MaxSize + 32 + 8)) + 8; - - std::vector tmp(sz, 0); - llarp_buffer_t buf(tmp); - + std::string buf; { util::Lock lock{m_ProfilesMutex}; - if (not BEncode(&buf)) + buf.resize((m_Profiles.size() * (RouterProfile::MaxSize + 32 + 8)) + 8); + bt_dict_producer d{buf.data(), buf.size()}; + try + { + BEncode(d); + } + catch (const std::exception& e) + { + log::warning(logcat, "Failed to encode profiling data: {}", e.what()); return false; + } + buf.resize(d.end() - buf.data()); } - buf.sz = buf.cur - buf.base; - auto optional_f = util::OpenFileStream(fpath, std::ios::binary); - if (!optional_f) - return false; - auto& f = *optional_f; - if (not f.is_open()) + try + { + util::dump_file(fpath, buf); + } + catch (const std::exception& e) + { + log::warning(logcat, "Failed to save profiling data to {}: {}", fpath, e.what()); return false; + } - f.write(reinterpret_cast(buf.base), buf.sz); - if (not f.good()) - return false; m_LastSave = llarp::time_now_ms(); return true; } - bool - Profiling::BEncode(llarp_buffer_t* buf) const - { - if (!bencode_start_dict(buf)) - return false; - - auto itr = m_Profiles.begin(); - while (itr != m_Profiles.end()) - { - if (!itr->first.BEncode(buf)) - return false; - if (!itr->second.BEncode(buf)) - return false; - ++itr; - } - return bencode_end(buf); - } - - bool - Profiling::DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf) + void + Profiling::BEncode(bt_dict_producer& dict) const { - if (k.sz != 32) - return false; - RouterProfile profile; - if (!bencode_decode_dict(profile, buf)) - return false; - RouterID pk = k.base; - return m_Profiles.emplace(pk, profile).second; + for (const auto& [r_id, profile] : m_Profiles) + profile.BEncode(dict.append_dict(r_id.ToView())); } - bool - Profiling::BDecode(llarp_buffer_t* buf) + void + Profiling::BDecode(bt_dict_consumer dict) { - return bencode_decode_dict(*this, buf); + m_Profiles.clear(); + while (dict) + { + auto [rid, subdict] = dict.next_dict_consumer(); + if (rid.size() != RouterID::SIZE) + throw std::invalid_argument{"invalid RouterID"}; + m_Profiles.emplace(reinterpret_cast(rid.data()), subdict); + } } bool Profiling::Load(const fs::path fname) { - util::Lock lock{m_ProfilesMutex}; - m_Profiles.clear(); - if (!BDecodeReadFile(fname, *this)) + try + { + std::string data = util::slurp_file(fname); + util::Lock lock{m_ProfilesMutex}; + BDecode(bt_dict_consumer{data}); + } + catch (const std::exception& e) { - llarp::LogWarn("failed to load router profiles from ", fname); + log::warning(logcat, "failed to load router profiles from {}: {}", fname, e.what()); return false; } m_LastSave = llarp::time_now_ms(); diff --git a/llarp/profiling.hpp b/llarp/profiling.hpp index df26b6bcb..c23199b32 100644 --- a/llarp/profiling.hpp +++ b/llarp/profiling.hpp @@ -8,6 +8,12 @@ #include "util/thread/annotations.hpp" #include +namespace oxenc +{ + class bt_dict_consumer; + class bt_dict_producer; +} // namespace oxenc + namespace llarp { struct RouterProfile @@ -22,11 +28,19 @@ namespace llarp llarp_time_t lastDecay = 0s; uint64_t version = llarp::constants::proto_version; - bool - BEncode(llarp_buffer_t* buf) const; + RouterProfile() = default; + RouterProfile(oxenc::bt_dict_consumer dict); - bool - DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf); + void + BEncode(oxenc::bt_dict_producer& dict) const; + void + BEncode(oxenc::bt_dict_producer&& dict) const + { + BEncode(dict); + } + + void + BDecode(oxenc::bt_dict_consumer dict); bool IsGood(uint64_t chances) const; @@ -89,16 +103,6 @@ namespace llarp void Tick() EXCLUDES(m_ProfilesMutex); - bool - BEncode(llarp_buffer_t* buf) const; - - bool - BDecode(llarp_buffer_t* buf); - - bool - DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf) NO_THREAD_SAFETY_ANALYSIS; - // disabled because we do load -> bencode::BDecodeReadFromFile -> DecodeKey - bool Load(const fs::path fname) EXCLUDES(m_ProfilesMutex); @@ -115,6 +119,12 @@ namespace llarp Enable(); private: + void + BEncode(oxenc::bt_dict_producer& dict) const; + + void + BDecode(oxenc::bt_dict_consumer dict); + mutable util::Mutex m_ProfilesMutex; // protects m_Profiles std::map m_Profiles GUARDED_BY(m_ProfilesMutex); llarp_time_t m_LastSave = 0s; diff --git a/llarp/util/aligned.hpp b/llarp/util/aligned.hpp index 92dcf38a4..fb756d562 100644 --- a/llarp/util/aligned.hpp +++ b/llarp/util/aligned.hpp @@ -260,6 +260,12 @@ namespace llarp return FromBytestring(&strbuf); } + std::string_view + ToView() const + { + return {reinterpret_cast(data()), sz}; + } + std::string ToHex() const { From 75e382604b5949d327ea335ee55a83bd9ab446c1 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Wed, 5 Oct 2022 16:48:59 -0300 Subject: [PATCH 3/5] Bump required oxenc version --- external/CMakeLists.txt | 2 +- external/oxen-encoding | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 1fcbc21ba..19ed9b05f 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -61,7 +61,7 @@ macro(system_or_submodule BIGNAME smallname pkgconf subdir) endif() endmacro() -system_or_submodule(OXENC oxenc liboxenc>=1.0.3 oxen-encoding) +system_or_submodule(OXENC oxenc liboxenc>=1.0.4 oxen-encoding) system_or_submodule(OXENMQ oxenmq liboxenmq>=1.2.12 oxen-mq) set(JSON_BuildTests OFF CACHE INTERNAL "") set(JSON_Install OFF CACHE INTERNAL "") diff --git a/external/oxen-encoding b/external/oxen-encoding index 79193e58f..707a83609 160000 --- a/external/oxen-encoding +++ b/external/oxen-encoding @@ -1 +1 @@ -Subproject commit 79193e58fb26624d40cd2e95156f78160f2b9b3e +Subproject commit 707a83609fb64d09b61ed1e56c82bf692050d2a1 From fae527517df56ac1a6f8a22a7737ef84cd5c094f Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Wed, 5 Oct 2022 17:06:01 -0300 Subject: [PATCH 4/5] Try to appease android's crappy toolchain --- llarp/util/file.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/llarp/util/file.cpp b/llarp/util/file.cpp index dcfb386cb..2cb914263 100644 --- a/llarp/util/file.cpp +++ b/llarp/util/file.cpp @@ -19,24 +19,23 @@ namespace llarp::util { - static std::pair - slurp_file_open(const fs::path& filename) + static std::streampos + slurp_file_open(const fs::path& filename, fs::ifstream& in) { - std::pair f; - auto& [in, size] = f; in.exceptions(std::ifstream::failbit | std::ifstream::badbit); in.open(filename, std::ios::binary | std::ios::in); in.seekg(0, std::ios::end); - size = in.tellg(); + auto size = in.tellg(); in.seekg(0, std::ios::beg); - return f; + return size; } std::string slurp_file(const fs::path& filename) { + fs::ifstream in; std::string contents; - auto [in, size] = slurp_file_open(filename); + auto size = slurp_file_open(filename, in); contents.resize(size); in.read(contents.data(), size); return contents; @@ -45,7 +44,8 @@ namespace llarp::util size_t slurp_file(const fs::path& filename, char* buffer, size_t buffer_size) { - auto [in, size] = slurp_file_open(filename); + fs::ifstream in; + auto size = slurp_file_open(filename, in); if (static_cast(size) > buffer_size) throw std::length_error{"file is too large for buffer"}; in.read(buffer, size); From 006394315de10c8c15ee9bca50601d245a361b01 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Wed, 5 Oct 2022 19:25:58 -0300 Subject: [PATCH 5/5] Add local mirror to most jobs; build script cleanups --- .drone.jsonnet | 25 ++++++++++++++++++------- contrib/android.sh | 2 +- contrib/cross.sh | 34 +++++++++++++++++----------------- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/.drone.jsonnet b/.drone.jsonnet index ef8494a68..c8b741f9c 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -27,7 +27,7 @@ local submodules = { }; // cmake options for static deps mirror -local ci_mirror_opts = '-DLOCAL_MIRROR=https://oxen.rocks/deps '; +local ci_dep_mirror(want_mirror) = (if want_mirror then ' -DLOCAL_MIRROR=https://oxen.rocks/deps ' else ''); local apt_get_quiet = 'apt-get -o=Dpkg::Use-Pty=0 -q'; @@ -40,6 +40,7 @@ local debian_pipeline(name, lto=false, werror=true, cmake_extra='', + local_mirror=true, extra_cmds=[], jobs=6, tests=true, @@ -80,7 +81,8 @@ local debian_pipeline(name, (if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + '-DWITH_LTO=' + (if lto then 'ON ' else 'OFF ') + '-DWITH_TESTS=' + (if tests then 'ON ' else 'OFF ') + - cmake_extra, + cmake_extra + + ci_dep_mirror(local_mirror), 'VERBOSE=1 make -j' + jobs, ] + (if tests then ['../contrib/ci/drone-gdb.sh ./test/testAll --use-colour yes'] else []) @@ -122,6 +124,7 @@ local windows_cross_pipeline(name, lto=false, werror=false, cmake_extra='', + local_mirror=true, gui_zip_url='', extra_cmds=[], jobs=6, @@ -147,7 +150,9 @@ local windows_cross_pipeline(name, 'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y p7zip-full build-essential cmake git pkg-config ccache g++-mingw-w64-x86-64-posix nsis zip automake libtool', 'update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix', 'update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix', - 'JOBS=' + jobs + ' VERBOSE=1 ./contrib/windows.sh ' + (if std.length(gui_zip_url) > 0 then '-DBUILD_GUI=OFF -DGUI_ZIP_URL=' + gui_zip_url else '') + ' -DSTRIP_SYMBOLS=ON ' + ci_mirror_opts, + 'JOBS=' + jobs + ' VERBOSE=1 ./contrib/windows.sh -DSTRIP_SYMBOLS=ON ' + + (if std.length(gui_zip_url) > 0 then '-DBUILD_GUI=OFF -DGUI_ZIP_URL=' + gui_zip_url else '') + + ci_dep_mirror(local_mirror), ] + extra_cmds, }, ], @@ -159,6 +164,7 @@ local linux_cross_pipeline(name, arch='amd64', build_type='Release', cmake_extra='', + local_mirror=true, extra_cmds=[], jobs=6, allow_fail=false) = { @@ -177,7 +183,8 @@ local linux_cross_pipeline(name, environment: { SSH_KEY: { from_secret: 'SSH_KEY' }, CROSS_TARGETS: std.join(':', cross_targets) }, commands: [ 'echo "Building on ${DRONE_STAGE_MACHINE}"', - 'VERBOSE=1 JOBS=' + jobs + ' ./contrib/cross.sh ' + std.join(' ', cross_targets) + (if std.length(cmake_extra) > 0 then ' -- ' + cmake_extra else ''), + 'VERBOSE=1 JOBS=' + jobs + ' ./contrib/cross.sh ' + std.join(' ', cross_targets) + + ' -- ' + cmake_extra + ci_dep_mirror(local_mirror), ], }, ], @@ -259,6 +266,7 @@ local mac_builder(name, build_type='Release', werror=true, cmake_extra='', + local_mirror=true, extra_cmds=[], jobs=6, codesign='-DCODESIGN=OFF', @@ -278,7 +286,10 @@ local mac_builder(name, // basic system headers. WTF apple: 'export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"', 'ulimit -n 1024', // because macos sets ulimit to 256 for some reason yeah idk - './contrib/mac-configure.sh ' + ci_mirror_opts + (if build_type == 'Debug' then ' -DWARN_DEPRECATED=OFF ' else '') + codesign, + './contrib/mac-configure.sh ' + + ci_dep_mirror(local_mirror) + + (if build_type == 'Debug' then ' -DWARN_DEPRECATED=OFF ' else '') + + codesign, 'cd build-mac', // We can't use the 'package' target here because making a .dmg requires an active logged in // macos gui to invoke Finder to invoke the partitioning tool to create a partitioned (!) @@ -378,7 +389,7 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = { lto=true, tests=false, oxen_repo=true, - cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' + ci_mirror_opts + + cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' + '-DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 ' + '-DCMAKE_CXX_FLAGS="-march=x86-64 -mtune=haswell" ' + '-DCMAKE_C_FLAGS="-march=x86-64 -mtune=haswell" ' + @@ -392,7 +403,7 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = { docker_base + 'debian-buster/arm32v7', arch='arm64', deps=['g++', 'python3-dev', 'automake', 'libtool'], - cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' + ci_mirror_opts + + cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' + '-DCMAKE_CXX_FLAGS="-march=armv7-a+fp -Wno-psabi" -DCMAKE_C_FLAGS="-march=armv7-a+fp" ' + '-DNATIVE_BUILD=OFF -DWITH_SYSTEMD=OFF -DWITH_BOOTSTRAP=OFF', extra_cmds=[ diff --git a/contrib/android.sh b/contrib/android.sh index 59b8944e0..bcb55d5a4 100755 --- a/contrib/android.sh +++ b/contrib/android.sh @@ -4,5 +4,5 @@ set +x root="$(readlink -f $(dirname $0)/../)" cd "$root" -./contrib/android-configure.sh . build-android $@ +./contrib/android-configure.sh . build-android "$@" make -C build-android -j ${JOBS:-$(nproc)} diff --git a/contrib/cross.sh b/contrib/cross.sh index 0b7d66328..2f4b3b611 100755 --- a/contrib/cross.sh +++ b/contrib/cross.sh @@ -15,27 +15,13 @@ root="$(readlink -e $(dirname $0)/../)" cd $root mkdir -p build-cross -cmake_opts="-DBUILD_STATIC_DEPS=ON \ - -DSTATIC_LINK=ON \ - -DBUILD_SHARED_LIBS=OFF \ - -DBUILD_TESTING=OFF \ - -DBUILD_LIBLOKINET=OFF \ - -DWITH_TESTS=OFF \ - -DNATIVE_BUILD=OFF \ - -DSTATIC_LINK=ON \ - -DWITH_SYSTEMD=OFF \ - -DFORCE_OXENMQ_SUBMODULE=ON \ - -DSUBMODULE_CHECK=OFF \ - -DWITH_LTO=OFF \ - -DWITH_BOOTSTRAP=OFF \ - -DCMAKE_BUILD_TYPE=RelWithDeb" - targets=() +cmake_extra=() while [ "$#" -gt 0 ]; do if [ "$1" = "--" ]; then shift - cmake_opts=$@ + cmake_extra=("$@") break fi targets+=("$1") @@ -55,7 +41,21 @@ for arch in $archs ; do -DCMAKE_EXE_LINKER_FLAGS=-fstack-protector \ -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always \ -DCMAKE_TOOLCHAIN_FILE=$root/contrib/cross/cross.toolchain.cmake \ - $cmake_opts \ + -DBUILD_STATIC_DEPS=ON \ + -DSTATIC_LINK=ON \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_TESTING=OFF \ + -DBUILD_LIBLOKINET=OFF \ + -DWITH_TESTS=OFF \ + -DNATIVE_BUILD=OFF \ + -DSTATIC_LINK=ON \ + -DWITH_SYSTEMD=OFF \ + -DFORCE_OXENMQ_SUBMODULE=ON \ + -DSUBMODULE_CHECK=OFF \ + -DWITH_LTO=OFF \ + -DWITH_BOOTSTRAP=OFF \ + -DCMAKE_BUILD_TYPE=RelWithDeb \ + "${cmake_extra[@]}" \ $root cd $root/build-cross echo -ne "$arch:\n\t\$(MAKE) -C build-$arch\n" >> $root/build-cross/Makefile