diff --git a/meson.build b/meson.build index 0e74520ca..56f447501 100644 --- a/meson.build +++ b/meson.build @@ -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 # diff --git a/src/libstore/meson.build b/src/libstore/meson.build index fa363bd19..5416bd2b5 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -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, diff --git a/src/libstore/platform.cc b/src/libstore/platform.cc index d10d33f0e..72757e39b 100644 --- a/src/libstore/platform.cc +++ b/src/libstore/platform.cc @@ -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::makeLocalStore(const Params & params) return std::shared_ptr(new LinuxLocalStore(params)); #elif __APPLE__ return std::shared_ptr(new DarwinLocalStore(params)); +#elif __FreeBSD__ + return std::shared_ptr(new FreeBSDLocalStore(params)); #else return std::shared_ptr(new FallbackLocalStore(params)); #endif @@ -32,6 +36,8 @@ std::shared_ptr LocalDerivationGoal::makeLocalDerivationGoa return std::make_shared(drvPath, wantedOutputs, worker, buildMode); #elif __APPLE__ return std::make_shared(drvPath, wantedOutputs, worker, buildMode); +#elif __FreeBSD__ + return std::make_shared(drvPath, wantedOutputs, worker, buildMode); #else return std::make_shared(drvPath, wantedOutputs, worker, buildMode); #endif @@ -53,6 +59,10 @@ std::shared_ptr LocalDerivationGoal::makeLocalDerivationGoa return std::make_shared( drvPath, drv, wantedOutputs, worker, buildMode ); +#elif __FreeBSD__ + return std::make_shared( + drvPath, drv, wantedOutputs, worker, buildMode + ); #else return std::make_shared( drvPath, drv, wantedOutputs, worker, buildMode diff --git a/src/libstore/platform/freebsd.cc b/src/libstore/platform/freebsd.cc new file mode 100644 index 000000000..bdba1abf5 --- /dev/null +++ b/src/libstore/platform/freebsd.cc @@ -0,0 +1,142 @@ +#include "platform/freebsd.hh" +#include "regex.hh" +#include +#include +#include +#include +#include +#include + +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(value, ";")) { + unchecked[path].emplace(fmt("{{sysctl:%1%}}", name)); + } +} + +struct ProcstatDeleter +{ + void operator()(struct procstat * ps) + { + procstat_close(ps); + } +}; + +template +struct ProcstatReferredDeleter +{ + struct procstat * ps; + + ProcstatReferredDeleter(struct procstat * ps) : ps(ps) {} + + template + 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(procstat_open_sysctl()); + if (!ps) { + throw SysError("procstat_open_sysctl"); + } + + auto procs = std::unique_ptr>( + nullptr, ps.get() + ); + auto files = std::unique_ptr>( + 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); + } + } + } +} +} diff --git a/src/libstore/platform/freebsd.hh b/src/libstore/platform/freebsd.hh new file mode 100644 index 000000000..99aff3c10 --- /dev/null +++ b/src/libstore/platform/freebsd.hh @@ -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: +}; + +}