diff --git a/meson.build b/meson.build index 6a23d59c9..7623753ef 100644 --- a/meson.build +++ b/meson.build @@ -14,6 +14,7 @@ if git.found() endif config_h.set_quoted('NIXD_VERSION', meson.project_version()) +config_h.set_quoted('NIXD_LIBEXEC', get_option('prefix') / get_option('libexecdir')) configure_file( output: 'nixd-config.h', diff --git a/nixd/meson.build b/nixd/meson.build index df5af0199..124fa9ea6 100644 --- a/nixd/meson.build +++ b/nixd/meson.build @@ -1,3 +1,4 @@ subdir('librpc') subdir('libutil') +subdir('nix-node-eval') subdir('tools') diff --git a/nixd/nix-node-eval/README.md b/nixd/nix-node-eval/README.md new file mode 100644 index 000000000..af9b04446 --- /dev/null +++ b/nixd/nix-node-eval/README.md @@ -0,0 +1,3 @@ +# nix-node-eval + +Collect per-node eval information, by using official evaluator. diff --git a/nixd/nix-node-eval/meson.build b/nixd/nix-node-eval/meson.build new file mode 100644 index 000000000..19d357fba --- /dev/null +++ b/nixd/nix-node-eval/meson.build @@ -0,0 +1,31 @@ +nixd_nix_node_eval_deps = [ libnixdrpc, nixt, libnixdutil ] + +nixd_nix_node_eval_lib = library( + 'nix-node-eval', + 'src/EvalProvider.cpp', + install: true, + dependencies: nixd_nix_node_eval_deps +) + +nixd_nix_node_eval_lib_dep = declare_dependency( + link_with: nixd_nix_node_eval_lib, + dependencies: nixd_nix_node_eval_deps, +) + + +nixd_nix_node_eval = executable('nix-node-eval', + 'src/Main.cpp', + install: true, + install_dir: get_option('libexecdir'), + dependencies: nixd_nix_node_eval_lib_dep +) + +test( + 'unit/nixd/nix-node-eval', + executable( + 'unit-nixd-nix-node-eval', + 'test/EvalProvider.cpp', + dependencies: [ nixd_nix_node_eval_lib_dep, gtest_main], + include_directories: include_directories('src') + ) +) diff --git a/nixd/nix-node-eval/src/EvalProvider.cpp b/nixd/nix-node-eval/src/EvalProvider.cpp new file mode 100644 index 000000000..b9cd29a11 --- /dev/null +++ b/nixd/nix-node-eval/src/EvalProvider.cpp @@ -0,0 +1,100 @@ +#include "EvalProvider.h" + +#include "nixd/rpc/Protocol.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace nixd { + +using bc::readBytecode; +using rpc::readBytecode; + +namespace bipc = boost::interprocess; + +using namespace rpc; + +EvalProvider::EvalProvider(int InboundFD, int OutboundFD) + : rpc::Transport(InboundFD, OutboundFD), + State(std::unique_ptr( + new nix::EvalState{{}, nix::openStore("dummy://")})) {} + +void EvalProvider::handleInbound(const std::vector &Buf) { + std::ostringstream OS; + rpc::RPCKind Kind; + std::string_view Data(Buf.data(), Buf.size()); + readBytecode(Data, Kind); + switch (Kind) { + case rpc::RPCKind::RegisterBC: { + rpc::RegisterBCParams Params; + readBytecode(Data, Params); + onRegisterBC(Params); + break; + } + case rpc::RPCKind::UnregisterBC: + case rpc::RPCKind::Log: + case rpc::RPCKind::ExprValue: { + rpc::ExprValueParams Params; + readBytecode(Data, Params); + rpc::ExprValueResponse Response = onExprValue(Params); + sendPacket(Response); + break; + } + } +} + +void EvalProvider::onRegisterBC(const rpc::RegisterBCParams &Params) { + // Path context + auto CachePath = State->rootPath(nix::CanonPath(Params.CachePath)); + + nixt::DeserializeContext Ctx = nixt::getDeserializeContext( + *State, Params.BasePath, nix::Pos::none_tag{}); + + // Extract the buffer from `Params` + bipc::shared_memory_object Shm(bipc::open_only, Params.Shm.c_str(), + bipc::read_only); + + bipc::mapped_region Region(Shm, bipc::read_only); + + std::string_view RegionView = {(char *)Region.get_address(), + (char *)Region.get_address() + Params.Size}; + + nix::Expr *AST = nixt::deserializeHookable(RegionView, Ctx, Pool, VMap, EMap); + + nix::Value V; + State->eval(AST, V); + + // Inject pre-parsed AST into EvalState cache + auto Cache = nixt::getFileParseCache(*State); + Cache[CachePath] = AST; +} + +ExprValueResponse EvalProvider::onExprValue(const ExprValueParams &Params) { + if (VMap.contains(Params.ExprID)) { + nix::Value V = VMap[Params.ExprID]; + + return { + ExprValueResponse::OK, + static_cast(V.integer), + ExprValueResponse::Int, + }; + } + return {ExprValueResponse::NotEvaluated, 0}; +} + +} // namespace nixd diff --git a/nixd/nix-node-eval/src/EvalProvider.h b/nixd/nix-node-eval/src/EvalProvider.h new file mode 100644 index 000000000..cd35b3d0e --- /dev/null +++ b/nixd/nix-node-eval/src/EvalProvider.h @@ -0,0 +1,30 @@ +#pragma once + +#include "nixd/rpc/Protocol.h" +#include "nixd/rpc/Transport.h" + +#include +#include + +#include + +namespace nixd { + +class EvalProvider : public rpc::Transport { + + nixt::PtrPool Pool; + nixt::ValueMap VMap; + nixt::EnvMap EMap; + std::unique_ptr State; + + void handleInbound(const std::vector &Buf) override; + +public: + EvalProvider(int InboundFD, int OutboundFD); + + void onRegisterBC(const rpc::RegisterBCParams &Params); + + rpc::ExprValueResponse onExprValue(const rpc::ExprValueParams &Params); +}; + +} // namespace nixd diff --git a/nixd/nix-node-eval/src/Main.cpp b/nixd/nix-node-eval/src/Main.cpp new file mode 100644 index 000000000..a383f4b99 --- /dev/null +++ b/nixd/nix-node-eval/src/Main.cpp @@ -0,0 +1,12 @@ + +#include "EvalProvider.h" + +#include + +#include + +int main() { + nixt::initEval(); + nixd::EvalProvider Provider(STDIN_FILENO, STDOUT_FILENO); + return Provider.run(); +} diff --git a/nixd/nix-node-eval/test/EvalProvider.cpp b/nixd/nix-node-eval/test/EvalProvider.cpp new file mode 100644 index 000000000..3751655b1 --- /dev/null +++ b/nixd/nix-node-eval/test/EvalProvider.cpp @@ -0,0 +1,44 @@ +#include + +#include "EvalProvider.h" + +#include "nixd/rpc/Protocol.h" +#include "nixd/rpc/Transport.h" + +#include + +#include + +namespace { + +using namespace nixd::rpc; + +#define READ 0 +#define WRITE 1 + +struct TestEvalProvider : testing::Test { + int Pipes[2][2]; + + TestEvalProvider() { nixt::initEval(); } +}; + +TEST_F(TestEvalProvider, ExprValue) { + ASSERT_EQ(pipe(Pipes[READ]), 0); + ASSERT_EQ(pipe(Pipes[WRITE]), 0); + + nixd::EvalProvider Provider(Pipes[READ][READ], Pipes[WRITE][WRITE]); + + sendPacket>(Pipes[READ][WRITE], + {RPCKind::ExprValue, {}}); + + close(Pipes[READ][WRITE]); + + [[maybe_unused]] int Result = Provider.run(); + + auto Packet = recvPacket(Pipes[WRITE][READ]); + + ASSERT_EQ(Packet.ValueID, 0); + ASSERT_EQ(Packet.ValueKind, 0); +} + +} // namespace