diff options
Diffstat (limited to 'src/libstore/platform/freebsd.cc')
-rw-r--r-- | src/libstore/platform/freebsd.cc | 142 |
1 files changed, 142 insertions, 0 deletions
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 <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); + } + } + } +} +} |