libstore: Add FreeBSD findPlatformRoots

Use libprocstat to find garbage collector roots on FreeBSD.
Tested working on a FreeBSD machine, although there is no CI yet

Change-Id: Id36bac8c3de6cc4de94e2d76e9663dd4b76068a9
This commit is contained in:
Artemis Tosini 2024-07-20 03:18:39 +00:00
parent 73c013a5df
commit 53f3e39815
Signed by: artemist
GPG key ID: EE5227935FE3FF18
5 changed files with 225 additions and 13 deletions

View file

@ -314,6 +314,10 @@ nlohmann_json = dependency('nlohmann_json', required : true)
# *absolutely* are not going to make it work)
lix_doc = declare_dependency(link_args : [ '-llix_doc' ])
if is_freebsd
libprocstat = declare_dependency(link_args : [ '-lprocstat' ])
endif
#
# Build-time tools
#

View file

@ -167,6 +167,9 @@ if host_machine.system() == 'linux'
elif host_machine.system() == 'darwin'
libstore_sources += files('platform/darwin.cc')
libstore_headers += files('platform/darwin.hh')
elif host_machine.system() == 'freebsd'
libstore_sources += files('platform/freebsd.cc')
libstore_headers += files('platform/freebsd.hh')
else
libstore_sources += files('platform/fallback.cc')
libstore_headers += files('platform/fallback.hh')
@ -202,23 +205,29 @@ foreach name, value : cpp_str_defines
]
endforeach
dependencies = [
libarchive,
liblixutil, # Internal.
seccomp,
sqlite,
sodium,
curl,
openssl,
aws_sdk,
aws_s3,
aws_sdk_transfer,
nlohmann_json,
]
if host_machine.system() == 'freebsd'
dependencies += [ libprocstat ]
endif
libstore = library(
'lixstore',
libstore_generated_headers,
libstore_sources,
dependencies : [
libarchive,
liblixutil, # Internal.
seccomp,
sqlite,
sodium,
curl,
openssl,
aws_sdk,
aws_s3,
aws_sdk_transfer,
nlohmann_json,
],
dependencies : dependencies,
cpp_args : cpp_args,
cpp_pch : cpp_pch,
install : true,

View file

@ -5,6 +5,8 @@
#include "platform/linux.hh"
#elif __APPLE__
#include "platform/darwin.hh"
#elif __FreeBSD__
#include "platform/freebsd.hh"
#else
#include "platform/fallback.hh"
#endif
@ -16,6 +18,8 @@ std::shared_ptr<LocalStore> LocalStore::makeLocalStore(const Params & params)
return std::shared_ptr<LocalStore>(new LinuxLocalStore(params));
#elif __APPLE__
return std::shared_ptr<LocalStore>(new DarwinLocalStore(params));
#elif __FreeBSD__
return std::shared_ptr<LocalStore>(new FreeBSDLocalStore(params));
#else
return std::shared_ptr<LocalStore>(new FallbackLocalStore(params));
#endif
@ -32,6 +36,8 @@ std::shared_ptr<LocalDerivationGoal> LocalDerivationGoal::makeLocalDerivationGoa
return std::make_shared<LinuxLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
#elif __APPLE__
return std::make_shared<DarwinLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
#elif __FreeBSD__
return std::make_shared<FreeBSDLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
#else
return std::make_shared<FallbackLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
#endif
@ -53,6 +59,10 @@ std::shared_ptr<LocalDerivationGoal> LocalDerivationGoal::makeLocalDerivationGoa
return std::make_shared<DarwinLocalDerivationGoal>(
drvPath, drv, wantedOutputs, worker, buildMode
);
#elif __FreeBSD__
return std::make_shared<FreeBSDLocalDerivationGoal>(
drvPath, drv, wantedOutputs, worker, buildMode
);
#else
return std::make_shared<FallbackLocalDerivationGoal>(
drvPath, drv, wantedOutputs, worker, buildMode

View file

@ -0,0 +1,142 @@
#include "platform/freebsd.hh"
#include "regex.hh"
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <libprocstat.h>
namespace nix {
static void readSysctlRoots(const char * name, UncheckedRoots & unchecked)
{
size_t len = 0;
std::string value;
if (int err = sysctlbyname(name, nullptr, &len, nullptr, 0) < 0) {
if (err == ENOENT || err == EACCES) {
return;
} else {
throw SysError(err, "sysctlbyname %1%", name);
}
}
value.resize(len, ' ');
if (int err = sysctlbyname(name, value.data(), &len, nullptr, 0) < 0) {
if (err == ENOENT || err == EACCES) {
return;
} else {
throw SysError(err, "sysctlbyname %1%", name);
}
}
for (auto & path : tokenizeString<Strings>(value, ";")) {
unchecked[path].emplace(fmt("{{sysctl:%1%}}", name));
}
}
struct ProcstatDeleter
{
void operator()(struct procstat * ps)
{
procstat_close(ps);
}
};
template<auto del>
struct ProcstatReferredDeleter
{
struct procstat * ps;
ProcstatReferredDeleter(struct procstat * ps) : ps(ps) {}
template<typename T>
void operator()(T * p)
{
del(ps, p);
}
};
void FreeBSDLocalStore::findPlatformRoots(UncheckedRoots & unchecked)
{
readSysctlRoots("kern.module_path", unchecked);
auto storePathRegex = regex::storePathRegex(storeDir);
auto ps = std::unique_ptr<struct procstat, ProcstatDeleter>(procstat_open_sysctl());
if (!ps) {
throw SysError("procstat_open_sysctl");
}
auto procs = std::unique_ptr<struct kinfo_proc[], ProcstatReferredDeleter<procstat_freeprocs>>(
nullptr, ps.get()
);
auto files = std::unique_ptr<struct filestat_list, ProcstatReferredDeleter<procstat_freefiles>>(
nullptr, ps.get()
);
unsigned int numprocs = 0;
procs.reset(procstat_getprocs(ps.get(), KERN_PROC_PROC, 0, &numprocs));
if (!procs || numprocs == 0) {
throw SysError("procstat_getprocs");
};
for (unsigned int procidx = 0; procidx < numprocs; procidx++) {
// Includes file descriptors, executable, cwd,
// and mmapped files (including dynamic libraries)
files.reset(procstat_getfiles(ps.get(), &procs[procidx], 1));
// We only have permission if we're root so just skip it if we fail
if (!files) {
continue;
}
for (struct filestat * file = files->stqh_first; file; file = file->next.stqe_next) {
if (!file->fs_path) {
continue;
}
std::string role;
if (file->fs_uflags & PS_FST_UFLAG_CTTY) {
role = "ctty";
} else if (file->fs_uflags & PS_FST_UFLAG_CDIR) {
role = "cwd";
} else if (file->fs_uflags & PS_FST_UFLAG_JAIL) {
role = "jail";
} else if (file->fs_uflags & PS_FST_UFLAG_RDIR) {
role = "root";
} else if (file->fs_uflags & PS_FST_UFLAG_TEXT) {
role = "text";
} else if (file->fs_uflags & PS_FST_UFLAG_TRACE) {
role = "trace";
} else if (file->fs_uflags & PS_FST_UFLAG_MMAP) {
role = "mmap";
} else {
role = fmt("fd/%1%", file->fs_fd);
}
unchecked[file->fs_path].emplace(fmt("{procstat:%1%/%2%}", procs[procidx].ki_pid, role)
);
}
auto env_name = fmt("{procstat:%1%/env}", procs[procidx].ki_pid);
// No need to free, the buffer is reused on next call and deallocated in procstat_close
char ** env = procstat_getenvv(ps.get(), &procs[procidx], 0);
if (env == nullptr) {
continue;
}
for (size_t i = 0; env[i]; i++) {
auto envString = std::string(env[i]);
auto envEnd = std::sregex_iterator{};
for (auto match =
std::sregex_iterator{envString.begin(), envString.end(), storePathRegex};
match != envEnd;
match++)
{
unchecked[match->str()].emplace(env_name);
}
}
}
}
}

View file

@ -0,0 +1,47 @@
#pragma once
///@file
#include "build/local-derivation-goal.hh"
#include "gc-store.hh"
#include "local-store.hh"
namespace nix {
/**
* FreeBSD-specific implementation of LocalStore
*/
class FreeBSDLocalStore : public LocalStore
{
public:
FreeBSDLocalStore(const Params & params)
: StoreConfig(params)
, LocalFSStoreConfig(params)
, LocalStoreConfig(params)
, Store(params)
, LocalFSStore(params)
, LocalStore(params)
{
}
FreeBSDLocalStore(const std::string scheme, std::string path, const Params & params)
: FreeBSDLocalStore(params)
{
throw UnimplementedError("FreeBSDLocalStore");
}
private:
void findPlatformRoots(UncheckedRoots & unchecked) override;
};
/**
* FreeBSD-specific implementation of LocalDerivationGoal
*/
class FreeBSDLocalDerivationGoal : public LocalDerivationGoal
{
public:
using LocalDerivationGoal::LocalDerivationGoal;
private:
};
}