Compare commits

...

17 Commits

Author SHA1 Message Date
Jason Volk c6ce4d3229 appveyor: Skip extra builds for tags; minor cleanup. 2023-03-16 22:06:01 -07:00
Jason Volk 76def3378f ircd:Ⓜ️:vm::fetch: Add stats items. 2023-03-16 21:44:20 -07:00
Jason Volk 767f6cbae5 ircd:Ⓜ️:vm::fetch: Eliminate the remaining coarse sleep for prev fetch+eval. 2023-03-16 21:44:20 -07:00
Jason Volk dcfae310ab ircd:Ⓜ️:vm::notify: Add promise/future based notify interface. 2023-03-16 19:32:14 -07:00
Jason Volk 2331de3f3e ircd:Ⓜ️:vm::notify: Use ctx::future/promise rather than direct context notify. 2023-03-16 19:25:45 -07:00
Jason Volk 0b6669ca20 ircd::util::unique_iterator: Add deref operator passthrus; minor reorg. 2023-03-16 17:23:28 -07:00
Jason Volk 43838608fd ircd::fs::dev: Add stats gather struct w/ console cmd. 2023-03-16 12:03:32 -07:00
Jason Volk f85781b65a ircd::fs::dev: Move dev::blk related into class nested. 2023-03-16 11:42:49 -07:00
Jason Volk d01e937f3e modules/console: Add arg to check specific file in db check cmd. 2023-03-16 10:21:10 -07:00
Jason Volk 9558637c20 modules/key/server: Selective key occlusion blacklisting w/ conf items. 2023-03-15 10:50:26 -07:00
Jason Volk e3edcefe17 modules/client/register_available: Fix and remove stale dynamic module links. 2023-03-15 10:50:26 -07:00
Jason Volk ac3b85114b ircd::net::dns::resolver: Add conf item to ease off ServFail retries. 2023-03-14 22:34:58 -07:00
Jason Volk 684dd18497 ircd:Ⓜ️:event: Split signature generating overload w/ custom sk/pk. 2023-03-14 20:51:46 -07:00
Jason Volk fb9b68b4e3 ircd:Ⓜ️:push: Reenable .m.rule.message default underride (with dont_notify e7089e8e7f). 2023-03-14 13:56:34 -07:00
Jason Volk 00094e272f ircd:Ⓜ️:vm::inject: Add membership prior to auth_events generation.
Fixes m.room.join_rules added to membership=leave auth_events.

