diff options
Diffstat (limited to 'src')
42 files changed, 309 insertions, 194 deletions
diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 94a4b7922..9beea5aa2 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -164,28 +164,30 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) return res.finish(); } -SourcePath lookupFileArg(EvalState & state, std::string_view s) +SourcePath lookupFileArg(EvalState & state, std::string_view fileArg) { - if (EvalSettings::isPseudoUrl(s)) { - auto storePath = fetchers::downloadTarball( - state.store, EvalSettings::resolvePseudoUrl(s), "source", false).tree.storePath; + if (EvalSettings::isPseudoUrl(fileArg)) { + auto const url = EvalSettings::resolvePseudoUrl(fileArg); + auto const downloaded = fetchers::downloadTarball( + state.store, + url, + /* name */ "source", + /* locked */ false + ); + StorePath const storePath = downloaded.tree.storePath; return state.rootPath(CanonPath(state.store->toRealPath(storePath))); - } - - else if (s.starts_with("flake:")) { + } else if (fileArg.starts_with("flake:")) { experimentalFeatureSettings.require(Xp::Flakes); - auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false); + static constexpr size_t FLAKE_LEN = std::string_view("flake:").size(); + auto flakeRef = parseFlakeRef(std::string(fileArg.substr(FLAKE_LEN)), {}, true, false); auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath; return state.rootPath(CanonPath(state.store->toRealPath(storePath))); - } - - else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') { - Path p(s.substr(1, s.size() - 2)); + } else if (fileArg.size() > 2 && fileArg.at(0) == '<' && fileArg.at(fileArg.size() - 1) == '>') { + Path p(fileArg.substr(1, fileArg.size() - 2)); return state.findFile(p); + } else { + return state.rootPath(CanonPath::fromCwd(fileArg)); } - - else - return state.rootPath(CanonPath::fromCwd(s)); } } diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 6359b2579..08a4b65e4 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -28,6 +28,26 @@ private: std::map<std::string, std::string> autoArgs; }; -SourcePath lookupFileArg(EvalState & state, std::string_view s); +/** @brief Resolve an argument that is generally a file, but could be something that + * is easy to resolve to a file, like a <lookup path> or a tarball URL. + * + * In particular, this will resolve and fetch pseudo-URLs starting with + * @c channel:, flakerefs starting with @c flake:, and anything that + * @ref nix::fetchers::downloadTarball() can take. + * + * Non-absolute files are looked up relative to the current directory(?) + * FIXME: the process's current directory or EvalState's current directory? + * + * @param state The nix::EvalState to base settings, store, and nixPath from. + * + * @param fileArg The the path-ish to resolve. + * + * @return A nix::SourcePath to the resolved and fetched file. + * + * @exception nix::FileTransferError from nix::fetchers::downloadTarball(). Probably others. + * + * @exception nix::ThrownError for failed search path lookup. Probably others. + */ +SourcePath lookupFileArg(EvalState & state, std::string_view fileArg); } diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index 615f70945..46bdd411b 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -105,9 +105,14 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths() fmt("while evaluating the flake output attribute '%s'", attrPath))) { return { *derivedPathWithInfo }; + } else { + throw Error( + "expected flake output attribute '%s' to be a derivation or path but found %s: %s", + attrPath, + showType(v), + ValuePrinter(*this->state, v, errorPrintOptions) + ); } - else - throw Error("flake output attribute '%s' is not a derivation or path", attrPath); } auto drvPath = attr->forceDerivation(); diff --git a/src/libcmd/lix-cmd.pc.in b/src/libcmd/lix-cmd.pc.in new file mode 100644 index 000000000..f2e3f9d5e --- /dev/null +++ b/src/libcmd/lix-cmd.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Lix (libcmd) +Description: Lix Package Manager (libcmd) +Version: @PACKAGE_VERSION@ +Requires: lix-base lix-util +Libs: -L${libdir} -llixcmd +Cflags: -I${includedir}/lix/libcmd diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build index 5a0e61503..4da1b496b 100644 --- a/src/libcmd/meson.build +++ b/src/libcmd/meson.build @@ -39,7 +39,7 @@ libcmd_generated_headers = [ ] libcmd = library( - 'nixcmd', + 'lixcmd', libcmd_generated_headers, libcmd_sources, dependencies : [ @@ -59,13 +59,13 @@ libcmd = library( install_rpath : libdir, ) -install_headers(libcmd_headers, subdir : 'nix', preserve_path : true) +install_headers(libcmd_headers, subdir : 'lix/libcmd', preserve_path : true) custom_target( command : [ 'cp', '@INPUT@', '@OUTPUT@' ], input : libcmd_generated_headers, output : '@PLAINNAME@', install : true, - install_dir : includedir / 'nix', + install_dir : includedir / 'lix/libcmd', ) liblixcmd = declare_dependency( @@ -76,8 +76,8 @@ liblixcmd = declare_dependency( # FIXME: not using the pkg-config module because it creates way too many deps # while meson migration is in progress, and we want to not include boost here configure_file( - input : 'nix-cmd.pc.in', - output : 'nix-cmd.pc', + input : 'lix-cmd.pc.in', + output : 'lix-cmd.pc', install_dir : libdir / 'pkgconfig', configuration : { 'prefix' : prefix, diff --git a/src/libcmd/nix-cmd.pc.in b/src/libcmd/nix-cmd.pc.in deleted file mode 100644 index 39575f222..000000000 --- a/src/libcmd/nix-cmd.pc.in +++ /dev/null @@ -1,9 +0,0 @@ -prefix=@prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: Nix -Description: Nix Package Manager -Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lnixcmd -Cflags: -I${includedir}/nix -std=c++2a diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index d3567e021..41589cda1 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -1,6 +1,8 @@ #include <cstdio> #include <iostream> #include <string> +#include <string_view> +#include <cerrno> #ifdef READLINE #include <readline/history.h> @@ -176,15 +178,42 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT if (!s) return false; - write_history(historyFile.c_str()); + this->writeHistory(); input += s; input += '\n'; return true; } +void ReadlineLikeInteracter::writeHistory() +{ + int ret = write_history(historyFile.c_str()); + int writeHistErr = errno; + + if (ret == 0) { + return; + } + + // If the open fails, editline returns EOF. If the close fails, editline + // forwards the return value of fclose(), which is EOF on error. + // readline however, returns the errno. + // So if we didn't get exactly EOF, then consider the return value the error + // code; otherwise use the errno we saved above. + // https://github.com/troglobit/editline/issues/66 + if (ret != EOF) { + writeHistErr = ret; + } + + // In any of these cases, we should explicitly ignore the error, but log + // them so the user isn't confused why their history is getting eaten. + + std::string_view const errMsg(std::strerror(writeHistErr)); + warn("ignoring error writing repl history to %s: %s", this->historyFile, errMsg); + +} + ReadlineLikeInteracter::~ReadlineLikeInteracter() { - write_history(historyFile.c_str()); + this->writeHistory(); } AutomationInteracter::Guard AutomationInteracter::init(detail::ReplCompleterMixin *) diff --git a/src/libcmd/repl-interacter.hh b/src/libcmd/repl-interacter.hh index c31b1a1e6..8f815fceb 100644 --- a/src/libcmd/repl-interacter.hh +++ b/src/libcmd/repl-interacter.hh @@ -42,6 +42,11 @@ public: } virtual Guard init(detail::ReplCompleterMixin * repl) override; virtual bool getLine(std::string & input, ReplPromptType promptType) override; + /** Writes the current history to the history file. + * + * This function logs but ignores errors from readline's write_history(). + */ + virtual void writeHistory(); virtual ~ReadlineLikeInteracter() override; }; diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 525c25560..46b6d57ed 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -90,7 +90,8 @@ struct NixRepl Strings loadedFiles; std::function<AnnotatedValues()> getValues; - const static int envSize = 32768; + // Uses 8MiB of memory. It's fine. + const static int envSize = 1 << 20; std::shared_ptr<StaticEnv> staticEnv; Env * env; int displ; @@ -375,6 +376,9 @@ StringSet NixRepl::completePrefix(const std::string & prefix) // Quietly ignore evaluation errors. } catch (BadURL & e) { // Quietly ignore BadURL flake-related errors. + } catch (SysError & e) { + // Quietly ignore system errors which can for example be raised by + // a non-existent file being `import`-ed. } } @@ -854,6 +858,11 @@ void NixRepl::loadReplOverlays() replInitFilesFunction->determinePos(noPos) ); + // n.b. this does in fact load the stuff into the environment twice (once + // from the superset of the environment returned by repl-overlays and once + // from the thing itself), but it's not fixable because clearEnv here could + // lead to dangling references to the old environment in thunks. + // https://git.lix.systems/lix-project/lix/issues/337#issuecomment-3745 addAttrsToScope(newAttrs); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 65f0a7938..a8b37325b 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1115,7 +1115,7 @@ void EvalState::evalFile(const SourcePath & path_, Value & v, bool mustBeTrivial return; } - printTalkative("evaluating file '%1%'", resolvedPath); + debug("evaluating file '%1%'", resolvedPath); Expr * e = nullptr; auto j = fileParseCache.find(resolvedPath); diff --git a/src/libexpr/flake/meson.build b/src/libexpr/flake/meson.build index 3ecc30f4e..cce1b0c75 100644 --- a/src/libexpr/flake/meson.build +++ b/src/libexpr/flake/meson.build @@ -4,5 +4,5 @@ libexpr_generated_headers += custom_target( output : '@PLAINNAME@.gen.hh', capture : true, install : true, - install_dir : includedir / 'nix/flake', + install_dir : includedir / 'lix/libexpr/flake', ) diff --git a/src/libexpr/lix-expr.pc.in b/src/libexpr/lix-expr.pc.in new file mode 100644 index 000000000..5e850976d --- /dev/null +++ b/src/libexpr/lix-expr.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Lix libexpr +Description: Lix Package Manager (libexpr) +Version: @PACKAGE_VERSION@ +Requires: lix-base lix-util lix-fetchers lix-store bdw-gc +Libs: -L${libdir} -llixexpr +Cflags: -I${includedir}/lix/libexpr diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 099279d56..fda6fde2c 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -15,7 +15,7 @@ parser_tab = custom_target( # NOTE(Qyriad): Meson doesn't support installing only part of a custom target, so we add # an install script below which removes parser-tab.cc. install : true, - install_dir : includedir / 'nix', + install_dir : includedir / 'lix/libexpr', ) lexer_tab = custom_target( @@ -37,7 +37,7 @@ lexer_tab = custom_target( # NOTE(Qyriad): Meson doesn't support installing only part of a custom target, so we add # an install script below which removes lexer-tab.cc. install : true, - install_dir : includedir / 'nix', + install_dir : includedir / 'lix/libexpr', ) # TODO(Qyriad): When the parser and lexer are rewritten this should be removed. @@ -59,7 +59,7 @@ foreach header : [ 'imported-drv-to-derivation.nix', 'fetchurl.nix' ] output : '@PLAINNAME@.gen.hh', capture : true, install : true, - install_dir : includedir / 'nix', + install_dir : includedir / 'lix/libexpr', ) endforeach subdir('flake') @@ -127,7 +127,7 @@ libexpr_headers = files( ) libexpr = library( - 'nixexpr', + 'lixexpr', libexpr_sources, parser_tab, lexer_tab, @@ -152,7 +152,7 @@ libexpr = library( install_headers( libexpr_headers, - subdir : 'nix', + subdir : 'lix/libexpr', preserve_path : true, ) @@ -164,8 +164,8 @@ liblixexpr = declare_dependency( # FIXME: not using the pkg-config module because it creates way too many deps # while meson migration is in progress, and we want to not include boost here configure_file( - input : 'nix-expr.pc.in', - output : 'nix-expr.pc', + input : 'lix-expr.pc.in', + output : 'lix-expr.pc', install_dir : libdir / 'pkgconfig', configuration : { 'prefix' : prefix, diff --git a/src/libexpr/nix-expr.pc.in b/src/libexpr/nix-expr.pc.in deleted file mode 100644 index 60ffb5dba..000000000 --- a/src/libexpr/nix-expr.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: Nix -Description: Nix Package Manager -Version: @PACKAGE_VERSION@ -Requires: nix-store bdw-gc -Libs: -L${libdir} -lnixexpr -Cflags: -I${includedir}/nix -std=c++2a diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 77e7cf22b..64a52dfd6 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -242,7 +242,7 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v // No need to call staticEnv.sort(), because // args[0]->attrs is already sorted. - printTalkative("evaluating file '%1%'", path); + debug("evaluating file '%1%'", path); Expr * e = state.parseExprFromFile(resolveExprPath(path), staticEnv); e->eval(state, *env, v); diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 1eec8b316..36692aafb 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -36,7 +36,7 @@ static RegisterPrimOp primop_hasContext({ > **Example** > - > Many operations require a string context to be empty because they are intended only to work with "regular" strings, and also to help users avoid unintentionally loosing track of string context elements. + > Many operations require a string context to be empty because they are intended only to work with "regular" strings, and also to help users avoid unintentionally losing track of string context elements. > `builtins.hasContext` can help create better domain-specific errors in those case. > > ```nix @@ -137,14 +137,14 @@ static RegisterPrimOp primop_addDrvOutputDependencies({ .name = "__addDrvOutputDependencies", .args = {"s"}, .doc = R"( - Create a copy of the given string where a single consant string context element is turned into a "derivation deep" string context element. + Create a copy of the given string where a single constant string context element is turned into a "derivation deep" string context element. The store path that is the constant string context element should point to a valid derivation, and end in `.drv`. The original string context element must not be empty or have multiple elements, and it must not have any other type of element other than a constant or derivation deep element. The latter is supported so this function is idempotent. - This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-addDrvOutputDependencies). + This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-unsafeDiscardOutputDependency). )", .fun = prim_addDrvOutputDependencies }); diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc index 0c8ecac9d..672e1e0bc 100644 --- a/src/libfetchers/cache.cc +++ b/src/libfetchers/cache.cc @@ -34,7 +34,16 @@ struct CacheImpl : Cache auto state(_state.lock()); auto dbPath = getCacheDir() + "/nix/fetcher-cache-v1.sqlite"; - createDirs(dirOf(dbPath)); + // It would be silly to fail fetcher operations if e.g. the user has no + // XDG_CACHE_HOME and their HOME directory doesn't exist. + // We'll warn the user if that happens, but fallback to an in-memory + // backend for the SQLite database. + try { + createDirs(dirOf(dbPath)); + } catch (SysError const & ex) { + warn("ignoring error initializing Lix fetcher cache: %s", ex.what()); + dbPath = ":memory:"; + } state->db = SQLite(dbPath); state->db.isCache(); diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index dc8df2217..210912cb6 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -133,6 +133,10 @@ std::pair<Tree, Input> Input::fetch(ref<Store> store) const } auto [storePath, input] = [&]() -> std::pair<StorePath, Input> { + // *sighs*, we print the base URL, rather than the full URL because the Nixpkgs + // fileset lib tests assume that fetching shallow and non-shallow prints exactly the + // same stderr... + printInfo("fetching %s input '%s'", this->getType(), this->toURL().base); try { return scheme->fetch(store, *this); } catch (Error & e) { diff --git a/src/libfetchers/lix-fetchers.pc.in b/src/libfetchers/lix-fetchers.pc.in new file mode 100644 index 000000000..fa213b769 --- /dev/null +++ b/src/libfetchers/lix-fetchers.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Lix libfetchers +Description: Lix Package Manager (libfetchers) +Version: @PACKAGE_VERSION@ +Requires: lix-base lix-util +Libs: -L${libdir} -llixfetchers +Cflags: -I${includedir}/lix/libfetchers diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index 3809701b7..dbb85b84c 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -23,7 +23,7 @@ libfetchers_headers = files( ) libfetchers = library( - 'nixfetchers', + 'lixfetchers', libfetchers_sources, dependencies : [ liblixstore, @@ -35,7 +35,21 @@ libfetchers = library( install_rpath : libdir, ) -install_headers(libfetchers_headers, subdir : 'nix', preserve_path : true) +install_headers(libfetchers_headers, subdir : 'lix/libfetchers', preserve_path : true) + +# FIXME: not using the pkg-config module because it creates way too many deps +# while meson migration is in progress, and we want to not include boost here +configure_file( + input : 'lix-fetchers.pc.in', + output : 'lix-fetchers.pc', + install_dir : libdir / 'pkgconfig', + configuration : { + 'prefix' : prefix, + 'libdir' : libdir, + 'includedir' : includedir, + 'PACKAGE_VERSION' : meson.project_version(), + }, +) liblixfetchers = declare_dependency( include_directories : include_directories('.'), diff --git a/src/libmain/lix-main.pc.in b/src/libmain/lix-main.pc.in new file mode 100644 index 000000000..0ceaec393 --- /dev/null +++ b/src/libmain/lix-main.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Lix libmain +Description: Lix Package Manager (libmain) +Version: @PACKAGE_VERSION@ +Requires: lix-base lix-util +Libs: -L${libdir} -llixmain +Cflags: -I${includedir}/lix/libmain diff --git a/src/libmain/meson.build b/src/libmain/meson.build index 3f50b158d..b17247a9d 100644 --- a/src/libmain/meson.build +++ b/src/libmain/meson.build @@ -14,7 +14,7 @@ libmain_headers = files( ) libmain = library( - 'nixmain', + 'lixmain', libmain_sources, dependencies : [ liblixutil, @@ -25,7 +25,7 @@ libmain = library( install_rpath : libdir, ) -install_headers(libmain_headers, subdir : 'nix', preserve_path : true) +install_headers(libmain_headers, subdir : 'lix/libmain', preserve_path : true) liblixmain = declare_dependency( include_directories : include_directories('.'), @@ -35,8 +35,8 @@ liblixmain = declare_dependency( # FIXME: not using the pkg-config module because it creates way too many deps # while meson migration is in progress, and we want to not include boost here configure_file( - input : 'nix-main.pc.in', - output : 'nix-main.pc', + input : 'lix-main.pc.in', + output : 'lix-main.pc', install_dir : libdir / 'pkgconfig', configuration : { 'prefix' : prefix, diff --git a/src/libmain/nix-main.pc.in b/src/libmain/nix-main.pc.in deleted file mode 100644 index fb3ead6fa..000000000 --- a/src/libmain/nix-main.pc.in +++ /dev/null @@ -1,9 +0,0 @@ -prefix=@prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: Nix -Description: Nix Package Manager -Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lnixmain -Cflags: -I${includedir}/nix -std=c++2a diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 3f24da276..5fa5deb7c 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -786,13 +786,6 @@ void DerivationGoal::tryLocalBuild() { } -static void chmod_(const Path & path, mode_t mode) -{ - if (chmod(path.c_str(), mode) == -1) - throw SysError("setting permissions on '%s'", path); -} - - /* Move/rename path 'src' to 'dst'. Temporarily make 'src' writable if it's a directory and we're not root (to be able to update the directory's parent link ".."). */ @@ -803,12 +796,12 @@ static void movePath(const Path & src, const Path & dst) bool changePerm = (geteuid() && S_ISDIR(st.st_mode) && !(st.st_mode & S_IWUSR)); if (changePerm) - chmod_(src, st.st_mode | S_IWUSR); + chmodPath(src, st.st_mode | S_IWUSR); renameFile(src, dst); if (changePerm) - chmod_(dst, st.st_mode); + chmodPath(dst, st.st_mode); } diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index cdbd0f5a7..9be780212 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -34,7 +34,6 @@ /* Includes required for chroot support. */ #if __linux__ #include <sys/ioctl.h> -#include "linux/fchmodat2-compat.hh" #include <net/if.h> #include <netinet/ip.h> #include <sys/mman.h> @@ -44,6 +43,7 @@ #include <sys/prctl.h> #include <sys/syscall.h> #if HAVE_SECCOMP +#include "linux/fchmodat2-compat.hh" #include <seccomp.h> #endif #define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) @@ -272,12 +272,6 @@ void LocalDerivationGoal::tryLocalBuild() started(); } -static void chmod_(const Path & path, mode_t mode) -{ - if (chmod(path.c_str(), mode) == -1) - throw SysError("setting permissions on '%s'", path); -} - /* Move/rename path 'src' to 'dst'. Temporarily make 'src' writable if it's a directory and we're not root (to be able to update the @@ -289,12 +283,12 @@ static void movePath(const Path & src, const Path & dst) bool changePerm = (geteuid() && S_ISDIR(st.st_mode) && !(st.st_mode & S_IWUSR)); if (changePerm) - chmod_(src, st.st_mode | S_IWUSR); + chmodPath(src, st.st_mode | S_IWUSR); renameFile(src, dst); if (changePerm) - chmod_(dst, st.st_mode); + chmodPath(dst, st.st_mode); } @@ -696,7 +690,7 @@ void LocalDerivationGoal::startBuilder() instead.) */ Path chrootTmpDir = chrootRootDir + "/tmp"; createDirs(chrootTmpDir); - chmod_(chrootTmpDir, 01777); + chmodPath(chrootTmpDir, 01777); /* Create a /etc/passwd with entries for the build user and the nobody account. The latter is kind of a hack to support @@ -721,7 +715,7 @@ void LocalDerivationGoal::startBuilder() build user. */ Path chrootStoreDir = chrootRootDir + worker.store.storeDir; createDirs(chrootStoreDir); - chmod_(chrootStoreDir, 01775); + chmodPath(chrootStoreDir, 01775); if (buildUser && chown(chrootStoreDir.c_str(), 0, buildUser->getGID()) == -1) throw SysError("cannot change ownership of '%1%'", chrootStoreDir); @@ -1618,7 +1612,6 @@ void LocalDerivationGoal::chownToBuilder(const Path & path) void setupSeccomp() { #if __linux__ - if (!settings.filterSyscalls) return; #if HAVE_SECCOMP scmp_filter_ctx ctx; @@ -1684,15 +1677,18 @@ void setupSeccomp() seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) != 0) throw SysError("unable to add seccomp rule"); - if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, settings.allowNewPrivileges ? 0 : 1) != 0) + // Set the NO_NEW_PRIVS prctl flag. + // This both makes loading seccomp filters work for unprivileged users, + // and is an additional security measure in its own right. + if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 1) != 0) throw SysError("unable to set 'no new privileges' seccomp attribute"); if (seccomp_load(ctx) != 0) throw SysError("unable to load seccomp BPF program"); #else - throw Error( - "seccomp is not supported on this platform; " - "you can bypass this error by setting the option 'filter-syscalls' to false, but note that untrusted builds can then create setuid binaries!"); + // Still set the no-new-privileges flag if libseccomp is not available. + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) + throw SysError("PR_SET_NO_NEW_PRIVS failed"); #endif #endif } @@ -1862,7 +1858,7 @@ void LocalDerivationGoal::runChild() auto dst = chrootRootDir + i.first; createDirs(dirOf(dst)); writeFile(dst, std::string_view((const char *) sh, sizeof(sh))); - chmod_(dst, 0555); + chmodPath(dst, 0555); } else #endif doBind(i.second.source, chrootRootDir + i.first, i.second.optional); @@ -1900,7 +1896,7 @@ void LocalDerivationGoal::runChild() /* Make sure /dev/pts/ptmx is world-writable. With some Linux versions, it is created with permissions 0. */ - chmod_(chrootRootDir + "/dev/pts/ptmx", 0666); + chmodPath(chrootRootDir + "/dev/pts/ptmx", 0666); } else { if (errno != EINVAL) throw SysError("mounting /dev/pts"); @@ -1911,7 +1907,7 @@ void LocalDerivationGoal::runChild() /* Make /etc unwritable */ if (!parsedDrv->useUidRange()) - chmod_(chrootRootDir + "/etc", 0555); + chmodPath(chrootRootDir + "/etc", 0555); /* Unshare this mount namespace. This is necessary because pivot_root() below changes the root of the mount @@ -1960,10 +1956,6 @@ void LocalDerivationGoal::runChild() throw SysError("setuid failed"); setUser = false; - - // Make sure we can't possibly gain new privileges in the sandbox - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) - throw SysError("PR_SET_NO_NEW_PRIVS failed"); } #endif @@ -2016,8 +2008,6 @@ void LocalDerivationGoal::runChild() /* Fill in the arguments. */ Strings args; - std::string builder = "invalid"; - #if __APPLE__ /* This has to appear before import statements. */ std::string sandboxProfile = "(version 1)\n"; @@ -2138,15 +2128,9 @@ void LocalDerivationGoal::runChild() _exit(1); } } +#endif - builder = drv->builder; args.push_back(std::string(baseNameOf(drv->builder))); -#else - if (!drv->isBuiltin()) { - builder = drv->builder; - args.push_back(std::string(baseNameOf(drv->builder))); - } -#endif for (auto & i : drv->args) args.push_back(rewriteStrings(i, inputRewrites)); @@ -2201,9 +2185,9 @@ void LocalDerivationGoal::runChild() posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL); } - posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data()); + posix_spawn(NULL, drv->builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data()); #else - execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data()); + execve(drv->builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data()); #endif throw SysError("executing '%1%'", drv->builder); diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 242b4ddc6..420fc8bfe 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -527,7 +527,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store, auto drvs = WorkerProto::Serialise<DerivedPaths>::read(*store, rconn); BuildMode mode = bmNormal; if (GET_PROTOCOL_MINOR(clientVersion) >= 15) { - mode = (BuildMode) readInt(from); + mode = buildModeFromInteger(readInt(from)); /* Repairing is not atomic, so disallowed for "untrusted" clients. @@ -551,7 +551,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store, case WorkerProto::Op::BuildPathsWithResults: { auto drvs = WorkerProto::Serialise<DerivedPaths>::read(*store, rconn); BuildMode mode = bmNormal; - mode = (BuildMode) readInt(from); + mode = buildModeFromInteger(readInt(from)); /* Repairing is not atomic, so disallowed for "untrusted" clients. @@ -582,7 +582,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store, * correctly. */ readDerivation(from, *store, drv, Derivation::nameFromPath(drvPath)); - BuildMode buildMode = (BuildMode) readInt(from); + BuildMode buildMode = buildModeFromInteger(readInt(from)); logger->startWork(); auto drvType = drv.type(); diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 8856d8fae..85789f6b5 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -912,29 +912,6 @@ public: )"}; #if __linux__ - Setting<bool> filterSyscalls{ - this, true, "filter-syscalls", - R"( - Whether to prevent certain dangerous system calls, such as - creation of setuid/setgid files or adding ACLs or extended - attributes. Only disable this if you're aware of the - security implications. - )"}; - - Setting<bool> allowNewPrivileges{ - this, false, "allow-new-privileges", - R"( - (Linux-specific.) By default, builders on Linux cannot acquire new - privileges by calling setuid/setgid programs or programs that have - file capabilities. For example, programs such as `sudo` or `ping` - will fail. (Note that in sandbox builds, no such programs are - available unless you bind-mount them into the sandbox via the - `sandbox-paths` option.) You can allow the use of such programs by - enabling this option. This is impure and usually undesirable, but - may be useful in certain scenarios (e.g. to spin up containers or - set up userspace network interfaces in tests). - )"}; - Setting<StringSet> ignoredAcls{ this, {"security.selinux", "system.nfs4_acl", "security.csm"}, "ignored-acls", R"( diff --git a/src/libstore/linux/fchmodat2-compat.hh b/src/libstore/linux/fchmodat2-compat.hh index b05da6786..6ad8a5578 100644 --- a/src/libstore/linux/fchmodat2-compat.hh +++ b/src/libstore/linux/fchmodat2-compat.hh @@ -20,18 +20,16 @@ #pragma once ///@file -#if HAVE_SECCOMP -# if defined(__alpha__) -# define NIX_SYSCALL_FCHMODAT2 562 -# elif defined(__x86_64__) && SIZE_MAX == 0xFFFFFFFF // x32 -# define NIX_SYSCALL_FCHMODAT2 1073742276 -# elif defined(__mips__) && defined(__mips64) && defined(_ABIN64) // mips64/n64 -# define NIX_SYSCALL_FCHMODAT2 5452 -# elif defined(__mips__) && defined(__mips64) && defined(_ABIN32) // mips64/n32 -# define NIX_SYSCALL_FCHMODAT2 6452 -# elif defined(__mips__) && defined(_ABIO32) // mips32 -# define NIX_SYSCALL_FCHMODAT2 4452 -# else -# define NIX_SYSCALL_FCHMODAT2 452 -# endif -#endif // HAVE_SECCOMP +#if defined(__alpha__) +# define NIX_SYSCALL_FCHMODAT2 562 +#elif defined(__x86_64__) && SIZE_MAX == 0xFFFFFFFF // x32 +# define NIX_SYSCALL_FCHMODAT2 1073742276 +#elif defined(__mips__) && defined(__mips64) && defined(_ABIN64) // mips64/n64 +# define NIX_SYSCALL_FCHMODAT2 5452 +#elif defined(__mips__) && defined(__mips64) && defined(_ABIN32) // mips64/n32 +# define NIX_SYSCALL_FCHMODAT2 6452 +#elif defined(__mips__) && defined(_ABIO32) // mips32 +# define NIX_SYSCALL_FCHMODAT2 4452 +#else +# define NIX_SYSCALL_FCHMODAT2 452 +#endif diff --git a/src/libstore/lix-store.pc.in b/src/libstore/lix-store.pc.in new file mode 100644 index 000000000..69c323a28 --- /dev/null +++ b/src/libstore/lix-store.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Lix libstore +Description: Lix Package Manager (libstore) +Version: @PACKAGE_VERSION@ +Requires: lix-base lix-util +Libs: -L${libdir} -llixstore -llixutil +Cflags: -I${includedir}/lix/libstore diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 5fde92dd0..98549f6d9 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -6,7 +6,7 @@ foreach header : [ 'schema.sql', 'ca-specific-schema.sql' ] output : '@PLAINNAME@.gen.hh', capture : true, install : true, - install_dir : includedir / 'nix', + install_dir : includedir / 'lix/libstore', ) endforeach @@ -201,7 +201,7 @@ foreach name, value : cpp_str_defines endforeach libstore = library( - 'nixstore', + 'lixstore', libstore_generated_headers, libstore_sources, dependencies : [ @@ -210,7 +210,6 @@ libstore = library( seccomp, sqlite, sodium, - seccomp, curl, openssl, aws_sdk, @@ -224,7 +223,7 @@ libstore = library( install_rpath : libdir, ) -install_headers(libstore_headers, subdir : 'nix', preserve_path : true) +install_headers(libstore_headers, subdir : 'lix/libstore', preserve_path : true) # Used by libfetchers. liblixstore = declare_dependency( @@ -235,8 +234,8 @@ liblixstore = declare_dependency( # FIXME: not using the pkg-config module because it creates way too many deps # while meson migration is in progress, and we want to not include boost here configure_file( - input : 'nix-store.pc.in', - output : 'nix-store.pc', + input : 'lix-store.pc.in', + output : 'lix-store.pc', install_dir : libdir / 'pkgconfig', configuration : { 'prefix' : prefix, diff --git a/src/libstore/nix-store.pc.in b/src/libstore/nix-store.pc.in deleted file mode 100644 index dc42d0bca..000000000 --- a/src/libstore/nix-store.pc.in +++ /dev/null @@ -1,9 +0,0 @@ -prefix=@prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: Nix -Description: Nix Package Manager -Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lnixstore -lnixutil -Cflags: -I${includedir}/nix -std=c++2a diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 509b0fa68..ed3566f5e 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -22,6 +22,14 @@ using json = nlohmann::json; namespace nix { +BuildMode buildModeFromInteger(int raw) { + switch (raw) { + case bmNormal: return bmNormal; + case bmRepair: return bmRepair; + case bmCheck: return bmCheck; + default: throw Error("Invalid BuildMode"); + } +} bool Store::isInStore(PathView path) const { diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 47e644fed..745fce594 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -90,6 +90,9 @@ const uint32_t exportMagic = 0x4558494e; enum BuildMode { bmNormal, bmRepair, bmCheck }; +/** Checks that a build mode is a valid one, then returns it */ +BuildMode buildModeFromInteger(int); + enum TrustedFlag : bool { NotTrusted = false, Trusted = true }; struct BuildResult; diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 00536c1e1..a18c54ebf 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -389,13 +389,4 @@ void copyNAR(Source & source, Sink & sink) } -void copyPath(const Path & from, const Path & to) -{ - auto source = sinkToSource([&](Sink & sink) { - dumpPath(from, sink); - }); - restorePath(to, *source); -} - - } diff --git a/src/libutil/archive.hh b/src/libutil/archive.hh index 2cf164a41..017b6633c 100644 --- a/src/libutil/archive.hh +++ b/src/libutil/archive.hh @@ -124,8 +124,6 @@ void restorePath(const Path & path, Source & source); */ void copyNAR(Source & source, Sink & sink); -void copyPath(const Path & from, const Path & to); - inline constexpr std::string_view narVersionMagic1 = "nix-archive-1"; diff --git a/src/libutil/lix-util.pc.in b/src/libutil/lix-util.pc.in new file mode 100644 index 000000000..cd749aabb --- /dev/null +++ b/src/libutil/lix-util.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Lix libutil +Description: Lix Package Manager (libutil) +Version: @PACKAGE_VERSION@ +Requires: lix-base lix-util +Libs: -L${libdir} -llixutil +Cflags: -I${includedir}/lix/libutil diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 13266f6bd..58e0bd062 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -100,7 +100,7 @@ libutil_headers = files( ) libutil = library( - 'nixutil', + 'lixutil', libutil_sources, dependencies : [ aws_sdk, @@ -118,7 +118,21 @@ libutil = library( install : true, ) -install_headers(libutil_headers, subdir : 'nix', preserve_path : true) +install_headers(libutil_headers, subdir : 'lix/libutil', preserve_path : true) + +# FIXME: not using the pkg-config module because it creates way too many deps +# while meson migration is in progress, and we want to not include boost here +configure_file( + input : 'lix-util.pc.in', + output : 'lix-util.pc', + install_dir : libdir / 'pkgconfig', + configuration : { + 'prefix' : prefix, + 'libdir' : libdir, + 'includedir' : includedir, + 'PACKAGE_VERSION' : meson.project_version(), + }, +) # Used by libstore and libfetchers. liblixutil = declare_dependency( diff --git a/src/libutil/util.cc b/src/libutil/util.cc index bc2dd1802..2c0fcc897 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -184,6 +184,11 @@ Path canonPath(PathView path, bool resolveSymlinks) return s.empty() ? "/" : std::move(s); } +void chmodPath(const Path & path, mode_t mode) +{ + if (chmod(path.c_str(), mode) == -1) + throw SysError("setting permissions on '%s'", path); +} Path dirOf(const PathView path) { @@ -1799,8 +1804,7 @@ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode) bind(fdSocket.get(), path); - if (chmod(path.c_str(), mode) == -1) - throw SysError("changing permissions on '%1%'", path); + chmodPath(path.c_str(), mode); if (listen(fdSocket.get(), 100) == -1) throw SysError("cannot listen on socket '%1%'", path); diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 914d6cce0..14868776c 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -78,6 +78,13 @@ Path absPath(Path path, Path canonPath(PathView path, bool resolveSymlinks = false); /** + * Change the permissions of a path + * Not called `chmod` as it shadows and could be confused with + * `int chmod(char *, mode_t)`, which does not handle errors + */ +void chmodPath(const Path & path, mode_t mode); + +/** * @return The directory part of the given canonical path, i.e., * everything before the final `/`. If the path is the root or an * immediate child thereof (e.g., `/foo`), this means `/` diff --git a/src/lix-base.pc.in b/src/lix-base.pc.in new file mode 100644 index 000000000..925a7753a --- /dev/null +++ b/src/lix-base.pc.in @@ -0,0 +1,8 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Lix base +Description: Lix Package Manager (config.hh fixup) +Version: @PACKAGE_VERSION@ +Cflags: -I${includedir}/lix diff --git a/src/nix/main.cc b/src/nix/main.cc index 64755d445..83d697326 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -363,11 +363,6 @@ void mainWrapped(int argc, char * * argv) setLogFormat("bar"); settings.verboseBuild = false; - if (isatty(STDERR_FILENO)) { - verbosity = lvlNotice; - } else { - verbosity = lvlInfo; - } NixArgs args; diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 55f833833..3e47fc8f0 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -111,7 +111,23 @@ struct CmdUpgradeNix : MixDryRun, EvalCommand if (pathExists(canonProfileDir + "/manifest.nix")) { - std::string nixEnvCmd = settings.nixBinDir + "/nix-env"; + // {settings.nixBinDir}/nix-env is a symlink to a {settings.nixBinDir}/nix, which *then* + // is a symlink to /nix/store/meow-nix/bin/nix. We want /nix/store/meow-nix/bin/nix-env. + Path const nixInStore = canonPath(settings.nixBinDir + "/nix-env", true); + Path const nixEnvCmd = dirOf(nixInStore) + "/nix-env"; + + // First remove the existing Nix, then use that Nix by absolute path to + // install the new one, in case the new and old versions aren't considered + // to be "the same package" by nix-env's logic (e.g., if their pnames differ). + Strings removeArgs = { + "--uninstall", + nixEnvCmd, + "--profile", + this->profileDir, + }; + printTalkative("running %s %s", nixEnvCmd, concatStringsSep(" ", removeArgs)); + runProgram(nixEnvCmd, false, removeArgs); + Strings upgradeArgs = { "--profile", this->profileDir, |