(regression matrix-org/synapse#13088 matrix-org/synapse@d4b1c0d800)
2023-03-13 19:09:08 -07:00
Jason Volk 98e366e012 ircd:Ⓜ️:room::auth: Perform relative check against state without the event included. 2023-03-13 12:54:08 -07:00
Jason Volk b5932ba33c modules/console: Add fed auth space cmd to the 'proper toolset and diagnostic equipment' 2023-03-12 19:59:24 -07:00
19 changed files with 876 additions and 200 deletions

View File

@ -1,8 +1,9 @@
version: master_{build}
clone_depth: 1
skip_tags: true
services:
- docker
- docker
environment:
COMMAND: if [[ "${APPVEYOR_REPO_COMMIT_MESSAGE@Q}" == *"[ci debug]"* ]]; then export DEBUG_FLAGS="--enable-debug"; fi; if [[ "${APPVEYOR_REPO_COMMIT_MESSAGE@Q}" == *"[ci gdb]"* ]]; then apt-get update && apt-get -y install gdb && export CONSTRUCT="gdb --batch -ex r -ex bt --return-child-result --args construct"; else export CONSTRUCT="construct"; fi && rmdir -v deps/rocksdb && ln -sv /usr/src/rocksdb deps && ./autogen.sh && ./configure --enable-assert ${DEBUG_FLAGS} && make install && ${CONSTRUCT} -smoketest -debug localhost
@ -18,8 +19,8 @@ environment:
DOCKER_IMAGE: jevolk/construct:ubuntu-22.04-full-build-clang-15-amd64
for:
-
build_script:
-
build_script:
- docker run -v `pwd`:/build "${DOCKER_IMAGE}" /bin/bash -c "${COMMAND}"
matrix:

View File

@ -14,9 +14,9 @@
namespace ircd::fs::dev
{
struct blk;
struct stats;
using major_minor = std::pair<ulong, ulong>;
using blk_closure = std::function<bool (const ulong &id, const blk &)>;
// Convert device ID's with the major(3) / minor(3) / makedev(3)
ulong id(const major_minor &);
@ -36,13 +36,12 @@ namespace ircd::fs::dev
sysfs(const ulong &id,
const string_view &path,
const R &def = 0);
bool for_each(const string_view &devtype, const blk_closure &);
bool for_each(const blk_closure &);
}
struct ircd::fs::dev::blk
{
using closure = util::function_bool<const ulong &, const blk &>;
static const size_t SECTOR_SIZE;
static const string_view BASE_PATH;
@ -66,6 +65,47 @@ struct ircd::fs::dev::blk
blk(const ulong &id);
blk() = default;
static bool for_each(const string_view &devtype, const closure &);
static bool for_each(const closure &);
};
struct ircd::fs::dev::stats
{
using closure = util::function_bool<const stats &>;
char name[32] {0};
major_minor id {0, 0};
uint64_t read {0};
uint64_t read_merged {0};
uint64_t read_sectors {0};
milliseconds read_time {0ms};
uint64_t write {0};
uint64_t write_merged {0};
uint64_t write_sectors {0};
milliseconds write_time {0ms};
uint64_t io_current {0};
milliseconds io_time {0ms};
milliseconds io_weighted_time {0ms};
// 4.18+
uint64_t discard {0};
uint64_t discard_merged {0};
uint64_t discard_sectors {0};
milliseconds discard_time {0ms};
// 5.5+
uint64_t flush {0};
milliseconds flush_time {0ms};
stats(const string_view &line);
stats() = default;
static bool for_each(const closure &);
static stats get(const major_minor &id);
};
/// Return a lex_cast'able (an integer) from a sysfs target.

View File

@ -41,11 +41,12 @@ namespace ircd::m
size_t degree(const event &);
bool before(const event &a, const event &b);
json::object hashes(const mutable_buffer &, const event &);
event signatures(const mutable_buffer &, const m::event &, const string_view &origin);
event signatures(const mutable_buffer &, const m::event &);
event essential(event, const mutable_buffer &content, const bool &sigs = false);
event signatures(const mutable_buffer &, const event &, const string_view &origin, const ed25519::sk &, const string_view &pkid);
event signatures(const mutable_buffer &, const event &, const string_view &origin);
event signatures(const mutable_buffer &, const event &);
json::object hashes(const mutable_buffer &, const event &);
bool verify_hash(const event &, const sha256::buf &);
bool verify_hash(const event &);

View File

@ -13,9 +13,12 @@
namespace ircd::m::vm::notify
{
using value_type = std::pair<const event::id, ctx::ctx *>;
struct future;
using value_type = std::pair<const event::id, ctx::promise<> *>;
using alloc_type = allocator::node<value_type>;
using map_type = std::multimap<event::id, ctx::ctx *, std::less<>, alloc_type::allocator>;
using map_type = std::multimap<const event::id, ctx::promise<> *, std::less<>, alloc_type::allocator>;
using node_type = std::pair<map_type::node_type, value_type>;
extern map_type map;
@ -23,6 +26,19 @@ namespace ircd::m::vm::notify
bool wait(const event::id &, const milliseconds);
}
class ircd::m::vm::notify::future
:public ctx::future<>
{
node_type node;
ctx::promise<> promise;
unique_iterator<map_type> it;
public:
future(const event::id &);
future(future &&) = delete;
future(const future &) = delete;
};
/// Yields ctx until event was successfully evaluated. Returns false if
/// timeout occurred.
inline bool

View File

@ -37,6 +37,7 @@ ircd::net::dns::resolver
static conf::item<milliseconds> send_rate;
static conf::item<size_t> send_burst;
static conf::item<size_t> retry_max;
static conf::item<bool> retry_serv_fail;
answers_callback callback;
std::vector<ip::udp::endpoint> server; // The list of active servers

View File

@ -32,41 +32,24 @@ template<class container,
class iterator>
struct ircd::util::unique_iterator
{
container *c;
container *c {nullptr};
iterator it;
unique_iterator(container &c, iterator it)
:c{&c}
,it{std::move(it)}
{}
operator const iterator &() const;
decltype(auto) operator->() const;
decltype(auto) operator*() const;
unique_iterator()
:c{nullptr}
{}
operator iterator &();
decltype(auto) operator->();
decltype(auto) operator*();
unique_iterator(container &c, iterator it);
unique_iterator() = default;
unique_iterator(unique_iterator &&o) noexcept;
unique_iterator(const unique_iterator &) = delete;
unique_iterator(unique_iterator &&o) noexcept
:c{std::move(o.c)}
,it{std::move(o.it)}
{
o.c = nullptr;
}
unique_iterator &operator=(unique_iterator &&o) noexcept;
unique_iterator &operator=(const unique_iterator &) = delete;
unique_iterator &operator=(unique_iterator &&o) noexcept
{
this->~unique_iterator();
c = std::move(o.c);
it = std::move(o.it);
o.c = nullptr;
return *this;
}
~unique_iterator() noexcept
{
if(c)
c->erase(it);
}
~unique_iterator() noexcept;
};
template<class container>
@ -76,3 +59,89 @@ struct ircd::util::unique_const_iterator
using iterator_type = typename container::const_iterator;
using unique_iterator<container, iterator_type>::unique_iterator;
};
template<class container,
class iterator>
inline
ircd::util::unique_iterator<container, iterator>::unique_iterator(container &c,
iterator it)
:c{&c}
,it{std::move(it)}
{}
template<class container,
class iterator>
inline
ircd::util::unique_iterator<container, iterator>::unique_iterator(unique_iterator &&o)
noexcept
:c{std::move(o.c)}
,it{std::move(o.it)}
{
o.c = nullptr;
}
template<class container,
class iterator>
inline ircd::util::unique_iterator<container, iterator> &
ircd::util::unique_iterator<container, iterator>::operator=(unique_iterator &&o)
noexcept
{
this->~unique_iterator();
c = std::move(o.c);
it = std::move(o.it);
o.c = nullptr;
return *this;
}
template<class container,
class iterator>
inline
ircd::util::unique_iterator<container, iterator>::~unique_iterator()
noexcept
{
if(c)
c->erase(it);
}
template<class container,
class iterator>
inline decltype(auto)
ircd::util::unique_iterator<container, iterator>::operator*()
{
return it.operator*();
}
template<class container,
class iterator>
inline decltype(auto)
ircd::util::unique_iterator<container, iterator>::operator->()
{
return it.operator->();
}
template<class container,
class iterator>
inline decltype(auto)
ircd::util::unique_iterator<container, iterator>::operator*()
const
{
return it.operator*();
}
template<class container,
class iterator>
inline decltype(auto)
ircd::util::unique_iterator<container, iterator>::operator->()
const
{
return it.operator->();
}
template<class container,
class iterator>
inline ircd::util::unique_iterator<container, iterator>::operator
const iterator &()
const
{
return it;
}

View File

@ -114,15 +114,13 @@ size_t
ircd::fs::aio::init::query_max_events()
{
size_t ret(0);
fs::dev::for_each("disk", [&ret]
fs::dev::blk::for_each("disk", [&ret]
(const ulong &id, const fs::dev::blk &device)
{
ret = std::clamp
(
device.queue_depth, ret, MAX_EVENTS
);
return true;
});
if(!ret)

View File

@ -10,55 +10,6 @@
#include <RB_INC_SYS_SYSMACROS_H
bool
ircd::fs::dev::for_each(const blk_closure &closure)
{
return for_each(string_view{}, closure);
}
bool
ircd::fs::dev::for_each(const string_view &type,
const blk_closure &closure)
{
for(const auto &dir : fs::ls(blk::BASE_PATH)) try
{
const auto &[major, minor]
{
split(filename(path_scratch, dir), ':')
};
if(!major || !minor)
continue;
const ulong id
{
dev::id({lex_cast<ulong>(major), lex_cast<ulong>(minor)})
};
char dtbuf[32];
if(type && blk::devtype(dtbuf, id) != type)
continue;
if(!closure(id, blk(id)))
return false;
}
catch(const ctx::interrupted &)
{
throw;
}
catch(const std::exception &e)
{
log::error
{
log, "%s :%s",
dir,
e.what(),
};
}
return true;
}
ircd::string_view
ircd::fs::dev::sysfs(const mutable_buffer &out,
const ulong &id,
@ -123,6 +74,59 @@ ircd::fs::dev::blk::BASE_PATH
"/sys/dev/block"
};
bool
ircd::fs::dev::blk::for_each(const closure &closure)
{
return for_each(string_view{}, closure);
}
bool
ircd::fs::dev::blk::for_each(const string_view &type,
const closure &closure)
{
for(const auto &dir : fs::ls(blk::BASE_PATH)) try
{
const auto &[major, minor]
{
split(filename(path_scratch, dir), ':')
};
if(!major || !minor)
continue;
const ulong id
{
dev::id({lex_cast<ulong>(major), lex_cast<ulong>(minor)})
};
char dtbuf[32];
if(type && blk::devtype(dtbuf, id) != type)
continue;
if(!closure(id, blk(id)))
return false;
}
catch(const ctx::interrupted &)
{
throw;
}
catch(const std::exception &e)
{
log::error
{
log, "%s :%s",
dir,
e.what(),
};
}
return true;
}
//
// dev::blk::blk
//
ircd::fs::dev::blk::blk(const ulong &id)
:type
{
@ -227,3 +231,100 @@ ircd::fs::dev::blk::devtype(const mutable_buffer &buf,
return ret;
}
//
// dev::stats
//
ircd::fs::dev::stats
ircd::fs::dev::stats::get(const major_minor &id)
{
stats ret;
for_each([&id, &ret]
(const auto &stats)
{
if(stats.id == id)
{
ret = stats;
return false;
}
else return true;
});
return ret;
}
bool
ircd::fs::dev::stats::for_each(const closure &closure)
{
thread_local char buf[16_KiB];
const fs::fd fd
{
"/proc/diskstats", fs::fd::opts
{
.mode = std::ios::in,
},
};
fs::read_opts opts;
opts.aio = false;
const string_view read
{
fs::read(fd, buf, opts)
};
return tokens(read, '\n', [&closure]
(const auto &line)
{
return closure(stats(line));
});
}
//
// dev::stats::stats
//
ircd::fs::dev::stats::stats(const string_view &line)
{
string_view item[20];
const auto items
{
tokens(line, ' ', item)
};
id.first = lex_cast<uint64_t>(item[0]);
id.second = lex_cast<uint64_t>(item[1]);
strlcpy(name, item[2]);
read = lex_cast<uint64_t>(item[3]);
read_merged = lex_cast<uint64_t>(item[4]);
read_sectors = lex_cast<uint64_t>(item[5]);
read_time = lex_cast<milliseconds>(item[6]);
write = lex_cast<uint64_t>(item[7]);
write_merged = lex_cast<uint64_t>(item[8]);
write_sectors = lex_cast<uint64_t>(item[9]);
write_time = lex_cast<milliseconds>(item[10]);
io_current = lex_cast<uint64_t>(item[11]);
io_time = lex_cast<milliseconds>(item[12]);
io_weighted_time = lex_cast<milliseconds>(item[13]);
if(items <= 14)
return;
discard = lex_cast<uint64_t>(item[14]);
discard_merged = lex_cast<uint64_t>(item[15]);
discard_sectors = lex_cast<uint64_t>(item[16]);
discard_time = lex_cast<milliseconds>(item[17]);
if(items <= 18)
return;
flush = lex_cast<uint64_t>(item[18]);
flush_time = lex_cast<milliseconds>(item[19]);
if(items <= 20)
return;
}

View File

@ -39,6 +39,13 @@ ircd::net::dns::resolver::retry_max
{ "default", 20L },
};
decltype(ircd::net::dns::resolver::retry_serv_fail)
ircd::net::dns::resolver::retry_serv_fail
{
{ "name", "ircd.net.dns.resolver.retry_serv_fail" },
{ "default", true },
};
decltype(ircd::net::dns::resolver::servers)
ircd::net::dns::resolver::servers
{
@ -726,7 +733,14 @@ ircd::net::dns::resolver::handle_reply(const ipport &from,
// Handle ServFail as a special case here. We can try again without
// handling this tag or propagating this error any further yet.
if(header.rcode == 2 && tag.tries < size_t(server.size()))
const bool serv_fail_retry
{
retry_serv_fail
&& header.rcode == 2
&& tag.tries < size_t(server.size())
};
if(serv_fail_retry)
{
log::error
{

View File

@ -322,10 +322,30 @@ ircd::m::signatures(const mutable_buffer &out,
return signatures(out, event, origin);
}
ircd::m::event
ircd::m::signatures(const mutable_buffer &out,
const m::event &event,
const string_view &origin)
{
const auto &secret_key
{
m::secret_key(my(origin))
};
const string_view public_key_id
{
m::public_key_id(my(origin))
};
return signatures(out, event, origin, secret_key, public_key_id);
}
ircd::m::event
ircd::m::signatures(const mutable_buffer &out_,
const m::event &event_,
const string_view &origin)
const string_view &origin,
const ed25519::sk &secret_key,
const string_view &public_key_id)
{
m::event event
{
@ -337,16 +357,6 @@ ircd::m::signatures(const mutable_buffer &out_,
stringify(event::buf[2], event)
};
const auto &secret_key
{
m::secret_key(my(origin))
};
const string_view public_key_id
{
m::public_key_id(my(origin))
};
const ed25519::sig my_sig
{
event::sign(preimage, secret_key)

View File

@ -1145,7 +1145,7 @@ ircd::m::push::rules::defaults{R"(
{
"rule_id": ".m.rule.message",
"default": true,
"enabled": false,
"enabled": true,
"conditions":
[
{

View File

@ -211,17 +211,37 @@ try
if(at<"type"_>(event) == "m.room.create")
return {true, {}};
if(!m::exists(event.event_id))
return {true, {}};
const auto &room_id
{
at<"room_id"_>(event)
};
const auto event_idx
{
m::index(std::nothrow, event.event_id)
};
if(!event_idx)
return {true, {}};
const auto prev_idx
{
m::room::state::prev(event_idx)
};
const auto prev_id
{
m::event_id(std::nothrow, prev_idx)
};
const auto state_id
{
prev_id?: event.event_id
};
const m::room room
{
room_id, event.event_id
room_id, state_id
};
const auto idxs

View File

@ -13,7 +13,7 @@ namespace ircd::m::vm::fetch
static void prev_check(const event &, vm::eval &);
static bool prev_wait(const event &, vm::eval &);
static std::forward_list<ctx::future<m::fetch::result>> prev_fetch(const event &, vm::eval &, const room &);
static void prev_eval(const event &, vm::eval &, ctx::future<m::fetch::result> &);
static void prev_eval(const event &, vm::eval &, ctx::future<m::fetch::result> &, const system_point &);
static void prev(const event &, vm::eval &, const room &);
static std::forward_list<ctx::future<m::fetch::result>> state_fetch(const event &, vm::eval &, const room &);
static void state(const event &, vm::eval &, const room &);
@ -22,9 +22,8 @@ namespace ircd::m::vm::fetch
static void auth(const event &, vm::eval &, const room &);
static void handle(const event &, vm::eval &);
extern conf::item<milliseconds> prev_fetch_check_interval;
extern conf::item<milliseconds> prev_preempt_time;
extern conf::item<milliseconds> prev_wait_time;
extern conf::item<size_t> prev_wait_count;
extern conf::item<size_t> prev_backfill_limit;
extern conf::item<seconds> event_timeout;
extern conf::item<seconds> state_timeout;
@ -36,6 +35,23 @@ namespace ircd::m::vm::fetch
extern log::log log;
}
namespace ircd::m::vm::fetch::stats
{
using ircd::stats::item;
extern item<uint64_t> prev_noempts;
extern item<uint64_t> prev_preempts;
extern item<uint64_t> prev_evals;
extern item<uint64_t> prev_fetched;
extern item<uint64_t> prev_fetches;
extern item<uint64_t> auth_evals;
extern item<uint64_t> auth_fetched;
extern item<uint64_t> auth_fetches;
extern item<uint64_t> state_evals;
extern item<uint64_t> state_fetched;
extern item<uint64_t> state_fetches;
}
decltype(ircd::m::vm::fetch::log)
ircd::m::vm::fetch::log
{
@ -104,25 +120,84 @@ ircd::m::vm::fetch::prev_backfill_limit
{ "default", 128L },
};
decltype(ircd::m::vm::fetch::prev_wait_count)
ircd::m::vm::fetch::prev_wait_count
{
{ "name", "ircd.m.vm.fetch.prev.wait.count" },
{ "default", 4L },
};
decltype(ircd::m::vm::fetch::prev_wait_time)
ircd::m::vm::fetch::prev_wait_time
{
{ "name", "ircd.m.vm.fetch.prev.wait.time" },
{ "default", 200L },
{ "default", 750L },
};
decltype(ircd::m::vm::fetch::prev_fetch_check_interval)
ircd::m::vm::fetch::prev_fetch_check_interval
decltype(ircd::m::vm::fetch::prev_preempt_time)
ircd::m::vm::fetch::prev_preempt_time
{
{ "name", "ircd.m.vm.fetch.prev.fetch.check_interval" },
{ "default", 500L },
{ "name", "ircd.m.vm.fetch.prev.preempt.time" },
{ "default", 5000L },
};
decltype(ircd::m::vm::fetch::stats::state_fetches)
ircd::m::vm::fetch::stats::state_fetches
{
{ "name", "ircd.m.vm.fetch.state.fetches" },
};
decltype(ircd::m::vm::fetch::stats::state_fetched)
ircd::m::vm::fetch::stats::state_fetched
{
{ "name", "ircd.m.vm.fetch.state.fetched" },
};
decltype(ircd::m::vm::fetch::stats::state_evals)
ircd::m::vm::fetch::stats::state_evals
{
{ "name", "ircd.m.vm.fetch.state.evals" },
};
decltype(ircd::m::vm::fetch::stats::auth_fetches)
ircd::m::vm::fetch::stats::auth_fetches
{
{ "name", "ircd.m.vm.fetch.auth.fetches" },
};
decltype(ircd::m::vm::fetch::stats::auth_fetched)
ircd::m::vm::fetch::stats::auth_fetched
{
{ "name", "ircd.m.vm.fetch.auth.fetched" },
};
decltype(ircd::m::vm::fetch::stats::auth_evals)
ircd::m::vm::fetch::stats::auth_evals
{
{ "name", "ircd.m.vm.fetch.auth.evals" },
};
decltype(ircd::m::vm::fetch::stats::prev_fetches)
ircd::m::vm::fetch::stats::prev_fetches
{
{ "name", "ircd.m.vm.fetch.prev.fetches" },
};
decltype(ircd::m::vm::fetch::stats::prev_fetched)
ircd::m::vm::fetch::stats::prev_fetched
{
{ "name", "ircd.m.vm.fetch.prev.fetched" },
};
decltype(ircd::m::vm::fetch::stats::prev_evals)
ircd::m::vm::fetch::stats::prev_evals
{
{ "name", "ircd.m.vm.fetch.prev.evals" },
};
decltype(ircd::m::vm::fetch::stats::prev_preempts)
ircd::m::vm::fetch::stats::prev_preempts
{
{ "name", "ircd.m.vm.fetch.prev.preempts" },
};
decltype(ircd::m::vm::fetch::stats::prev_noempts)
ircd::m::vm::fetch::stats::prev_noempts
{
{ "name", "ircd.m.vm.fetch.prev.noempts" },
};
//
@ -303,11 +378,13 @@ try
};
// recv
stats::auth_fetches++;
const auto result
{
future.get(seconds(auth_timeout))
};
stats::auth_fetched++;
const json::object response
{
result
@ -320,6 +397,7 @@ try
};
auth_chain_eval(event, eval, room, auth_chain, result.origin);
stats::auth_evals++;
}
catch(const vm::error &e)
{
@ -435,6 +513,7 @@ try
return;
}
stats::state_fetches++;
log::dwarning
{
log, "%s fetching possible missing state in %s",
@ -454,6 +533,7 @@ try
};
//TODO: XXX
stats::state_fetched++;
log::info
{
log, "%s evaluated missing state in %s fetched:-- good:-- fail:--",
@ -504,21 +584,32 @@ ircd::m::vm::fetch::prev(const event &event,
// Attempt to wait for missing prev_events without issuing fetches here.
if(prev_wait(event, eval))
{
stats::prev_noempts += prev_count;
return;
}
if(!m::vm::fetch::enable)
{
// No fetches will take place; only check if satisfied.
prev_check(event, eval);
return;
}
auto futures
// Launch fetches for missing prev events
auto fetching
{
prev_fetch(event, eval, room)
};
const auto fetching_count
{
std::distance(begin(fetching), end(fetching))
};
// At this point one or more prev_events are missing; the fetches were
// launched asynchronously if the options allowed for it.
stats::prev_fetches += fetching_count;
log::dwarning
{
log, "%s depth:%ld prev_events:%zu miss:%zu fetching:%zu fetching ...",
@ -526,43 +617,63 @@ ircd::m::vm::fetch::prev(const event &event,
at<"depth"_>(event),
prev_count,
prev_count - prev_exists,
std::distance(begin(futures), end(futures)),
};
auto fetching
{
ctx::when_all(begin(futures), end(futures))
};
const auto timeout
{
now<system_point>() + seconds(event_timeout)
};
const milliseconds &check_interval
{
prev_fetch_check_interval
fetching_count,
};
// Rather than waiting for all of the events to arrive or for the entire
// timeout to expire, we check if the sought events made it to the server
// in the meantime. If so we can drop these requests and bail.
//TODO: Ideally should be replaced with listener/notification/hook on the
//TODO: events arriving rather than this coarse sleep cycles.
while(now<system_point>() < timeout)
{
// Wait for an interval to give this loop some iterations.
if(fetching.wait(check_interval, std::nothrow))
break;
// in the meantime. If so we can drop these fetches and bail.
std::optional<vm::notify::future> evaling[prev_count];
for(size_t i(0); i < prev_count; ++i)
evaling[i].emplace(prev.prev_event(i));
// Check for satisfaction.
if(prev.prev_events_exist() == prev_count)
return;
// Either all of the fetches are done and we can start evaluating or all
// of the events arrived elsehow and we don't need any of the fetches.
// XXX: Ideally this could be refactored with mix-and-match granularity
// but at this time it's unknown if there's practical benefit.
ctx::future<> when[]
{
ctx::when_all(begin(fetching), end(fetching)),
ctx::when_all(evaling, evaling + prev_count, []
(auto &optional) -> ctx::future<> &
{
return optional->value();
}),
};
// Represents one of the two outcomes.
auto future
{
ctx::when_any(begin(when), end(when))
};
const auto prev_wait_until
{
now<system_point>() + milliseconds(prev_preempt_time)
};
// Wait for one of the two outcomes.
const bool finished
{
future.wait_until(prev_wait_until, std::nothrow)
};
// Check for satisfaction.
if(prev.prev_events_exist() == prev_count)
{
stats::prev_preempts += prev_count;
assert(finished);
return;
}
// evaluate results
for(auto &future : futures)
prev_eval(event, eval, future);
const auto event_wait_until
{
now<system_point>() + seconds(event_timeout)
};
// If we're not satisfied we commit to evaluating the fetches.
for(auto &fetch : fetching)
prev_eval(event, eval, fetch, event_wait_until);
// check if result evals have satisfied this eval now; or throw
prev_check(event, eval);
@ -571,14 +682,16 @@ ircd::m::vm::fetch::prev(const event &event,
void
ircd::m::vm::fetch::prev_eval(const event &event,
vm::eval &eval,
ctx::future<m::fetch::result> &future)
ctx::future<m::fetch::result> &future,
const system_point &until)
try
{
m::fetch::result result
{
future.get()
future.get_until(until)
};
stats::prev_fetched++;
const json::object content
{
result
@ -605,6 +718,8 @@ try
{
pdus, opts
};
stats::prev_evals++;
}
catch(const ctx::interrupted &)
{

View File

@ -267,6 +267,17 @@ ircd::m::vm::inject(eval &eval,
}
};
const json::iov::defaults membership_
{
event, event.at("type") == "m.room.member",
{
"membership", [&contents]()
{
return contents.at("membership");
}
}
};
const bool add_auth_events
{
!is_room_create

View File

@ -42,12 +42,7 @@ ircd::m::vm::notify::wait(const vector_view<const event::id> &event_id,
const milliseconds to)
{
using iterator_type = unique_iterator<map_type>;
using node_type = std::pair<map_type::node_type, value_type>;
static const size_t max_ids
{
64
};
static const size_t max_ids {64};
assume(event_id.size() <= max_ids);
const auto event_ids
@ -60,14 +55,15 @@ ircd::m::vm::notify::wait(const vector_view<const event::id> &event_id,
m::exists(event_id)
};
size_t exists(0);
node_type node[max_ids];
iterator_type it[event_ids];
ctx::future<> future[event_ids];
ctx::promise<> promise[event_ids];
for(size_t i(0); i < event_ids; ++i)
{
if(exists_mask & (1UL << i))
{
exists++;
future[i] = ctx::future<>{ctx::already};
continue;
}
@ -85,22 +81,75 @@ ircd::m::vm::notify::wait(const vector_view<const event::id> &event_id,
it[i] =
{
map, map.emplace(event_id[i], ctx::current)
map, map.emplace(event_id[i], promise + i)
};
assert(it[i]->second);
future[i] = ctx::future<>
{
*it[i]->second
};
}
bool timeout(false);
const auto tp(now<system_point>() + to);
while(exists < event_ids && !timeout)
auto all
{
timeout = ctx::wait_until(tp, std::nothrow);
exists = m::exists_count(event_id);
}
ctx::when_all(future, future + event_ids)
};
const bool ok
{
all.wait_until(now<system_point>() + to, std::nothrow)
};
const size_t exists
{
!ok?
m::exists_count(event_id):
event_ids
};
assert(exists <= event_ids);
return exists;
}
//
// future::future
//
ircd::m::vm::notify::future::future(const event::id &event_id)
:ctx::future<>{ctx::already}
{
if(m::exists(event_id))
return;
const auto &s
{
map.get_allocator().s
};
assert(s);
assert(!s->next);
const scope_restore next
{
s->next, reinterpret_cast<notify::value_type *>(&node)
};
it =
{
map, map.emplace(event_id, &promise)
};
assert(it->second);
static_cast<ctx::future<> &>(*this) = ctx::future<>
{
*it->second
};
}
//
// internal
//
void
ircd::m::vm::notify::hook_handle(const m::event &event,
vm::eval &)
@ -112,11 +161,13 @@ ircd::m::vm::notify::hook_handle(const m::event &event,
for(; pit.first != pit.second; ++pit.first)
{
const auto &[event_id, ctx] {*pit.first};
const auto &[event_id, promise] {*pit.first};
assert(promise);
assert(promise->valid());
assert(event_id == event.event_id);
assert(ctx != nullptr);
ctx::notify(*ctx);
if(likely(*promise))
promise->set_value();
}
}

View File

@ -37,29 +37,21 @@ method_get
}
};
mods::import<void (const m::id::user &)>
validate_user_id
{
"client_register", "validate_user_id"
};
mods::import<ircd::conf::item<bool>>
register_enable
{
"client_register", "register_enable"
};
mods::import<ircd::conf::item<bool>>
register_user_enable
{
"client_register", "register_user_enable"
};
m::resource::response
get__register_available(client &client,
const m::resource::request &request)
{
if(!bool(register_enable) || !bool(register_user_enable))
const bool register_enable
{
conf::as("ircd.client.register.enable", false)
};
const bool register_user_enable
{
conf::as("ircd.client.register.user.enable", false)
};
if(!register_enable || !register_user_enable)
throw m::error
{
http::FORBIDDEN, "M_REGISTRATION_DISABLED",
@ -74,7 +66,7 @@ get__register_available(client &client,
};
// Performs additional custom checks on the user_id else throws.
validate_user_id(user_id);
m::user::registar::validate_user_id(user_id);
// We indicate availability of a valid mxid in the cacheable 200 OK
return resource::response

View File

@ -1057,7 +1057,7 @@ console_cmd__fs__dev(opt &out, const string_view &line)
<< std::setw(24) << std::left << "SCHED" << ' '
<< std::endl;
fs::dev::for_each(type, [&out]
fs::dev::blk::for_each(type, [&out]
(const ulong &id, const fs::dev::blk &dev)
{
const auto mm(fs::dev::id(id));
@ -1082,12 +1082,83 @@ console_cmd__fs__dev(opt &out, const string_view &line)
<< std::setw(16) << std::left << dev.vendor << ' '
<< std::setw(24) << std::left << dev.scheduler << ' '
<< std::endl;
return true;
});
return true;
}
bool
console_cmd__fs__dev__stats(opt &out, const string_view &line)
{
const params param{line, " ",
{
"name"
}};
const string_view name
{
param["name"]
};
out
<< std::setw(3) << std::right << "maj" << ':'
<< std::setw(3) << std::left << "min" << ' '
<< std::setw(18) << std::left << "name" << ' '
<< std::setw(6) << std::right << "io cur" << ' '
<< std::setw(12) << std::right << "io time" << ' '
<< std::setw(12) << std::right << "io weighted" << ' '
<< std::setw(12) << std::right << "reads" << ' '
<< std::setw(12) << std::right << "read sect" << ' '
<< std::setw(12) << std::right << "read merge" << ' '
<< std::setw(12) << std::right << "read time" << ' '
<< std::setw(12) << std::right << "writes" << ' '
<< std::setw(12) << std::right << "write sect" << ' '
<< std::setw(12) << std::right << "write merge" << ' '
<< std::setw(12) << std::right << "write time" << ' '
<< std::setw(12) << std::right << "discards" << ' '
<< std::setw(12) << std::right << "discard sect" << ' '
<< std::setw(12) << std::right << "discard merg" << ' '
<< std::setw(12) << std::right << "discard time" << ' '
<< std::setw(12) << std::right << "flushes" << ' '
<< std::setw(12) << std::right << "flush time" << ' '
<< std::endl;
fs::dev::stats::for_each([&out, &name]
(const auto &stats)
{
if(name && !startswith(stats.name, name))
return true;
char tmbuf[8][32];
out
<< std::setw(3) << std::right << stats.id.first << ':'
<< std::setw(3) << std::left << stats.id.second << ' '
<< std::setw(18) << std::left << stats.name << ' '
<< std::setw(6) << std::right << stats.io_current << ' '
<< std::setw(12) << std::right << pretty(tmbuf[0], stats.io_time, 1) << ' '
<< std::setw(12) << std::right << pretty(tmbuf[1], stats.io_weighted_time, 1) << ' '
<< std::setw(12) << std::right << stats.read << ' '
<< std::setw(12) << std::right << stats.read_merged << ' '
<< std::setw(12) << std::right << stats.read_sectors << ' '
<< std::setw(12) << std::right << pretty(tmbuf[2], stats.read_time, 1) << ' '
<< std::setw(12) << std::right << stats.write << ' '
<< std::setw(12) << std::right << stats.write_merged << ' '
<< std::setw(12) << std::right << stats.write_sectors << ' '
<< std::setw(12) << std::right << pretty(tmbuf[3], stats.write_time, 1) << ' '
<< std::setw(12) << std::right << stats.discard << ' '
<< std::setw(12) << std::right << stats.discard_merged << ' '
<< std::setw(12) << std::right << stats.discard_sectors << ' '
<< std::setw(12) << std::right << pretty(tmbuf[4], stats.discard_time, 1) << ' '
<< std::setw(12) << std::right << stats.flush << ' '
<< std::setw(12) << std::right << pretty(tmbuf[5], stats.flush_time, 1) << ' '
<< '\n';
return !name;
});
out << std::endl;
return true;
}
bool
console_cmd__ls(opt &out, const string_view &line)
{
@ -5343,7 +5414,7 @@ try
{
const params param{line, " ",
{
"dbname", "column"
"dbname", "column|file"
}};
const auto &dbname
@ -5353,7 +5424,14 @@ try
const auto &colname
{
param["column"]
param["column|file"]
};
const auto &fname
{
!startswith(colname, '/')?
string_view{}:
lstrip(colname, '/')
};
auto &database
@ -5361,6 +5439,15 @@ try
db::database::get(dbname)
};
if(fname)
{
check(database, fname);
out << "Check of file " << fname << " in " << dbname << " completed without error."
<< std::endl;
return true;
}
if(colname)
{
db::column column
@ -16616,6 +16703,108 @@ console_cmd__fed__auth(opt &out, const string_view &line)
return true;
}
bool
console_cmd__fed__auth__space(opt &out, const string_view &line)
{
const params param{line, " ",
{
"room_id", "type", "state_key", "remote", "depth", "limit"
}};
const auto &room_id
{
m::room_id(param.at("room_id"))
};
const string_view &type
{
param["type"] != "*"?
param["type"] : string_view{},
};
const string_view &state_key
{
param["state_key"] != "\"\""?
param["state_key"] : string_view{},
};
const string_view remote
{
param.at("remote")
};
const int64_t depth
{
param.at("depth", -1L)
};
const uint64_t limit
{
param.at("limit", -1UL)
};
const m::room::state state
{
room_id
};
const m::room::state::space space
{
room_id
};
const unique_buffer<mutable_buffer> buf
{
16_KiB
};
const server::request::opts sopts
{
.http_exceptions = false,
};
size_t i(0);
space.for_each(type, state_key, depth, [&]
(const auto &type, const auto &state_key, const auto &depth, const auto &event_idx)
{
const m::event::fetch event
{
std::nothrow, event_idx
};
if(!event.valid)
return true;
m::fed::event_auth::opts opts;
opts.remote = remote;
opts.sopts = &sopts;
m::fed::event_auth request
{
room_id, event.event_id, buf, std::move(opts)
};
request.wait(out.timeout);
const auto code
{
request.get()
};
const char report
{
code == 200? '+':
code == 404? '-':
code == 403? 'X':
'?'
};
out << report << ' ';
m::pretty_stateline(out, event, event_idx);
return ++i < limit;
});
return true;
}
bool
console_cmd__fed__query_auth(opt &out, const string_view &line)
{

View File

@ -26,6 +26,20 @@ server_resource
}
};
conf::item<std::string>
occlusion_blacklist
{
{ "name", "ircd.key.occlude.blacklist" },
{ "default", "" },
};
conf::item<std::string>
occlusion_whitelist
{
{ "name", "ircd.key.occlude.whitelist" },
{ "default", "" },
};
m::resource::response
handle_get(client &client,
const m::resource::request &request)
@ -36,6 +50,38 @@ handle_get(client &client,
url::decode(key_id_buf, request.params)
};
const blackwhite::list acl
{
' ', occlusion_blacklist, occlusion_whitelist
};
char remote_buf[256];
const auto ip_str
{
occlusion_blacklist || occlusion_whitelist?
host(string(remote_buf, remote(client))):
string_view{}
};
const bool allow
{
acl(ip_str)
};
log::debug
{
m::log, "%s requested key %s (%s)",
loghead(client),
key_id?: "*"_sv,
allow? "ALLOWED": "DENIED",
};
if(!allow)
return m::resource::response
{
client, http::FORBIDDEN
};
m::keys::get(my_host(), key_id, [&client]
(const json::object &keys)
{

View File

@ -196,6 +196,7 @@ ircd::m::push::execute(const event &event,
const event::idx &rule_idx)
try
{
assert(json::get<"enabled"_>(rule));
const auto &[scope, kind, ruleid]
{
path