forked from lix-project/lix
Compare commits
30 commits
12ac97e526
...
2cc6abbaeb
Author | SHA1 | Date | |
---|---|---|---|
kloenk | 2cc6abbaeb | ||
alois31 | e7957baa51 | ||
alois31 | d18d1e85ca | ||
alois31 | 7b5cc94ee2 | ||
jade | 9923fb6dd9 | ||
jade | a9c610fe37 | ||
jade | f82a2a9aaa | ||
jade | e1059bfa34 | ||
jade | d5c670ad01 | ||
jade | 16ea19ced8 | ||
jade | 7be0d237e0 | ||
jade | e715e5fd31 | ||
jade | 068576042b | ||
jade | d194939ff5 | ||
jade | 74fb2e8c47 | ||
jade | ac28cff28f | ||
alois31 | 1d6fd94cf9 | ||
jade | d0b28f0e74 | ||
jade | 38e4e69633 | ||
Pierre Bourdon | 248ecb11af | ||
jade | 73898cad0e | ||
jade | 59b5965bbf | ||
jade | 211f79d4a2 | ||
jade | 5f6eb6eb44 | ||
jade | d9345d8836 | ||
jade | 6939ffc9f9 | ||
jade | 479055aee8 | ||
Pierre Bourdon | f7b6552699 | ||
alois31 | 3c0434999e | ||
Qyriad | f46194faa2 |
18
.github/CODEOWNERS
vendored
18
.github/CODEOWNERS
vendored
|
@ -1,18 +0,0 @@
|
||||||
# Pull requests concerning the listed files will automatically invite the respective maintainers as reviewers.
|
|
||||||
# This file is not used for denoting any kind of ownership, but is merely a tool for handling notifications.
|
|
||||||
#
|
|
||||||
# Merge permissions are required for maintaining an entry in this file.
|
|
||||||
# For documentation on this mechanism, see https://help.github.com/articles/about-codeowners/
|
|
||||||
|
|
||||||
# Default reviewers if nothing else matches
|
|
||||||
* @edolstra
|
|
||||||
|
|
||||||
# This file
|
|
||||||
.github/CODEOWNERS @edolstra
|
|
||||||
|
|
||||||
# Public documentation
|
|
||||||
/doc @fricklerhandwerk
|
|
||||||
*.md @fricklerhandwerk
|
|
||||||
|
|
||||||
# Libstore layer
|
|
||||||
/src/libstore @thufschmitt
|
|
2
.github/ISSUE_TEMPLATE/installer.md
vendored
2
.github/ISSUE_TEMPLATE/installer.md
vendored
|
@ -9,7 +9,7 @@ assignees: ''
|
||||||
|
|
||||||
## Platform
|
## Platform
|
||||||
|
|
||||||
<!-- select the platform on which you tried to install Nix -->
|
<!-- select the platform on which you tried to install Lix -->
|
||||||
|
|
||||||
- [ ] Linux: <!-- state your distribution, e.g. Arch Linux, Ubuntu, ... -->
|
- [ ] Linux: <!-- state your distribution, e.g. Arch Linux, Ubuntu, ... -->
|
||||||
- [ ] macOS
|
- [ ] macOS
|
||||||
|
|
11
.github/ISSUE_TEMPLATE/missing_documentation.md
vendored
11
.github/ISSUE_TEMPLATE/missing_documentation.md
vendored
|
@ -19,9 +19,10 @@ assignees: ''
|
||||||
|
|
||||||
<!-- make sure this issue is not redundant or obsolete -->
|
<!-- make sure this issue is not redundant or obsolete -->
|
||||||
|
|
||||||
- [ ] checked [latest Nix manual] \([source])
|
- [ ] checked [latest Lix manual] \([source]\)
|
||||||
- [ ] checked [open documentation issues and pull requests] for possible duplicates
|
- [ ] checked [documentation issues] and [recent documentation changes] for possible duplicates
|
||||||
|
|
||||||
[latest Nix manual]: https://nixos.org/manual/nix/unstable/
|
[latest Nix manual]: https://docs.lix.systems/manual/lix/nightly
|
||||||
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/src
|
[source]: https://git.lix.systems/lix-project/lix/src/main/doc/manual/src
|
||||||
[open documentation issues and pull requests]: https://github.com/NixOS/nix/labels/documentation
|
[documentation issues]: https://git.lix.systems/lix-project/lix/issues?labels=151&state=all
|
||||||
|
[recent documentation changes]: https://gerrit.lix.systems/q/p:lix+path:%22%5Edoc/manual/.*%22
|
||||||
|
|
35
.github/STALE-BOT.md
vendored
35
.github/STALE-BOT.md
vendored
|
@ -1,35 +0,0 @@
|
||||||
# Stale bot information
|
|
||||||
|
|
||||||
- Thanks for your contribution!
|
|
||||||
- To remove the stale label, just leave a new comment.
|
|
||||||
- _How to find the right people to ping?_ → [`git blame`](https://git-scm.com/docs/git-blame) to the rescue! (or GitHub's history and blame buttons.)
|
|
||||||
- You can always ask for help on [our Discourse Forum](https://discourse.nixos.org/) or on [Matrix - #nix:nixos.org](https://matrix.to/#/#nix:nixos.org).
|
|
||||||
|
|
||||||
## Suggestions for PRs
|
|
||||||
|
|
||||||
1. GitHub sometimes doesn't notify people who commented / reviewed a PR previously, when you (force) push commits. If you have addressed the reviews you can [officially ask for a review](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/requesting-a-pull-request-review) from those who commented to you or anyone else.
|
|
||||||
2. If it is unfinished but you plan to finish it, please mark it as a draft.
|
|
||||||
3. If you don't expect to work on it any time soon, closing it with a short comment may encourage someone else to pick up your work.
|
|
||||||
4. To get things rolling again, rebase the PR against the target branch and address valid comments.
|
|
||||||
5. If you need a review to move forward, ask in [the Discourse thread for PRs that need help](https://discourse.nixos.org/t/prs-in-distress/3604).
|
|
||||||
6. If all you need is a merge, check the git history to find and [request reviews](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/requesting-a-pull-request-review) from people who usually merge related contributions.
|
|
||||||
|
|
||||||
## Suggestions for issues
|
|
||||||
|
|
||||||
1. If it is resolved (either for you personally, or in general), please consider closing it.
|
|
||||||
2. If this might still be an issue, but you are not interested in promoting its resolution, please consider closing it while encouraging others to take over and reopen an issue if they care enough.
|
|
||||||
3. If you still have interest in resolving it, try to ping somebody who you believe might have an interest in the topic. Consider discussing the problem in [our Discourse Forum](https://discourse.nixos.org/).
|
|
||||||
4. As with all open source projects, your best option is to submit a Pull Request that addresses this issue. We :heart: this attitude!
|
|
||||||
|
|
||||||
**Memorandum on closing issues**
|
|
||||||
|
|
||||||
Don't be afraid to close an issue that holds valuable information. Closed issues stay in the system for people to search, read, cross-reference, or even reopen--nothing is lost! Closing obsolete issues is an important way to help maintainers focus their time and effort.
|
|
||||||
|
|
||||||
## Useful GitHub search queries
|
|
||||||
|
|
||||||
- [Open PRs with any stale-bot interaction](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+commenter%3Aapp%2Fstale+)
|
|
||||||
- [Open PRs with any stale-bot interaction and `stale`](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+commenter%3Aapp%2Fstale+label%3A%22stale%22)
|
|
||||||
- [Open PRs with any stale-bot interaction and NOT `stale`](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+commenter%3Aapp%2Fstale+-label%3A%22stale%22+)
|
|
||||||
- [Open Issues with any stale-bot interaction](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+commenter%3Aapp%2Fstale+)
|
|
||||||
- [Open Issues with any stale-bot interaction and `stale`](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+commenter%3Aapp%2Fstale+label%3A%22stale%22+)
|
|
||||||
- [Open Issues with any stale-bot interaction and NOT `stale`](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+commenter%3Aapp%2Fstale+-label%3A%22stale%22+)
|
|
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
|
@ -1,6 +0,0 @@
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
23
.github/labeler.yml
vendored
23
.github/labeler.yml
vendored
|
@ -1,23 +0,0 @@
|
||||||
"documentation":
|
|
||||||
- doc/manual/*
|
|
||||||
- src/nix/**/*.md
|
|
||||||
|
|
||||||
"store":
|
|
||||||
- src/libstore/store-api.*
|
|
||||||
- src/libstore/*-store.*
|
|
||||||
|
|
||||||
"fetching":
|
|
||||||
- src/libfetchers/**/*
|
|
||||||
|
|
||||||
"repl":
|
|
||||||
- src/libcmd/repl.*
|
|
||||||
- src/nix/repl.*
|
|
||||||
|
|
||||||
"new-cli":
|
|
||||||
- src/nix/**/*
|
|
||||||
|
|
||||||
"with-tests":
|
|
||||||
# Unit tests
|
|
||||||
- src/*/tests/**/*
|
|
||||||
# Functional and integration tests
|
|
||||||
- tests/functional/**/*
|
|
9
.github/stale.yml
vendored
9
.github/stale.yml
vendored
|
@ -1,9 +0,0 @@
|
||||||
# Configuration for probot-stale - https://github.com/probot/stale
|
|
||||||
daysUntilStale: 180
|
|
||||||
daysUntilClose: false
|
|
||||||
exemptLabels:
|
|
||||||
- "critical"
|
|
||||||
- "never-stale"
|
|
||||||
staleLabel: "stale"
|
|
||||||
markComment: false
|
|
||||||
closeComment: false
|
|
|
@ -1,56 +1,8 @@
|
||||||
diff --git a/darwin_stop_world.c b/darwin_stop_world.c
|
|
||||||
index 0468aaec..b348d869 100644
|
|
||||||
--- a/darwin_stop_world.c
|
|
||||||
+++ b/darwin_stop_world.c
|
|
||||||
@@ -356,6 +356,7 @@ GC_INNER void GC_push_all_stacks(void)
|
|
||||||
int nthreads = 0;
|
|
||||||
word total_size = 0;
|
|
||||||
mach_msg_type_number_t listcount = (mach_msg_type_number_t)THREAD_TABLE_SZ;
|
|
||||||
+ size_t stack_limit;
|
|
||||||
if (!EXPECT(GC_thr_initialized, TRUE))
|
|
||||||
GC_thr_init();
|
|
||||||
|
|
||||||
@@ -411,6 +412,19 @@ GC_INNER void GC_push_all_stacks(void)
|
|
||||||
GC_push_all_stack_sections(lo, hi, p->traced_stack_sect);
|
|
||||||
}
|
|
||||||
if (altstack_lo) {
|
|
||||||
+ // When a thread goes into a coroutine, we lose its original sp until
|
|
||||||
+ // control flow returns to the thread.
|
|
||||||
+ // While in the coroutine, the sp points outside the thread stack,
|
|
||||||
+ // so we can detect this and push the entire thread stack instead,
|
|
||||||
+ // as an approximation.
|
|
||||||
+ // We assume that the coroutine has similarly added its entire stack.
|
|
||||||
+ // This could be made accurate by cooperating with the application
|
|
||||||
+ // via new functions and/or callbacks.
|
|
||||||
+ stack_limit = pthread_get_stacksize_np(p->id);
|
|
||||||
+ if (altstack_lo >= altstack_hi || altstack_lo < altstack_hi - stack_limit) { // sp outside stack
|
|
||||||
+ altstack_lo = altstack_hi - stack_limit;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
total_size += altstack_hi - altstack_lo;
|
|
||||||
GC_push_all_stack(altstack_lo, altstack_hi);
|
|
||||||
}
|
|
||||||
diff --git a/include/gc.h b/include/gc.h
|
|
||||||
index edab6c22..f2c61282 100644
|
|
||||||
--- a/include/gc.h
|
|
||||||
+++ b/include/gc.h
|
|
||||||
@@ -2172,6 +2172,11 @@ GC_API void GC_CALL GC_win32_free_heap(void);
|
|
||||||
(*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_ignore_off_page)
|
|
||||||
#endif /* _AMIGA && !GC_AMIGA_MAKINGLIB */
|
|
||||||
|
|
||||||
+#if !__APPLE__
|
|
||||||
+/* Patch doesn't work on apple */
|
|
||||||
+#define NIX_BOEHM_PATCH_VERSION 1
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
diff --git a/pthread_stop_world.c b/pthread_stop_world.c
|
diff --git a/pthread_stop_world.c b/pthread_stop_world.c
|
||||||
index b5d71e62..aed7b0bf 100644
|
index 2b45489..0e6d8ef 100644
|
||||||
--- a/pthread_stop_world.c
|
--- a/pthread_stop_world.c
|
||||||
+++ b/pthread_stop_world.c
|
+++ b/pthread_stop_world.c
|
||||||
@@ -768,6 +768,8 @@ STATIC void GC_restart_handler(int sig)
|
@@ -776,6 +776,8 @@ STATIC void GC_restart_handler(int sig)
|
||||||
/* world is stopped. Should not fail if it isn't. */
|
/* world is stopped. Should not fail if it isn't. */
|
||||||
GC_INNER void GC_push_all_stacks(void)
|
GC_INNER void GC_push_all_stacks(void)
|
||||||
{
|
{
|
||||||
|
@ -59,20 +11,23 @@ index b5d71e62..aed7b0bf 100644
|
||||||
GC_bool found_me = FALSE;
|
GC_bool found_me = FALSE;
|
||||||
size_t nthreads = 0;
|
size_t nthreads = 0;
|
||||||
int i;
|
int i;
|
||||||
@@ -851,6 +853,37 @@ GC_INNER void GC_push_all_stacks(void)
|
@@ -868,6 +870,40 @@ GC_INNER void GC_push_all_stacks(void)
|
||||||
hi = p->altstack + p->altstack_size;
|
hi = p->altstack + p->altstack_size;
|
||||||
|
# endif
|
||||||
/* FIXME: Need to scan the normal stack too, but how ? */
|
/* FIXME: Need to scan the normal stack too, but how ? */
|
||||||
/* FIXME: Assume stack grows down */
|
|
||||||
+ } else {
|
+ } else {
|
||||||
+ #ifdef HAVE_PTHREAD_ATTR_GET_NP
|
+ #ifdef HAVE_PTHREAD_ATTR_GET_NP
|
||||||
+ if (!pthread_attr_init(&pattr)
|
+ if (pthread_attr_init(&pattr) != 0) {
|
||||||
+ || !pthread_attr_get_np(p->id, &pattr))
|
+ ABORT("GC_push_all_stacks: pthread_attr_init failed!");
|
||||||
+#else /* HAVE_PTHREAD_GETATTR_NP */
|
+ }
|
||||||
+ if (pthread_getattr_np(p->id, &pattr))
|
+ if (pthread_attr_get_np(p->id, &pattr) != 0) {
|
||||||
+#endif
|
+ ABORT("GC_push_all_stacks: pthread_attr_get_np failed!");
|
||||||
+ {
|
+ }
|
||||||
|
+ #else
|
||||||
|
+ if (pthread_getattr_np(p->id, &pattr)) {
|
||||||
+ ABORT("GC_push_all_stacks: pthread_getattr_np failed!");
|
+ ABORT("GC_push_all_stacks: pthread_getattr_np failed!");
|
||||||
+ }
|
+ }
|
||||||
|
+ #endif
|
||||||
+ if (pthread_attr_getstacksize(&pattr, &stack_limit)) {
|
+ if (pthread_attr_getstacksize(&pattr, &stack_limit)) {
|
||||||
+ ABORT("GC_push_all_stacks: pthread_attr_getstacksize failed!");
|
+ ABORT("GC_push_all_stacks: pthread_attr_getstacksize failed!");
|
||||||
+ }
|
+ }
|
||||||
|
@ -95,5 +50,5 @@ index b5d71e62..aed7b0bf 100644
|
||||||
+ #error "STACK_GROWS_UP not supported in boost_coroutine2 (as of june 2021), so we don't support it in Nix."
|
+ #error "STACK_GROWS_UP not supported in boost_coroutine2 (as of june 2021), so we don't support it in Nix."
|
||||||
+ #endif
|
+ #endif
|
||||||
}
|
}
|
||||||
GC_push_all_stack_sections(lo, hi, traced_stack_sect);
|
# ifdef STACKPTR_CORRECTOR_AVAILABLE
|
||||||
# ifdef STACK_GROWS_UP
|
if (GC_sp_corrector != 0)
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
diff --git a/include/gc_allocator.h b/include/gc_allocator.h
|
|
||||||
index 597c7f13..587286be 100644
|
|
||||||
--- a/include/gc_allocator.h
|
|
||||||
+++ b/include/gc_allocator.h
|
|
||||||
@@ -312,6 +312,7 @@ public:
|
|
||||||
|
|
||||||
template<>
|
|
||||||
class traceable_allocator<void> {
|
|
||||||
+public:
|
|
||||||
typedef size_t size_type;
|
|
||||||
typedef ptrdiff_t difference_type;
|
|
||||||
typedef void* pointer;
|
|
|
@ -60,6 +60,10 @@ jade:
|
||||||
forgejo: jade
|
forgejo: jade
|
||||||
github: lf-
|
github: lf-
|
||||||
|
|
||||||
|
kloenk:
|
||||||
|
forgejo: kloenk
|
||||||
|
github: kloenk
|
||||||
|
|
||||||
lovesegfault:
|
lovesegfault:
|
||||||
github: lovesegfault
|
github: lovesegfault
|
||||||
|
|
||||||
|
|
14
doc/manual/rl-next/multiline-log-format.md
Normal file
14
doc/manual/rl-next/multiline-log-format.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
synopsis: Add log formats `multiline` and `multiline-with-logs`
|
||||||
|
cls: [1369]
|
||||||
|
credits: [kloenk]
|
||||||
|
category: Improvements
|
||||||
|
---
|
||||||
|
|
||||||
|
Added two new log formats (`multiline` and `multiline-with-logs`) that display
|
||||||
|
current activities below each other for better visibility.
|
||||||
|
|
||||||
|
These formats attempt to use the maximum available lines
|
||||||
|
(defaulting to 25 if unable to determine) and print up to that many lines.
|
||||||
|
The status bar is displayed as the first line, with each subsequent
|
||||||
|
activity on its own line.
|
|
@ -78,6 +78,15 @@ Most commands in Lix accept the following command-line options:
|
||||||
|
|
||||||
Display the raw logs, with the progress bar at the bottom.
|
Display the raw logs, with the progress bar at the bottom.
|
||||||
|
|
||||||
|
- `multiline`
|
||||||
|
|
||||||
|
Display a progress bar during the builds and in the lines below that one line per activity.
|
||||||
|
|
||||||
|
|
||||||
|
- `multiline-with-logs`
|
||||||
|
|
||||||
|
Displayes the raw logs, with a progress bar and activities each in a new line at the bottom.
|
||||||
|
|
||||||
- <span id="opt-no-build-output">[`--no-build-output`](#opt-no-build-output)</span> / `-Q`
|
- <span id="opt-no-build-output">[`--no-build-output`](#opt-no-build-output)</span> / `-Q`
|
||||||
|
|
||||||
By default, output written by builders to standard output and standard error is echoed to the Lix command's standard error.
|
By default, output written by builders to standard output and standard error is echoed to the Lix command's standard error.
|
||||||
|
|
|
@ -12,44 +12,51 @@ The unit tests are defined using the [googletest] and [rapidcheck] frameworks.
|
||||||
> An example of some files, demonstrating much of what is described below
|
> An example of some files, demonstrating much of what is described below
|
||||||
>
|
>
|
||||||
> ```
|
> ```
|
||||||
> src
|
|
||||||
> ├── libexpr
|
|
||||||
> │ ├── value/context.hh
|
|
||||||
> │ ├── value/context.cc
|
|
||||||
> │ │
|
|
||||||
> │ …
|
|
||||||
> └── tests
|
|
||||||
> │ ├── value/context.hh
|
|
||||||
> │ ├── value/context.cc
|
|
||||||
> │ │
|
|
||||||
> │ …
|
|
||||||
> │
|
|
||||||
> ├── unit-test-data
|
|
||||||
> │ ├── libstore
|
|
||||||
> │ │ ├── worker-protocol/content-address.bin
|
|
||||||
> │ │ …
|
|
||||||
> │ …
|
|
||||||
> …
|
> …
|
||||||
|
> ├── src
|
||||||
|
> │ ├── libexpr
|
||||||
|
> │ │ ├── …
|
||||||
|
> │ │ ├── value
|
||||||
|
> │ │ │ ├── context.cc
|
||||||
|
> │ │ │ └── context.hh
|
||||||
|
> │ … …
|
||||||
|
> ├── tests
|
||||||
|
> │ …
|
||||||
|
> │ └── unit
|
||||||
|
> │ ├── libcmd
|
||||||
|
> │ │ └── args.cc
|
||||||
|
> │ ├── libexpr
|
||||||
|
> │ │ ├── …
|
||||||
|
> │ │ └── value
|
||||||
|
> │ │ ├── context.cc
|
||||||
|
> │ │ └── print.cc
|
||||||
|
> │ ├── libexpr-support
|
||||||
|
> │ │ └── tests
|
||||||
|
> │ │ ├── libexpr.hh
|
||||||
|
> │ │ └── value
|
||||||
|
> │ │ ├── context.cc
|
||||||
|
> │ │ └── context.hh
|
||||||
|
> │ ├── libstore
|
||||||
|
> │ │ ├── common-protocol.cc
|
||||||
|
> │ │ ├── data
|
||||||
|
> │ │ │ ├── libstore
|
||||||
|
> │ │ │ │ ├── common-protocol
|
||||||
|
> │ │ │ │ │ ├── content-address.bin
|
||||||
|
> │ │ │ │ │ ├── drv-output.bin
|
||||||
|
> … … … … … …
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
<!-- FIXME(Lix): this might get renamed to liblixexpr, etc? -->
|
The unit tests for each Lix library (`liblixexpr`, `liblixstore`, etc..) live inside a directory `src/${library_shortname}/tests` within the directory for the library (`src/${library_shortname}`).
|
||||||
|
|
||||||
The unit tests for each Lix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `src/${library_shortname}/tests` within the directory for the library (`src/${library_shortname}`).
|
The data is in `tests/unit/LIBNAME/data/LIBNAME`, with one subdir per library, with the same name as where the code goes.
|
||||||
|
For example, `liblixstore` code is in `src/libstore`, and its test data is in `tests/unit/libstore/data/libstore`.
|
||||||
The data is in `unit-test-data`, with one subdir per library, with the same name as where the code goes.
|
The path to the unit test data directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`.
|
||||||
For example, `libnixstore` code is in `src/libstore`, and its test data is in `unit-test-data/libstore`.
|
|
||||||
The path to the `unit-test-data` directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`.
|
|
||||||
|
|
||||||
> **Note**
|
|
||||||
> Due to the way googletest works, downstream unit test executables will actually include and re-run upstream library tests.
|
|
||||||
> Therefore it is important that the same value for `_NIX_TEST_UNIT_DATA` be used with the tests for each library.
|
|
||||||
> That is why we have the test data nested within a single `unit-test-data` directory.
|
|
||||||
|
|
||||||
### Running tests
|
### Running tests
|
||||||
|
|
||||||
You can run the whole testsuite with `just test` (see justfile for exact invocation of meson), and if you want to run just one test suite, use `just test --suite installcheck functional-init` where `installcheck` is the name of the test suite in this case and `functional-init` is the name of the test.
|
You can run the whole testsuite with `just test` (see justfile for exact invocation of meson), and if you want to run just one test suite, use `just test --suite installcheck functional-init` where `installcheck` is the name of the test suite in this case and `functional-init` is the name of the test.
|
||||||
|
|
||||||
To get a list of tests, use `meson test -C build --list`.
|
To get a list of tests, use `meson test -C build --list` (or `just test --list` for short).
|
||||||
|
|
||||||
For `installcheck` specifically, first run `just install` before running the test suite (this is due to meson limitations that don't let us put a dependency on installing before doing the test).
|
For `installcheck` specifically, first run `just install` before running the test suite (this is due to meson limitations that don't let us put a dependency on installing before doing the test).
|
||||||
|
|
||||||
|
@ -74,20 +81,27 @@ See [below](#characterization-testing-1) for a broader discussion of characteriz
|
||||||
Like with the functional characterization, `_NIX_TEST_ACCEPT=1` is also used.
|
Like with the functional characterization, `_NIX_TEST_ACCEPT=1` is also used.
|
||||||
For example:
|
For example:
|
||||||
```shell-session
|
```shell-session
|
||||||
$ _NIX_TEST_ACCEPT=1 make libstore-tests-exe_RUN
|
$ _NIX_TEST_ACCEPT=1 just test --suite check libstore-unit-tests
|
||||||
...
|
...
|
||||||
[ SKIPPED ] WorkerProtoTest.string_read
|
../tests/unit/libstore/common-protocol.cc:27: Skipped
|
||||||
[ SKIPPED ] WorkerProtoTest.string_write
|
Cannot read golden master because another test is also updating it
|
||||||
[ SKIPPED ] WorkerProtoTest.storePath_read
|
|
||||||
[ SKIPPED ] WorkerProtoTest.storePath_write
|
../tests/unit/libstore/common-protocol.cc:62: Skipped
|
||||||
|
Updating golden master
|
||||||
|
|
||||||
|
../tests/unit/libstore/common-protocol.cc:27: Skipped
|
||||||
|
Cannot read golden master because another test is also updating it
|
||||||
|
|
||||||
|
../tests/unit/libstore/common-protocol.cc:62: Skipped
|
||||||
|
Updating golden master
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
will regenerate the "golden master" expected result for the `libnixstore` characterization tests.
|
will regenerate the "golden master" expected result for the `liblixstore` characterization tests.
|
||||||
The characterization tests will mark themselves "skipped" since they regenerated the expected result instead of actually testing anything.
|
The characterization tests will mark themselves "skipped" since they regenerated the expected result instead of actually testing anything.
|
||||||
|
|
||||||
## Functional tests
|
## Functional tests
|
||||||
|
|
||||||
The functional tests reside under the `tests/functional` directory and are listed in `tests/functional/local.mk`.
|
The functional tests reside under the `tests/functional` directory and are listed in `tests/functional/meson.build`.
|
||||||
Each test is a bash script.
|
Each test is a bash script.
|
||||||
|
|
||||||
### Running the whole test suite
|
### Running the whole test suite
|
||||||
|
@ -185,7 +199,7 @@ edit it like so:
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="warning">
|
<div class="warning">
|
||||||
FIXME(meson): the command here may be incorrect for meson.
|
FIXME(meson): the command here is incorrect for meson and this whole functionality may need rebuilding.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Then, running the test with `./mk/debug-test.sh` will drop you into GDB once the script reaches that point:
|
Then, running the test with `./mk/debug-test.sh` will drop you into GDB once the script reaches that point:
|
||||||
|
@ -209,15 +223,11 @@ This technique is to include the exact output/behavior of a former version of Ni
|
||||||
|
|
||||||
For example, this technique is used for the language tests, to check both the printed final value if evaluation was successful, and any errors and warnings encountered.
|
For example, this technique is used for the language tests, to check both the printed final value if evaluation was successful, and any errors and warnings encountered.
|
||||||
|
|
||||||
<div class="warning">
|
|
||||||
FIXME(meson): this is incorrect for meson. `_NIX_TEST_ACCEPT=1` is still valid but the test invocation needs to change.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
It is frequently useful to regenerate the expected output.
|
It is frequently useful to regenerate the expected output.
|
||||||
To do that, rerun the failed test(s) with `_NIX_TEST_ACCEPT=1`.
|
To do that, rerun the failed test(s) with `_NIX_TEST_ACCEPT=1`.
|
||||||
For example:
|
For example:
|
||||||
```bash
|
```bash
|
||||||
_NIX_TEST_ACCEPT=1 make tests/functional/lang.sh.test
|
_NIX_TEST_ACCEPT=1 just test --suite installcheck -v functional-lang
|
||||||
```
|
```
|
||||||
|
|
||||||
An interesting situation to document is the case when these tests are "overfitted".
|
An interesting situation to document is the case when these tests are "overfitted".
|
||||||
|
@ -320,3 +330,135 @@ solved this need?
|
||||||
~~>
|
~~>
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## Magic environment variables
|
||||||
|
|
||||||
|
FIXME: maybe this section should be moved elsewhere or turned partially into user docs, but I just need a complete index for now.
|
||||||
|
I actually want to ban people calling getenv without writing documentation, and produce a comprehensive list of env-vars used by Lix and enforce it.
|
||||||
|
|
||||||
|
This is a non-exhaustive list of almost all environment variables, magic or not, accepted or used by various parts of the test suite as well as Lix itself.
|
||||||
|
Please add more if you find them.
|
||||||
|
|
||||||
|
I looked for these in the testsuite with the following bad regexes:
|
||||||
|
|
||||||
|
```
|
||||||
|
rg '(?:[^A-Za-z]|^)(_[A-Z][^-\[ }/:");$(]+)' -r '$1' --no-filename --only-matching tests | sort -u > vars.txt
|
||||||
|
rg '\$\{?([A-Z][^-\[ }/:");]+)' -r '$1' --no-filename --only-matching tests | sort -u > vars.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
I grepped `src/` for `get[eE]nv\("` to find the mentions in Lix code.
|
||||||
|
|
||||||
|
### Used by Lix testing support code
|
||||||
|
|
||||||
|
- `_NIX_TEST_ACCEPT` (optional) - Writes out the result of a characterization test as the new expected value.
|
||||||
|
**Expected value**: 1
|
||||||
|
|
||||||
|
- `_NIX_TEST_UNIT_DATA` - The path to the directory for the data for a given unit test suite.
|
||||||
|
|
||||||
|
**Expected value**: `tests/unit/libstore/data/libstore` or similar
|
||||||
|
|
||||||
|
|
||||||
|
### Used by Lix
|
||||||
|
|
||||||
|
- `_NIX_FORCE_HTTP` - Forces file URIs to be treated as remote ones.
|
||||||
|
|
||||||
|
Used by `src/libfetchers/git.cc`, `src/libstore/http-binary-cache-store.cc`,
|
||||||
|
`src/libstore/local-binary-cache-store.cc`. Seems to be for forcing Git
|
||||||
|
clones of `git+file://` URLs, making the HTTP binary
|
||||||
|
cache store accept `file://` URLs (presumably passing them to curl?), and
|
||||||
|
unknown reasons for the local binary cache.
|
||||||
|
|
||||||
|
FIXME(jade): is this obscuring a bug in https://git.lix.systems/lix-project/lix/issues/200?
|
||||||
|
|
||||||
|
**Expected value**: 1
|
||||||
|
- `NIX_ATTRS_SH_FILE`, `NIX_ATTRS_JSON_FILE` (output) - Set by Lix builders; see
|
||||||
|
`structuredAttrs` documentation.
|
||||||
|
- `NIX_BIN_DIR`, `NIX_STORE_DIR` (or its inconsistently-used old alias `NIX_STORE`), `NIX_DATA_DIR`,
|
||||||
|
`NIX_LOG_DIR`, `NIX_LOG_DIR`, `NIX_STATE_DIR`, `NIX_CONF_DIR` -
|
||||||
|
Overrides compile-time configuration of various locations used by Lix. See `src/libstore/globals.cc`.
|
||||||
|
|
||||||
|
**Expected value**: a directory
|
||||||
|
- `NIX_DAEMON_SOCKET_PATH` (optional) - Overrides the daemon socket path from `$NIX_STATE_DIR/daemon-socket/socket`.
|
||||||
|
|
||||||
|
**Expected value**: path to a socket
|
||||||
|
- `NIX_LOG_FD` (output) - An FD number for logs in `internal-json` format to be sent to.
|
||||||
|
Used for, mostly, "setPhase" in nixpkgs setup.sh, but can also be creatively used to print verbose log messages from derivations.
|
||||||
|
|
||||||
|
**Provided value**: number corresponding to an FD in the builder
|
||||||
|
- `NIX_PATH` - Search path for `<whatever>`. Documented elsewhere in the manual.
|
||||||
|
|
||||||
|
**Expected value**: `:` separated list of things that are not necessarily pointing to filesystem paths
|
||||||
|
- `NIX_REMOTE` - The default value of the Lix setting `store`.
|
||||||
|
|
||||||
|
**Expected value**: "daemon", usually. Could be "auto" or any other value acceptable in `store`.
|
||||||
|
- `NIX_BUILD_SHELL` - Documented elsewhere; the shell to invoke with `nix-shell` but not `nix develop`/`nix shell`.
|
||||||
|
The latter ignoring it altogether seems like a bug.
|
||||||
|
|
||||||
|
**Expected value**: the path to an executable shell
|
||||||
|
- `PRINT_PATH` - Undocumented. Used by `nix-prefetch-url` as an alternative form of `--print-path`. Why???
|
||||||
|
- `_NIX_IN_TEST` - If present with any value, makes `fetchClosure` accept file URLs in addition to HTTP ones. Why is this not `_NIX_FORCE_HTTP`??
|
||||||
|
|
||||||
|
Not used anywhere else.
|
||||||
|
- `NIX_ALLOW_EVAL` - Used by eval-cache tests to block evaluation if set to `0`.
|
||||||
|
|
||||||
|
**Expected value**: 1 or 0
|
||||||
|
- `EDITOR` - Used by `editorFor()`, which has some extremely sketchy editor-detection code for jumping to line numbers.
|
||||||
|
- `LISTEN_FDS` and `LISTEN_PID` - Used for systemd socket activation using the systemd socket activation protocol.
|
||||||
|
- `NIX_PAGER` (alternatively, `PAGER`) - Used to select a pager for Lix output. Why does this not use libutil `getEnv()`?
|
||||||
|
- `LESS` (output) - Sets the pager settings for `less` when invoked by Lix.
|
||||||
|
- `NIX_IGNORE_SYMLINK_STORE` - When set, Lix allows the store to be a symlink. Why do we support this?
|
||||||
|
|
||||||
|
Apparently [someone was using it enough to fix it](https://github.com/NixOS/nix/pull/4038).
|
||||||
|
- `NIX_SSL_CERT_FILE` (alternatively, `SSL_CERT_FILE`) - Used to set CA certificates for libcurl.
|
||||||
|
|
||||||
|
**Expected value**: "/etc/ssl/certs/ca-certificates.crt" or similar
|
||||||
|
- `NIX_REMOTE_SYSTEMS` - Used to set `builders`. Can we please deprecate this?
|
||||||
|
- `NIX_USER_CONF_FILES` - `:` separated list of config files to load before
|
||||||
|
`/nix/nix.conf` under each of `XDG_CONFIG_DIRS`.
|
||||||
|
- `NIX_CONFIG` - Newline separated configuration to load into Lix.
|
||||||
|
- `NIX_GET_COMPLETIONS` - Returns completions.
|
||||||
|
Unsure of the exact format, someone should document it; either way my shell never had any completions.
|
||||||
|
|
||||||
|
**Expected value**: number of completions to return.
|
||||||
|
- `IN_SYSTEMD` - Used to switch the logging format so that systemd gets the correct log levels. I think.
|
||||||
|
- `NIX_HELD_LOCKS` - Not used, what is this for?? We should surely remove it right after searching github?
|
||||||
|
- `GC_INITIAL_HEAP_SIZE` - Used to set the initial heap size, processed by boehmgc.
|
||||||
|
- `NIX_COUNT_CALLS` - Documented elsewhere; prints call counts for profiling purposes.
|
||||||
|
- `NIX_SHOW_STATS` - Documented elsewhere; prints various evaluation statistics like function calls, gc info, and similar.
|
||||||
|
- `NIX_SHOW_STATS_PATH` - Writes those statistics into a file at the given path instead of stdout. Undocumented.
|
||||||
|
- `NIX_SHOW_SYMBOLS` - Dumps the symbol table into the show-stats json output.
|
||||||
|
- `TERM` - If `dumb` or unset, disables ANSI colour output.
|
||||||
|
- `NO_COLOR`, `NOCOLOR` - Disables ANSI colour output.
|
||||||
|
- `_NIX_DEVELOPER_SHOW_UNKNOWN_LOCATIONS` - Highlights unknown locations in errors.
|
||||||
|
- `NIX_PROFILE` - Selects which profile `nix-env` will operate on. Documented elsewhere.
|
||||||
|
- `NIX_SSHOPTS` - Options passed to `ssh(1)` when using a ssh remote store.
|
||||||
|
Incorrectly documented on `nix-copy-closure` which is *surely* not the only place they are used??
|
||||||
|
- `_NIX_TEST_NO_LSOF` - Used on non-Linux, non-macOS platforms to disable using `lsof` when finding gc roots.
|
||||||
|
|
||||||
|
Since https://git.lix.systems/lix-project/lix/issues/156 was fixed, this should probably just be removed as it was a bad workaround for a macOS issue.
|
||||||
|
- `_NIX_TEST_GC_SYNC_1` - Path to a pipe that is used to block the GC briefly to validate invariants from the test suite.
|
||||||
|
- `_NIX_TEST_GC_SYNC_2` - Path to a pipe that is used to block the GC briefly to validate invariants from the test suite.
|
||||||
|
- `_NIX_TEST_FREE_SPACE_FILE` - Path to a file containing a decimal number with the free space that the GC is to believe it has.
|
||||||
|
- Various XDG vars
|
||||||
|
- `NIX_DEBUG_SQLITE_TRACES` - Dump all sqlite queries to the log at `notice` level.
|
||||||
|
- `_NIX_TEST_NO_SANDBOX` - Disables actually setting up the sandbox on macOS while leaving other logic the same. Unused on other platforms.
|
||||||
|
- `_NIX_TRACE_BUILT_OUTPUTS` - Dumps all the derivation paths alongside their outputs as lines into a file of the given name.
|
||||||
|
|
||||||
|
### Used by the functional test framework
|
||||||
|
|
||||||
|
- `NIX_DAEMON_PACKAGE` - Runs the test suite against an alternate Nix daemon with the current client.
|
||||||
|
|
||||||
|
**Expected value**: something like `/nix/store/...-nix-2.18.2`
|
||||||
|
- `NIX_CLIENT_PACKAGE` - Runs the test suite against an alternate Nix client with the current daemon.
|
||||||
|
|
||||||
|
**Expected value**: something like `/nix/store/...-nix-2.18.2`
|
||||||
|
- `NIX_TESTS_CA_BY_DEFAULT` - Pass `__contentAddressed`, `outputHashMode` and `outputHashAlgo` to builds of some input-addressed derivations in the test suite.
|
||||||
|
|
||||||
|
**Expected value**: 1
|
||||||
|
- `TEST_DATA` - Not an environment variable! This is used in repl characterization tests to refer to `tests/functional/repl_characterization/data`.
|
||||||
|
More specifically, that path is replaced with the string `$TEST_DATA` in output for reproducibility.
|
||||||
|
- `TEST_HOME` (output) - Set to the temporary directory that is set as `$HOME` inside the tests, underneath `$TEST_ROOT`.
|
||||||
|
- `TEST_ROOT` (output) - Set to the temporary directory that is created for each test to mess with.
|
||||||
|
- `_NIX_TEST_DAEMON_PID` (output) - Used to track the daemon pid to be able to kill it.
|
||||||
|
|
||||||
|
**Provided value**: Daemon pid as a base-10 integer, e.g. 2345
|
||||||
|
|
|
@ -1,32 +1,121 @@
|
||||||
# Multi-User Mode
|
# Multi-User Mode
|
||||||
|
|
||||||
To allow a Nix store to be shared safely among multiple users, it is
|
To allow a Nix store to be shared safely among multiple users, it is important that users cannot meaningfully influence the execution of derivation builds such that they could inject malicious code into them without changing their (either input- or output- addressed) hash.
|
||||||
important that users are not able to run builders that modify the Nix
|
If they could do so, they could install a Trojan horse in some package and compromise the accounts of other users.
|
||||||
store or database in arbitrary ways, or that interfere with builds
|
|
||||||
started by other users. If they could do so, they could install a Trojan
|
|
||||||
horse in some package and compromise the accounts of other users.
|
|
||||||
|
|
||||||
To prevent this, the Nix store and database are owned by some privileged
|
To prevent this, the Nix store and database are owned by some privileged user (usually `root`) and builders are executed under unprivileged system user accounts (usually named `nixbld1`, `nixbld2`, etc.).
|
||||||
user (usually `root`) and builders are executed under special user
|
When an unprivileged user runs a Nix command, actions that operate on the Nix store (such as builds) are forwarded to a *Nix daemon* running under the owner of the Nix store/database that performs the operation.
|
||||||
accounts (usually named `nixbld1`, `nixbld2`, etc.). When a unprivileged
|
|
||||||
user runs a Nix command, actions that operate on the Nix store (such as
|
|
||||||
builds) are forwarded to a *Nix daemon* running under the owner of the
|
|
||||||
Nix store/database that performs the operation.
|
|
||||||
|
|
||||||
> **Note**
|
The buried lede in the above sentence is that *currently*, even in multi-user mode using a daemon, if executing as the user that owns the store, Lix directly manipulates the store unless `--store daemon` is specified.
|
||||||
>
|
[We intend to change this in the future][multi-user-should-not-be-root].
|
||||||
> Multi-user mode has one important limitation: only root and a set of
|
|
||||||
> trusted users specified in `nix.conf` can specify arbitrary binary
|
|
||||||
> caches. So while unprivileged users may install packages from
|
|
||||||
> arbitrary Nix expressions, they may not get pre-built binaries.
|
|
||||||
|
|
||||||
## Setting up the build users
|
<div class="warning">
|
||||||
|
The Lix team considers the goal of the sandbox to be primarily for preventing reproducibility mistakes, and does not consider multi-user mode to be a strong security boundary between users.
|
||||||
|
|
||||||
|
Do not evaluate or build untrusted, potentially-malicious, Nix language code on machines that you care deeply about maintaining user isolation on.
|
||||||
|
|
||||||
|
Although we would consider any sandbox escapes to be serious security bugs and we intend to fix them, we are not confident enough in the daemon's security to call the daemon a security boundary.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
[multi-user-should-not-be-root]: https://git.lix.systems/lix-project/lix/issues/18
|
||||||
|
|
||||||
|
## Trust model
|
||||||
|
|
||||||
|
There are two categories of users of the Lix daemon: trusted users and untrusted users.
|
||||||
|
The Lix daemon only allows connections from users that are either trusted users, or are specified in, or are members of groups specified in, [`allowed-users`](../command-ref/conf-file.md#conf-allowed-users) in `nix.conf`.
|
||||||
|
Trusted users are users and users of groups specified in [`trusted-users`](../command-ref/conf-file.md#conf-trusted-users) in `nix.conf`.
|
||||||
|
|
||||||
|
All users of the Lix daemon may do the following to bring things into the Nix store:
|
||||||
|
|
||||||
|
- Users may load derivations and output-addressed files into the store with `nix-store --add` or through Nix language code.
|
||||||
|
- Users may locally build derivations, either of the output-addressed or input-addressed variety, creating output paths.
|
||||||
|
|
||||||
|
Note that [fixed-output derivations only consider name and hash](https://github.com/NixOS/nix/issues/969), so it is possible to write a fixed-output derivation for something important with a bogus hash and have it resolve to something else already built in the store.
|
||||||
|
|
||||||
|
On systems with `sandbox` enabled (default on Linux; [not *yet* on macOS][sandbox-enable-macos]), derivations are either:
|
||||||
|
- Input-addressed, so they are run in the sandbox with no network access, with the following exceptions:
|
||||||
|
|
||||||
|
- The (poorly named, since it is not *just* about chroot) property `__noChroot` is set on the derivation and `sandbox` is set to `relaxed`.
|
||||||
|
- On macOS, the derivation property `__darwinAllowLocalNetworking` allows network access to localhost from input-addressed derivations regardless of the `sandbox` setting value. This property exists with such semantics because macOS has no network namespace equivalent to isolate individual processes' localhost networking.
|
||||||
|
- Output-addressed, so they are run with network access but their result must match an expected hash.
|
||||||
|
|
||||||
|
Trusted users may set any setting, including `sandbox = false`, so the sandbox state can be different at runtime from what is described in `nix.conf` for builds invoked with such settings.
|
||||||
|
- Users may copy appropriately-signed derivation outputs into the store.
|
||||||
|
|
||||||
|
By default, any paths *copied into a store* (such as by substitution) must have signatures from [`trusted-public-keys`](../command-ref/conf-file.md#conf-trusted-public-keys) unless they are [output-addressed](../glossary.md#gloss-output-addressed-store-object).
|
||||||
|
|
||||||
|
Unsigned paths may be copied into a store if [`require-sigs`](../command-ref/conf-file.md#conf-require-sigs) is disabled in the daemon's configuration (not default), or if the client is a trusted user and passed `--no-check-sigs` to `nix copy`.
|
||||||
|
- Users may request that the daemon substitutes appropriately-signed derivation outputs from a binary cache in the daemon's [`substituters`](../command-ref/conf-file.md#conf-substituters) list.
|
||||||
|
|
||||||
|
Untrusted clients may also specify additional values for `substituters` (via e.g. `--extra-substituters` on a Nix command) that are listed in [`trusted-substituters`](../command-ref/conf-file.md#conf-trusted-substituters).
|
||||||
|
|
||||||
|
A client could in principle substitute such paths itself then copy them to the daemon (see clause above) if they are appropriately signed but are *not* from a trusted substituter, however this is not implemented in the current Lix client to our knowledge, at the time of writing.
|
||||||
|
This probably means that `trusted-substituters` is a redundant setting except insofar as such substitution would have to be done on the client rather than as root on the daemon; and it is highly defensible to not allow random usage of our HTTP client running as root.
|
||||||
|
|
||||||
|
[sandbox-enable-macos]: https://git.lix.systems/lix-project/lix/issues/386
|
||||||
|
|
||||||
|
### The Lix daemon as a security non-boundary
|
||||||
|
|
||||||
|
The Lix team and wider community does not consider the Lix daemon to be a *security boundary* against malicious Nix language code.
|
||||||
|
|
||||||
|
Although we do our best to make it secure, we do not recommend sharing a Lix daemon with potentially malicious users.
|
||||||
|
That means that public continuous integration (CI) builds of untrusted Nix code should not share builders with CI that writes into a cache used by trusted infrastructure.
|
||||||
|
|
||||||
|
For example, [hydra.nixos.org], which is the builder for [cache.nixos.org], does not execute untrusted Nix language code; a separate system, [ofborg] is used for CI of nixpkgs pull requests.
|
||||||
|
The build output of pull request CI is never pushed to [cache.nixos.org], and those systems are considered entirely untrusted.
|
||||||
|
|
||||||
|
This is because, among other things, the Lix sandbox is *more* susceptible to kernel exploits than Docker, which, unlike Lix, blocks nested user namespaces via `seccomp` in its default policy, and there have been many kernel bugs only exposed to unprivileged users via user namespaces allowing otherwise-root-only system calls.
|
||||||
|
In general, the Lix sandbox is set up to be relatively unrestricted while maintaining its goals of building useful, reproducible software; security is not its primary goal.
|
||||||
|
|
||||||
|
The Lix sandbox is a custom *non-rootless* Linux container implementation that has not been audited to nearly the same degree as Docker and similar systems.
|
||||||
|
Also, the Lix daemon is a complex and historied C++ executable running as root with very little privilege separation.
|
||||||
|
All of this means that a security hole in the Lix daemon gives immediate root access.
|
||||||
|
Systems like Docker (especially non-rootless Docker) should *themselves* probably not be used in a multi-tenant manner with mutually distrusting tenants, but the Lix daemon *especially* should not be used as such as of this writing.
|
||||||
|
|
||||||
|
The primary purpose of the sandbox is to strongly encourage packages to be reproducible, a goal which it is generally quite successful at.
|
||||||
|
|
||||||
|
[hydra.nixos.org]: https://hydra.nixos.org
|
||||||
|
[ofborg]: https://github.com/NixOS/ofborg
|
||||||
|
[cache.nixos.org]: https://cache.nixos.org
|
||||||
|
|
||||||
|
### Trusted users
|
||||||
|
|
||||||
|
Trusted users are permitted to set any setting and bypass security restrictions on the daemon.
|
||||||
|
They are currently in widespread use for a couple of reasons such as remote builds (which we [intend to fix](https://git.lix.systems/lix-project/lix/issues/171)).
|
||||||
|
|
||||||
|
Trusted users are effectively root on Nix daemons running as root (the default configuration) for *at least* the following reasons, and should be thus thought of as equivalent to passwordless sudo.
|
||||||
|
This is not a comprehensive list.
|
||||||
|
|
||||||
|
- They may copy an unsigned malicious built output into the store for `systemd` or anything else that will run as root, then when the system is upgraded, that path will be used from the local store rather than substituted.
|
||||||
|
- They may set the following settings that are commands the daemon will run as root:
|
||||||
|
- `build-hook`
|
||||||
|
- `diff-hook`
|
||||||
|
- `pre-build-hook`
|
||||||
|
- `post-build-hook`
|
||||||
|
- They may set `build-users-group`.
|
||||||
|
|
||||||
|
In particular, they may set it to empty string, which runs builds as root with respect to the rest of the system (!!).
|
||||||
|
We, too, [think that is absurd and intend to not accept such a configuration](https://git.lix.systems/lix-project/lix/issues/242).
|
||||||
|
It is then simply an exercise to the reader to find a daemon that does `SCM_CREDENTIALS` over a `unix(7)` socket and lets you run commands as root, and mount it into the sandbox with `extra-sandbox-paths`.
|
||||||
|
|
||||||
|
At the very least, the Lix daemon itself (since `root` is a trusted user by default) and probably `systemd` qualify for this.
|
||||||
|
- They may set the `builders` list, which will have ssh run as root.
|
||||||
|
We aren't sure if there is a way to abuse this for command execution but it's plausible.
|
||||||
|
|
||||||
|
Note that setting `accept-flake-config` allows arbitrary Nix flakes to set Nix settings in the `nixConfig` stanza.
|
||||||
|
Do not set this setting or pass `--accept-flake-config` while executing untrusted Nix language code as a trusted user for the reasons above!
|
||||||
|
|
||||||
|
## Build users
|
||||||
|
|
||||||
The *build users* are the special UIDs under which builds are performed.
|
The *build users* are the special UIDs under which builds are performed.
|
||||||
They should all be members of the *build users group* `nixbld`. This
|
A build user is selected for a build by looking in the group specified by [`build-users-group`](../command-ref/conf-file.md#conf-build-users-group), by default, `nixbld`, then a member of that group not currently executing a build is selected for the build.
|
||||||
group should have no other members. The build users should not be
|
The build users should not be members of any other group.
|
||||||
members of any other group. On Linux, you can create the group and users
|
|
||||||
as follows:
|
There can never be more concurrent builds than the number of build users, unless using [`auto-allocate-uids`](../command-ref/conf-file.md#conf-auto-allocate-uids) ([tracking issue][auto-allocate-uids-issue]).
|
||||||
|
|
||||||
|
[auto-allocate-uids-issue]: https://git.lix.systems/lix-project/lix/issues/387
|
||||||
|
|
||||||
|
If, for some reason, you need to create such users manually, the following command will create 10 build users on Linux:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ groupadd -r nixbld
|
$ groupadd -r nixbld
|
||||||
|
@ -35,43 +124,12 @@ $ for n in $(seq 1 10); do useradd -c "Nix build user $n" \
|
||||||
nixbld$n; done
|
nixbld$n; done
|
||||||
```
|
```
|
||||||
|
|
||||||
This creates 10 build users. There can never be more concurrent builds
|
|
||||||
than the number of build users, so you may want to increase this if you
|
|
||||||
expect to do many builds at the same time.
|
|
||||||
|
|
||||||
## Running the daemon
|
## Running the daemon
|
||||||
|
|
||||||
The [Nix daemon](../command-ref/nix-daemon.md) should be started as
|
The [Nix daemon](../command-ref/nix-daemon.md) can be started manually as follows (as `root`):
|
||||||
follows (as `root`):
|
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix-daemon
|
# nix-daemon
|
||||||
```
|
```
|
||||||
|
|
||||||
You’ll want to put that line somewhere in your system’s boot scripts.
|
In standard installations of Lix, the daemon is started by a `systemd` unit (Linux) or `launchd` service (macOS).
|
||||||
|
|
||||||
To let unprivileged users use the daemon, they should set the
|
|
||||||
[`NIX_REMOTE` environment variable](../command-ref/env-common.md) to
|
|
||||||
`daemon`. So you should put a line like
|
|
||||||
|
|
||||||
```console
|
|
||||||
export NIX_REMOTE=daemon
|
|
||||||
```
|
|
||||||
|
|
||||||
into the users’ login scripts.
|
|
||||||
|
|
||||||
## Restricting access
|
|
||||||
|
|
||||||
To limit which users can perform Nix operations, you can use the
|
|
||||||
permissions on the directory `/nix/var/nix/daemon-socket`. For instance,
|
|
||||||
if you want to restrict the use of Nix to the members of a group called
|
|
||||||
`nix-users`, do
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ chgrp nix-users /nix/var/nix/daemon-socket
|
|
||||||
$ chmod ug=rwx,o= /nix/var/nix/daemon-socket
|
|
||||||
```
|
|
||||||
|
|
||||||
This way, users who are not in the `nix-users` group cannot connect to
|
|
||||||
the Unix domain socket `/nix/var/nix/daemon-socket/socket`, so they
|
|
||||||
cannot perform Nix operations.
|
|
||||||
|
|
|
@ -7,9 +7,8 @@ management operations. All other users can then use the installed
|
||||||
packages, but they cannot perform package management operations
|
packages, but they cannot perform package management operations
|
||||||
themselves.
|
themselves.
|
||||||
|
|
||||||
Alternatively, you can configure Lix in “multi-user mode”. In this
|
Alternatively, you can configure Lix in “multi-user mode”. In this model, all users can perform package management operations — for instance, every user can install software for themselves without requiring root privileges.
|
||||||
model, all users can perform package management operations — for
|
Lix does its best to ensure that this is secure.
|
||||||
instance, every user can install software without requiring root
|
For instance, it would be considered a serious security bug for one untrusted user to be able to overwrite a package used by another user with a Trojan horse.
|
||||||
privileges. Lix ensures that this is secure. For instance, it’s not
|
|
||||||
possible for one user to overwrite a package used by another user with a
|
Nevertheless, the Lix team does not consider multi-user mode a strong security boundary, and does not recommend running untrusted user-supplied Nix language code on privileged machines, even if it is secure to the best of our knowledge at any moment in time.
|
||||||
Trojan horse.
|
|
||||||
|
|
|
@ -229,7 +229,7 @@ This release has the following new features:
|
||||||
<https://cache.nixos.org/> or <ssh://machine>. The following store
|
<https://cache.nixos.org/> or <ssh://machine>. The following store
|
||||||
types are supported:
|
types are supported:
|
||||||
|
|
||||||
- `LocalStore` (stori URI `local` or an absolute path) and the
|
- `LocalStore` (store URI `local` or an absolute path) and the
|
||||||
misnamed `RemoteStore` (`daemon`) provide access to a local Nix
|
misnamed `RemoteStore` (`daemon`) provide access to a local Nix
|
||||||
store, the latter via the Nix daemon. You can use `auto` or the
|
store, the latter via the Nix daemon. You can use `auto` or the
|
||||||
empty string to auto-select a local or daemon store depending on
|
empty string to auto-select a local or daemon store depending on
|
||||||
|
|
|
@ -34,16 +34,16 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1715123187,
|
"lastModified": 1718111384,
|
||||||
"narHash": "sha256-0czuu757t53lK6uWeo1a5/jJbCd9t4sOtLDFpts60DM=",
|
"narHash": "sha256-7tSst0S5FOmcgvNtfy6cjZX5w8CabCVAfAeCkhY4OVg=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "0c592f9a288bdf764b6f24c757277c0e49757a46",
|
"rev": "a508a44af0c1b1b57785c34d8b54783536273eeb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-23.11-small",
|
"ref": "nixos-24.05-small",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
|
48
flake.nix
48
flake.nix
|
@ -2,7 +2,7 @@
|
||||||
description = "The purely functional package manager";
|
description = "The purely functional package manager";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11-small";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05-small";
|
||||||
nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
|
nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
|
||||||
pre-commit-hooks = {
|
pre-commit-hooks = {
|
||||||
url = "github:cachix/git-hooks.nix";
|
url = "github:cachix/git-hooks.nix";
|
||||||
|
@ -87,10 +87,11 @@
|
||||||
crossSystems = [
|
crossSystems = [
|
||||||
"armv6l-linux"
|
"armv6l-linux"
|
||||||
"armv7l-linux"
|
"armv7l-linux"
|
||||||
# FIXME: doesn't evaluate, plausibly fixed in >=24.05, so recheck when
|
"riscv64-linux"
|
||||||
# we update to 24.05
|
# FIXME: still broken in 24.05: fails to build rustc(??) due to missing -lstdc++ dep
|
||||||
# "x86_64-freebsd13"
|
# "x86_64-freebsd"
|
||||||
"x86_64-netbsd"
|
# FIXME: broken dev shell due to python
|
||||||
|
# "x86_64-netbsd"
|
||||||
];
|
];
|
||||||
|
|
||||||
stdenvs = [
|
stdenvs = [
|
||||||
|
@ -135,13 +136,11 @@
|
||||||
{
|
{
|
||||||
system = crossSystem;
|
system = crossSystem;
|
||||||
}
|
}
|
||||||
// lib.optionalAttrs (crossSystem == "x86_64-freebsd13") { useLLVM = true; };
|
// lib.optionalAttrs (crossSystem == "x86_64-freebsd") { useLLVM = true; };
|
||||||
overlays = [
|
overlays = [
|
||||||
(overlayFor (p: p.${stdenv}))
|
(overlayFor (p: p.${stdenv}))
|
||||||
(final: prev: { nixfmt = final.callPackage ./nix-support/nixfmt.nix { }; })
|
(final: prev: { nixfmt = final.callPackage ./nix-support/nixfmt.nix { }; })
|
||||||
];
|
];
|
||||||
|
|
||||||
config.permittedInsecurePackages = [ "nix-2.13.6" ];
|
|
||||||
};
|
};
|
||||||
stdenvs = forAllStdenvs (make-pkgs null);
|
stdenvs = forAllStdenvs (make-pkgs null);
|
||||||
native = stdenvs.stdenvPackages;
|
native = stdenvs.stdenvPackages;
|
||||||
|
@ -153,8 +152,6 @@
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
binaryTarball = nix: pkgs: pkgs.callPackage ./nix-support/binary-tarball.nix { inherit nix; };
|
|
||||||
|
|
||||||
overlayFor =
|
overlayFor =
|
||||||
getStdenv: final: prev:
|
getStdenv: final: prev:
|
||||||
let
|
let
|
||||||
|
@ -167,7 +164,7 @@
|
||||||
nixUnstable = prev.nixUnstable;
|
nixUnstable = prev.nixUnstable;
|
||||||
|
|
||||||
check-headers = final.buildPackages.callPackage ./maintainers/check-headers.nix { };
|
check-headers = final.buildPackages.callPackage ./maintainers/check-headers.nix { };
|
||||||
clangbuildanalyzer = final.buildPackages.callPackage ./misc/clangbuildanalyzer.nix { };
|
check-syscalls = final.buildPackages.callPackage ./maintainers/check-syscalls.nix { };
|
||||||
|
|
||||||
default-busybox-sandbox-shell = final.busybox.override {
|
default-busybox-sandbox-shell = final.busybox.override {
|
||||||
useMusl = true;
|
useMusl = true;
|
||||||
|
@ -215,6 +212,11 @@
|
||||||
# Binary package for various platforms.
|
# Binary package for various platforms.
|
||||||
build = forAllSystems (system: self.packages.${system}.nix);
|
build = forAllSystems (system: self.packages.${system}.nix);
|
||||||
|
|
||||||
|
devShell = forAllSystems (system: {
|
||||||
|
default = self.devShells.${system}.default;
|
||||||
|
clang = self.devShells.${system}.native-clangStdenvPackages;
|
||||||
|
});
|
||||||
|
|
||||||
rl-next = forAllSystems (
|
rl-next = forAllSystems (
|
||||||
system:
|
system:
|
||||||
let
|
let
|
||||||
|
@ -233,13 +235,11 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
# Perl bindings for various platforms.
|
# Perl bindings for various platforms.
|
||||||
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings);
|
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.passthru.perl-bindings);
|
||||||
|
|
||||||
# Binary tarball for various platforms, containing a Nix store
|
# Binary tarball for various platforms, containing a Nix store
|
||||||
# with the closure of 'nix' package.
|
# with the closure of 'nix' package.
|
||||||
binaryTarball = forAllSystems (
|
binaryTarball = forAllSystems (system: nixpkgsFor.${system}.native.nix.passthru.binaryTarball);
|
||||||
system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native
|
|
||||||
);
|
|
||||||
|
|
||||||
# docker image with Lix inside
|
# docker image with Lix inside
|
||||||
dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
|
dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
|
||||||
|
@ -282,9 +282,20 @@
|
||||||
|
|
||||||
nixpkgsLibTests = forAllSystems (
|
nixpkgsLibTests = forAllSystems (
|
||||||
system:
|
system:
|
||||||
import (nixpkgs + "/lib/tests/release.nix") {
|
let
|
||||||
|
inherit (self.packages.${system}) nix;
|
||||||
pkgs = nixpkgsFor.${system}.native;
|
pkgs = nixpkgsFor.${system}.native;
|
||||||
nixVersions = [ self.packages.${system}.nix ];
|
testWithNix = import (nixpkgs + "/lib/tests/test-with-nix.nix") { inherit pkgs lib nix; };
|
||||||
|
in
|
||||||
|
pkgs.symlinkJoin {
|
||||||
|
name = "nixpkgs-lib-tests";
|
||||||
|
paths =
|
||||||
|
[ testWithNix ]
|
||||||
|
# FIXME: This is disabled on darwin due to a nixpkgs bug https://github.com/NixOS/nixpkgs/issues/319147
|
||||||
|
# After that is fixed, it should be restored to use lib/tests/release.nix as before, rather than this reimplementation.
|
||||||
|
++ lib.optionals pkgs.stdenv.isLinux [
|
||||||
|
(import (nixpkgs + "/pkgs/test/release") { inherit pkgs lib nix; })
|
||||||
|
];
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -311,6 +322,9 @@
|
||||||
checks = forAvailableSystems (
|
checks = forAvailableSystems (
|
||||||
system:
|
system:
|
||||||
{
|
{
|
||||||
|
# devShells and packages already get checked by nix flake check, so
|
||||||
|
# this is just jobs that are special
|
||||||
|
|
||||||
binaryTarball = self.hydraJobs.binaryTarball.${system};
|
binaryTarball = self.hydraJobs.binaryTarball.${system};
|
||||||
perlBindings = self.hydraJobs.perlBindings.${system};
|
perlBindings = self.hydraJobs.perlBindings.${system};
|
||||||
nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system};
|
nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system};
|
||||||
|
|
16
maintainers/check-syscalls.nix
Normal file
16
maintainers/check-syscalls.nix
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
runCommandNoCC,
|
||||||
|
lib,
|
||||||
|
libseccomp,
|
||||||
|
writeShellScriptBin,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
syscalls-csv = runCommandNoCC "syscalls.csv" { } ''
|
||||||
|
echo ${lib.escapeShellArg libseccomp.src}
|
||||||
|
tar -xf ${lib.escapeShellArg libseccomp.src} --strip-components=2 ${libseccomp.name}/src/syscalls.csv
|
||||||
|
mv syscalls.csv "$out"
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
writeShellScriptBin "check-syscalls" ''
|
||||||
|
${./check-syscalls.sh} ${syscalls-csv}
|
||||||
|
''
|
7
maintainers/check-syscalls.sh
Executable file
7
maintainers/check-syscalls.sh
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
diff -u <(awk < src/libstore/build/local-derivation-goal.cc '/BEGIN extract-syscalls/ { extracting = 1; next }
|
||||||
|
match($0, /allowSyscall\(ctx, SCMP_SYS\(([^)]*)\)\);|\/\/ skip ([^ ]*)/, result) { print result[1] result[2] }
|
||||||
|
/END extract-syscalls/ { extracting = 0; next }') <(tail -n+2 "$1" | cut -d, -f 1)
|
23
meson.build
23
meson.build
|
@ -182,23 +182,18 @@ elif is_linux
|
||||||
# Clang sanitizers on Linux.
|
# Clang sanitizers on Linux.
|
||||||
# FIXME(Qyriad): is that true?
|
# FIXME(Qyriad): is that true?
|
||||||
endif
|
endif
|
||||||
deps = [ ]
|
|
||||||
configdata = { }
|
configdata = { }
|
||||||
|
|
||||||
#
|
#
|
||||||
# Dependencies
|
# Dependencies
|
||||||
#
|
#
|
||||||
|
|
||||||
boehm = dependency('bdw-gc', required : get_option('gc'))
|
boehm = dependency('bdw-gc', required : get_option('gc'), version : '>=8.2.6')
|
||||||
if boehm.found()
|
|
||||||
deps += boehm
|
|
||||||
endif
|
|
||||||
configdata += {
|
configdata += {
|
||||||
'HAVE_BOEHMGC': boehm.found().to_int(),
|
'HAVE_BOEHMGC': boehm.found().to_int(),
|
||||||
}
|
}
|
||||||
|
|
||||||
boost = dependency('boost', required : true, modules : ['context', 'coroutine', 'container'])
|
boost = dependency('boost', required : true, modules : ['context', 'coroutine', 'container'])
|
||||||
deps += boost
|
|
||||||
|
|
||||||
# cpuid only makes sense on x86_64
|
# cpuid only makes sense on x86_64
|
||||||
cpuid_required = is_x64 ? get_option('cpuid') : false
|
cpuid_required = is_x64 ? get_option('cpuid') : false
|
||||||
|
@ -206,7 +201,6 @@ cpuid = dependency('libcpuid', 'cpuid', required : cpuid_required)
|
||||||
configdata += {
|
configdata += {
|
||||||
'HAVE_LIBCPUID': cpuid.found().to_int(),
|
'HAVE_LIBCPUID': cpuid.found().to_int(),
|
||||||
}
|
}
|
||||||
deps += cpuid
|
|
||||||
|
|
||||||
# seccomp only makes sense on Linux
|
# seccomp only makes sense on Linux
|
||||||
seccomp_required = is_linux ? get_option('seccomp-sandboxing') : false
|
seccomp_required = is_linux ? get_option('seccomp-sandboxing') : false
|
||||||
|
@ -219,17 +213,14 @@ configdata += {
|
||||||
}
|
}
|
||||||
|
|
||||||
libarchive = dependency('libarchive', required : true)
|
libarchive = dependency('libarchive', required : true)
|
||||||
deps += libarchive
|
|
||||||
|
|
||||||
brotli = [
|
brotli = [
|
||||||
dependency('libbrotlicommon', required : true),
|
dependency('libbrotlicommon', required : true),
|
||||||
dependency('libbrotlidec', required : true),
|
dependency('libbrotlidec', required : true),
|
||||||
dependency('libbrotlienc', required : true),
|
dependency('libbrotlienc', required : true),
|
||||||
]
|
]
|
||||||
deps += brotli
|
|
||||||
|
|
||||||
openssl = dependency('libcrypto', 'openssl', required : true)
|
openssl = dependency('libcrypto', 'openssl', required : true)
|
||||||
deps += openssl
|
|
||||||
|
|
||||||
aws_sdk = dependency('aws-cpp-sdk-core', required : false)
|
aws_sdk = dependency('aws-cpp-sdk-core', required : false)
|
||||||
aws_sdk_transfer = dependency('aws-cpp-sdk-transfer', required : aws_sdk.found(), fallback : ['aws_sdk', 'aws_cpp_sdk_transfer_dep'])
|
aws_sdk_transfer = dependency('aws-cpp-sdk-transfer', required : aws_sdk.found(), fallback : ['aws_sdk', 'aws_cpp_sdk_transfer_dep'])
|
||||||
|
@ -243,7 +234,6 @@ if aws_sdk.found()
|
||||||
links : true,
|
links : true,
|
||||||
sources : true,
|
sources : true,
|
||||||
)
|
)
|
||||||
deps += aws_sdk
|
|
||||||
s = aws_sdk.version().split('.')
|
s = aws_sdk.version().split('.')
|
||||||
configdata += {
|
configdata += {
|
||||||
'AWS_VERSION_MAJOR': s[0].to_int(),
|
'AWS_VERSION_MAJOR': s[0].to_int(),
|
||||||
|
@ -270,7 +260,6 @@ if aws_s3.found()
|
||||||
links : true,
|
links : true,
|
||||||
sources : true,
|
sources : true,
|
||||||
)
|
)
|
||||||
deps += aws_s3
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
configdata += {
|
configdata += {
|
||||||
|
@ -278,26 +267,20 @@ configdata += {
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19', required : true)
|
sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19', required : true)
|
||||||
deps += sqlite
|
|
||||||
|
|
||||||
sodium = dependency('libsodium', 'sodium', required : true)
|
sodium = dependency('libsodium', 'sodium', required : true)
|
||||||
deps += sodium
|
|
||||||
|
|
||||||
curl = dependency('libcurl', 'curl', required : true)
|
curl = dependency('libcurl', 'curl', required : true)
|
||||||
deps += curl
|
|
||||||
|
|
||||||
editline = dependency('libeditline', 'editline', version : '>=1.14', required : true)
|
editline = dependency('libeditline', 'editline', version : '>=1.14', required : true)
|
||||||
deps += editline
|
|
||||||
|
|
||||||
lowdown = dependency('lowdown', version : '>=0.9.0', required : true)
|
lowdown = dependency('lowdown', version : '>=0.9.0', required : true)
|
||||||
deps += lowdown
|
|
||||||
|
|
||||||
# HACK(Qyriad): rapidcheck's pkg-config doesn't include the libs lol
|
# HACK(Qyriad): rapidcheck's pkg-config doesn't include the libs lol
|
||||||
# Note: technically we 'check' for rapidcheck twice, for the internal-api-docs handling above,
|
# Note: technically we 'check' for rapidcheck twice, for the internal-api-docs handling above,
|
||||||
# but Meson will cache the result of the first one, and the required : arguments are different.
|
# but Meson will cache the result of the first one, and the required : arguments are different.
|
||||||
rapidcheck_meson = dependency('rapidcheck', required : enable_tests)
|
rapidcheck_meson = dependency('rapidcheck', required : enable_tests)
|
||||||
rapidcheck = declare_dependency(dependencies : rapidcheck_meson, link_args : ['-lrapidcheck'])
|
rapidcheck = declare_dependency(dependencies : rapidcheck_meson, link_args : ['-lrapidcheck'])
|
||||||
deps += rapidcheck
|
|
||||||
|
|
||||||
gtest = [
|
gtest = [
|
||||||
dependency('gtest', required : enable_tests),
|
dependency('gtest', required : enable_tests),
|
||||||
|
@ -305,13 +288,10 @@ gtest = [
|
||||||
dependency('gmock', required : enable_tests),
|
dependency('gmock', required : enable_tests),
|
||||||
dependency('gmock_main', required : enable_tests),
|
dependency('gmock_main', required : enable_tests),
|
||||||
]
|
]
|
||||||
deps += gtest
|
|
||||||
|
|
||||||
toml11 = dependency('toml11', version : '>=3.7.0', required : true, method : 'cmake')
|
toml11 = dependency('toml11', version : '>=3.7.0', required : true, method : 'cmake')
|
||||||
deps += toml11
|
|
||||||
|
|
||||||
nlohmann_json = dependency('nlohmann_json', required : true)
|
nlohmann_json = dependency('nlohmann_json', required : true)
|
||||||
deps += nlohmann_json
|
|
||||||
|
|
||||||
# lix-doc is a Rust project provided via buildInputs and unfortunately doesn't have any way to be detected.
|
# lix-doc is a Rust project provided via buildInputs and unfortunately doesn't have any way to be detected.
|
||||||
# Just declare it manually to resolve this.
|
# Just declare it manually to resolve this.
|
||||||
|
@ -319,7 +299,6 @@ deps += nlohmann_json
|
||||||
# FIXME: build this with meson in the future after we drop Make (with which we
|
# FIXME: build this with meson in the future after we drop Make (with which we
|
||||||
# *absolutely* are not going to make it work)
|
# *absolutely* are not going to make it work)
|
||||||
lix_doc = declare_dependency(link_args : [ '-llix_doc' ])
|
lix_doc = declare_dependency(link_args : [ '-llix_doc' ])
|
||||||
deps += lix_doc
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Build-time tools
|
# Build-time tools
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
# Upstreaming here, can be deleted once it's upstreamed:
|
|
||||||
# https://github.com/NixOS/nixpkgs/pull/297102
|
|
||||||
{
|
|
||||||
stdenv,
|
|
||||||
lib,
|
|
||||||
cmake,
|
|
||||||
fetchFromGitHub,
|
|
||||||
}:
|
|
||||||
stdenv.mkDerivation (finalAttrs: {
|
|
||||||
pname = "clangbuildanalyzer";
|
|
||||||
version = "1.5.0";
|
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
|
||||||
owner = "aras-p";
|
|
||||||
repo = "ClangBuildAnalyzer";
|
|
||||||
rev = "v${finalAttrs.version}";
|
|
||||||
sha256 = "sha256-kmgdk634zM0W0OoRoP/RzepArSipa5bNqdVgdZO9gxo=";
|
|
||||||
};
|
|
||||||
|
|
||||||
nativeBuildInputs = [ cmake ];
|
|
||||||
|
|
||||||
meta = {
|
|
||||||
description = "Tool for analyzing Clang's -ftrace-time files";
|
|
||||||
homepage = "https://github.com/aras-p/ClangBuildAnalyzer";
|
|
||||||
maintainers = with lib.maintainers; [ lf- ];
|
|
||||||
license = lib.licenses.unlicense;
|
|
||||||
platforms = lib.platforms.unix;
|
|
||||||
# `long long int` != `size_t`
|
|
||||||
# There's no convenient lib.platforms.32bit or anything, but it's easy enough to do ourselves.
|
|
||||||
badPlatforms = lib.filter (plat: (lib.systems.elaborate plat).is32bit) lib.platforms.all;
|
|
||||||
mainProgram = "ClangBuildAnalyzer";
|
|
||||||
};
|
|
||||||
})
|
|
44
package.nix
44
package.nix
|
@ -39,12 +39,10 @@
|
||||||
pkg-config,
|
pkg-config,
|
||||||
python3,
|
python3,
|
||||||
rapidcheck,
|
rapidcheck,
|
||||||
skopeo,
|
|
||||||
sqlite,
|
sqlite,
|
||||||
toml11,
|
toml11,
|
||||||
util-linuxMinimal ? utillinuxMinimal,
|
util-linuxMinimal ? utillinuxMinimal,
|
||||||
utillinuxMinimal ? null,
|
utillinuxMinimal ? null,
|
||||||
xonsh-unwrapped,
|
|
||||||
xz,
|
xz,
|
||||||
|
|
||||||
busybox-sandbox-shell,
|
busybox-sandbox-shell,
|
||||||
|
@ -71,8 +69,6 @@
|
||||||
# `boehmgc-nix` then this will almost certainly have duplicate patches, which means
|
# `boehmgc-nix` then this will almost certainly have duplicate patches, which means
|
||||||
# the patches won't apply and we'll get a build failure.
|
# the patches won't apply and we'll get a build failure.
|
||||||
./boehmgc-coroutine-sp-fallback.diff
|
./boehmgc-coroutine-sp-fallback.diff
|
||||||
# https://github.com/ivmai/bdwgc/pull/586
|
|
||||||
./boehmgc-traceable_allocator-public.diff
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -380,8 +376,6 @@ stdenv.mkDerivation (finalAttrs: {
|
||||||
platforms = lib.platforms.unix;
|
platforms = lib.platforms.unix;
|
||||||
};
|
};
|
||||||
|
|
||||||
passthru.perl-bindings = pkgs.callPackage ./perl { inherit fileset stdenv; };
|
|
||||||
|
|
||||||
# Export the patched version of boehmgc.
|
# Export the patched version of boehmgc.
|
||||||
# flake.nix exports that into its overlay.
|
# flake.nix exports that into its overlay.
|
||||||
passthru = {
|
passthru = {
|
||||||
|
@ -394,24 +388,27 @@ stdenv.mkDerivation (finalAttrs: {
|
||||||
mkDevShell =
|
mkDevShell =
|
||||||
{
|
{
|
||||||
mkShell,
|
mkShell,
|
||||||
just,
|
|
||||||
nixfmt,
|
bashInteractive,
|
||||||
glibcLocales,
|
|
||||||
pre-commit-checks,
|
|
||||||
clang-tools,
|
clang-tools,
|
||||||
llvmPackages,
|
|
||||||
clangbuildanalyzer,
|
clangbuildanalyzer,
|
||||||
|
glibcLocales,
|
||||||
|
just,
|
||||||
|
llvmPackages,
|
||||||
|
nixfmt,
|
||||||
|
skopeo,
|
||||||
|
xonsh,
|
||||||
|
|
||||||
|
# Lix specific packages
|
||||||
|
pre-commit-checks,
|
||||||
contribNotice,
|
contribNotice,
|
||||||
|
check-syscalls,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
glibcFix = lib.optionalAttrs (buildPlatform.isLinux && glibcLocales != null) {
|
glibcFix = lib.optionalAttrs (buildPlatform.isLinux && glibcLocales != null) {
|
||||||
# Required to make non-NixOS Linux not complain about missing locale files during configure in a dev shell
|
# Required to make non-NixOS Linux not complain about missing locale files during configure in a dev shell
|
||||||
LOCALE_ARCHIVE = "${lib.getLib pkgs.glibcLocales}/lib/locale/locale-archive";
|
LOCALE_ARCHIVE = "${lib.getLib pkgs.glibcLocales}/lib/locale/locale-archive";
|
||||||
};
|
};
|
||||||
# for some reason that seems accidental and was changed in
|
|
||||||
# NixOS 24.05-pre, clang-tools is pinned to LLVM 14 when
|
|
||||||
# default LLVM is newer.
|
|
||||||
clang-tools_llvm = clang-tools.override { inherit llvmPackages; };
|
|
||||||
|
|
||||||
pythonPackages = (
|
pythonPackages = (
|
||||||
p: [
|
p: [
|
||||||
|
@ -419,11 +416,9 @@ stdenv.mkDerivation (finalAttrs: {
|
||||||
p.python-frontmatter
|
p.python-frontmatter
|
||||||
p.requests
|
p.requests
|
||||||
p.xdg-base-dirs
|
p.xdg-base-dirs
|
||||||
(p.toPythonModule xonsh-unwrapped)
|
(p.toPythonModule xonsh.passthru.unwrapped)
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
# FIXME: This will explode when we switch to 24.05 if we don't backport
|
|
||||||
# https://github.com/NixOS/nixpkgs/pull/317636 first
|
|
||||||
pythonEnv = python3.withPackages pythonPackages;
|
pythonEnv = python3.withPackages pythonPackages;
|
||||||
|
|
||||||
# pkgs.mkShell uses pkgs.stdenv by default, regardless of inputsFrom.
|
# pkgs.mkShell uses pkgs.stdenv by default, regardless of inputsFrom.
|
||||||
|
@ -452,11 +447,16 @@ stdenv.mkDerivation (finalAttrs: {
|
||||||
++ [ (lib.mesonBool "enable-pch-std" stdenv.cc.isClang) ];
|
++ [ (lib.mesonBool "enable-pch-std" stdenv.cc.isClang) ];
|
||||||
|
|
||||||
packages =
|
packages =
|
||||||
lib.optional (stdenv.cc.isClang && hostPlatform == buildPlatform) clang-tools_llvm
|
lib.optional (stdenv.cc.isClang && hostPlatform == buildPlatform) clang-tools
|
||||||
++ [
|
++ [
|
||||||
|
# Why are we providing a bashInteractive? Well, when you run
|
||||||
|
# `bash` from inside `nix develop`, say, because you are using it
|
||||||
|
# via direnv, you will by default get bash (unusable edition).
|
||||||
|
bashInteractive
|
||||||
pythonEnv
|
pythonEnv
|
||||||
# docker image tool
|
# docker image tool
|
||||||
skopeo
|
skopeo
|
||||||
|
check-syscalls
|
||||||
just
|
just
|
||||||
nixfmt
|
nixfmt
|
||||||
# Load-bearing order. Must come before clang-unwrapped below, but after clang_tools above.
|
# Load-bearing order. Must come before clang-unwrapped below, but after clang_tools above.
|
||||||
|
@ -509,5 +509,11 @@ stdenv.mkDerivation (finalAttrs: {
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
perl-bindings = pkgs.callPackage ./perl { inherit fileset stdenv; };
|
||||||
|
|
||||||
|
binaryTarball = pkgs.callPackage ./nix-support/binary-tarball.nix {
|
||||||
|
nix = finalAttrs.finalPackage;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
from xonsh.main import setup
|
from xonsh.main import setup
|
||||||
|
|
||||||
setup()
|
setup()
|
||||||
del setup
|
del setup
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import xonsh.base_shell
|
||||||
|
|
||||||
from . import environment
|
from . import environment
|
||||||
from . import create_release
|
from . import create_release
|
||||||
|
@ -13,19 +17,55 @@ from . import docker
|
||||||
from . import docker_assemble
|
from . import docker_assemble
|
||||||
from . import gitutils
|
from . import gitutils
|
||||||
|
|
||||||
rootLogger = logging.getLogger()
|
|
||||||
rootLogger.setLevel(logging.DEBUG)
|
def setup_logging():
|
||||||
log = logging.getLogger(__name__)
|
"""
|
||||||
log.setLevel(logging.DEBUG)
|
Sets up logging to work properly. The following are intended to work:
|
||||||
|
- ipython/xonsh configuration files adding log handlers out of band
|
||||||
|
- Reloading the module in xonsh/ipython not causing Bonus Loggers (which is
|
||||||
|
why we check if there is already a handler. This also helps the previous
|
||||||
|
case)
|
||||||
|
- Importing the releng module from xonsh and poking at it interactively
|
||||||
|
"""
|
||||||
|
|
||||||
|
LEVELS = {
|
||||||
|
# Root logger must be DEBUG so that anything else can be DEBUG
|
||||||
|
None: logging.DEBUG,
|
||||||
|
# Everything in releng
|
||||||
|
__name__: logging.DEBUG,
|
||||||
|
# Log spam caused by prompt_toolkit
|
||||||
|
'asyncio': logging.INFO,
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, level in LEVELS.items():
|
||||||
|
logger = logging.getLogger(name)
|
||||||
|
logger.setLevel(level)
|
||||||
|
|
||||||
|
root_logger = logging.getLogger()
|
||||||
|
|
||||||
fmt = logging.Formatter('{asctime} {levelname} {name}: {message}',
|
fmt = logging.Formatter('{asctime} {levelname} {name}: {message}',
|
||||||
datefmt='%b %d %H:%M:%S',
|
datefmt='%b %d %H:%M:%S',
|
||||||
style='{')
|
style='{')
|
||||||
|
|
||||||
if not any(isinstance(h, logging.StreamHandler) for h in rootLogger.handlers):
|
if not any(
|
||||||
hand = logging.StreamHandler()
|
isinstance(h, logging.StreamHandler) for h in root_logger.handlers):
|
||||||
|
stderr = sys.stderr
|
||||||
|
# XXX: Horrible hack required by the virtual stderr xonsh uses for each entered
|
||||||
|
# command getting closed after the command is run: we need to pull out
|
||||||
|
# the real stderr because this survives across multiple command runs.
|
||||||
|
#
|
||||||
|
# This only applies when running xonsh in interactive mode and importing releng.
|
||||||
|
if isinstance(sys.stderr, xonsh.base_shell._TeeStd):
|
||||||
|
stderr = stderr.std # type: ignore
|
||||||
|
|
||||||
|
hand = logging.StreamHandler(stream=stderr)
|
||||||
|
hand.set_name('releng root handler')
|
||||||
hand.setFormatter(fmt)
|
hand.setFormatter(fmt)
|
||||||
rootLogger.addHandler(hand)
|
root_logger.addHandler(hand)
|
||||||
|
|
||||||
|
|
||||||
|
setup_logging()
|
||||||
|
|
||||||
|
|
||||||
def reload():
|
def reload():
|
||||||
import importlib
|
import importlib
|
||||||
|
|
|
@ -2,16 +2,16 @@ from . import create_release
|
||||||
from . import docker
|
from . import docker
|
||||||
from .environment import RelengEnvironment
|
from .environment import RelengEnvironment
|
||||||
from . import environment
|
from . import environment
|
||||||
import functools
|
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def do_build(args):
|
def do_build(args):
|
||||||
if args.target == 'all':
|
if args.target == 'all':
|
||||||
create_release.build_artifacts(no_check_git=args.no_check_git)
|
create_release.build_artifacts(args.profile, no_check_git=args.no_check_git)
|
||||||
elif args.target == 'manual':
|
elif args.target == 'manual':
|
||||||
eval_result = create_release.eval_jobs()
|
# n.b. args.profile does nothing here, you will just get the x86_64-linux manual no matter what.
|
||||||
|
eval_result = create_release.eval_jobs(args.profile)
|
||||||
create_release.build_manual(eval_result)
|
create_release.build_manual(eval_result)
|
||||||
else:
|
else:
|
||||||
raise ValueError('invalid target, unreachable')
|
raise ValueError('invalid target, unreachable')
|
||||||
|
@ -80,6 +80,10 @@ def main():
|
||||||
build.add_argument('--target',
|
build.add_argument('--target',
|
||||||
choices=['manual', 'all'],
|
choices=['manual', 'all'],
|
||||||
help='Whether to build everything or just the manual')
|
help='Whether to build everything or just the manual')
|
||||||
|
build.add_argument('--profile',
|
||||||
|
default='all',
|
||||||
|
choices=('all', 'x86_64-linux-only'),
|
||||||
|
help='Which systems to build targets for.')
|
||||||
build.set_defaults(cmd=do_build)
|
build.set_defaults(cmd=do_build)
|
||||||
|
|
||||||
upload = sps.add_parser(
|
upload = sps.add_parser(
|
||||||
|
|
|
@ -27,9 +27,6 @@ RELENG_MSG = "Release created with releng/create_release.xsh"
|
||||||
BUILD_CORES = 16
|
BUILD_CORES = 16
|
||||||
MAX_JOBS = 2
|
MAX_JOBS = 2
|
||||||
|
|
||||||
# TODO
|
|
||||||
RELEASE_SYSTEMS = ["x86_64-linux"]
|
|
||||||
|
|
||||||
|
|
||||||
def setup_creds(env: RelengEnvironment):
|
def setup_creds(env: RelengEnvironment):
|
||||||
key = keys.get_ephemeral_key(env)
|
key = keys.get_ephemeral_key(env)
|
||||||
|
@ -82,11 +79,9 @@ def realise(paths: list[str]):
|
||||||
nix-store @(args) @(paths)
|
nix-store @(args) @(paths)
|
||||||
|
|
||||||
|
|
||||||
def eval_jobs():
|
def eval_jobs(build_profile):
|
||||||
nej_output = $(nix-eval-jobs --workers 4 --gc-roots-dir @(GCROOTS_DIR) --force-recurse --flake '.#release-jobs')
|
nej_output = $(nix-eval-jobs --workers 4 --gc-roots-dir @(GCROOTS_DIR) --force-recurse --flake f'.#release-jobs.{build_profile}')
|
||||||
return [x for x in (json.loads(s) for s in nej_output.strip().split('\n'))
|
return [json.loads(s) for s in nej_output.strip().split('\n')]
|
||||||
if x['system'] in RELEASE_SYSTEMS
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def upload_drv_paths_and_outputs(env: RelengEnvironment, paths: list[str]):
|
def upload_drv_paths_and_outputs(env: RelengEnvironment, paths: list[str]):
|
||||||
|
@ -275,11 +270,11 @@ def do_tag_merge(force_tag=False, no_check_git=False):
|
||||||
|
|
||||||
|
|
||||||
def build_manual(eval_result):
|
def build_manual(eval_result):
|
||||||
manual = next(x['outputs']['doc'] for x in eval_result if x['attr'] == 'build.x86_64-linux')
|
(drv, manual) = next((x['drvPath'], x['outputs']['doc']) for x in eval_result if x['attr'] == 'build.x86_64-linux')
|
||||||
print('[+] Building manual')
|
print('[+] Building manual')
|
||||||
realise([manual])
|
realise([drv])
|
||||||
|
|
||||||
cp --no-preserve=mode -vr @(manual)/share/doc/nix @(MANUAL)
|
cp --no-preserve=mode -T -vr @(manual)/share/doc/nix/manual @(MANUAL)
|
||||||
|
|
||||||
|
|
||||||
def upload_manual(env: RelengEnvironment):
|
def upload_manual(env: RelengEnvironment):
|
||||||
|
@ -295,14 +290,14 @@ def upload_manual(env: RelengEnvironment):
|
||||||
aws s3 sync @(MANUAL)/ @(env.docs_bucket)/manual/lix/stable/
|
aws s3 sync @(MANUAL)/ @(env.docs_bucket)/manual/lix/stable/
|
||||||
|
|
||||||
|
|
||||||
def build_artifacts(no_check_git=False):
|
def build_artifacts(build_profile, no_check_git=False):
|
||||||
rm -rf release/
|
rm -rf release/
|
||||||
if not no_check_git:
|
if not no_check_git:
|
||||||
verify_are_on_tag()
|
verify_are_on_tag()
|
||||||
git_preconditions()
|
git_preconditions()
|
||||||
|
|
||||||
print('[+] Evaluating')
|
print('[+] Evaluating')
|
||||||
eval_result = eval_jobs()
|
eval_result = eval_jobs(build_profile)
|
||||||
drv_paths = [x['drvPath'] for x in eval_result]
|
drv_paths = [x['drvPath'] for x in eval_result]
|
||||||
|
|
||||||
print('[+] Building')
|
print('[+] Building')
|
||||||
|
|
|
@ -19,6 +19,7 @@ def check_all_logins(env: RelengEnvironment):
|
||||||
check_login(target)
|
check_login(target)
|
||||||
|
|
||||||
def check_login(target: DockerTarget):
|
def check_login(target: DockerTarget):
|
||||||
|
log.info('Checking login for %s', target.registry_name)
|
||||||
skopeo login @(target.registry_name())
|
skopeo login @(target.registry_name())
|
||||||
|
|
||||||
def upload_docker_images(target: DockerTarget, paths: list[Path]):
|
def upload_docker_images(target: DockerTarget, paths: list[Path]):
|
||||||
|
@ -43,7 +44,23 @@ def upload_docker_images(target: DockerTarget, paths: list[Path]):
|
||||||
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
digest_file = tmp / (path.name + '.digest')
|
digest_file = tmp / (path.name + '.digest')
|
||||||
inspection = json.loads($(skopeo inspect docker-archive:@(path)))
|
tmp_image = tmp / 'tmp-image.tar.gz'
|
||||||
|
|
||||||
|
# insecure-policy: we don't have any signature policy, we are just uploading an image
|
||||||
|
#
|
||||||
|
# Absurd: we copy it into an OCI image first so we can get the hash
|
||||||
|
# we need to upload it untagged, because skopeo has no "don't tag
|
||||||
|
# this" option.
|
||||||
|
# The reason for this is that forgejo's container registry throws
|
||||||
|
# away old versions of tags immediately, so we cannot use a temp
|
||||||
|
# tag, and it *does* reduce confusion to not upload tags that
|
||||||
|
# should not be used.
|
||||||
|
#
|
||||||
|
# Workaround for: https://github.com/containers/skopeo/issues/2354
|
||||||
|
log.info('skopeo copy to temp oci-archive %s', tmp_image)
|
||||||
|
skopeo --insecure-policy copy --format oci --all --digestfile @(digest_file) docker-archive:@(path) oci-archive:@(tmp_image)
|
||||||
|
|
||||||
|
inspection = json.loads($(skopeo inspect oci-archive:@(tmp_image)))
|
||||||
|
|
||||||
docker_arch = inspection['Architecture']
|
docker_arch = inspection['Architecture']
|
||||||
docker_os = inspection['Os']
|
docker_os = inspection['Os']
|
||||||
|
@ -51,21 +68,13 @@ def upload_docker_images(target: DockerTarget, paths: list[Path]):
|
||||||
|
|
||||||
log.info('Pushing image %s for %s to %s', path, docker_arch, target.registry_path)
|
log.info('Pushing image %s for %s to %s', path, docker_arch, target.registry_path)
|
||||||
|
|
||||||
# insecure-policy: we don't have any signature policy, we are just uploading an image
|
|
||||||
# We upload to a junk tag, because otherwise it will upload to `latest`, which is undesirable
|
|
||||||
skopeo --insecure-policy copy --format oci --digestfile @(digest_file) docker-archive:@(path) docker://@(target.registry_path):temp
|
|
||||||
|
|
||||||
digest = digest_file.read_text().strip()
|
digest = digest_file.read_text().strip()
|
||||||
|
skopeo --insecure-policy copy --preserve-digests --all oci-archive:@(tmp_image) f'docker://{target.registry_path}@{digest}'
|
||||||
|
|
||||||
# skopeo doesn't give us the manifest size directly, so we just ask the registry
|
# skopeo doesn't give us the manifest size directly, so we just ask the registry
|
||||||
metadata = reg.image_info(target.registry_path, digest)
|
metadata = reg.image_info(target.registry_path, digest)
|
||||||
|
|
||||||
manifests.append(OCIIndexItem(metadata=metadata, architecture=docker_arch, os=docker_os))
|
manifests.append(OCIIndexItem(metadata=metadata, architecture=docker_arch, os=docker_os))
|
||||||
# delete the temp tag, which we only have to create because of skopeo
|
|
||||||
# limitations anyhow (it seems to not have a way to say "don't tag it, find
|
|
||||||
# your checksum and put it there")
|
|
||||||
# FIXME: this is not possible because GitHub only has a proprietary API for it. amazing. 11/10.
|
|
||||||
# reg.delete_tag(target.registry_path, 'temp')
|
|
||||||
|
|
||||||
log.info('Pushed images to %r, building a bigger and more menacing manifest from %r with metadata %r', target, manifests, meta)
|
log.info('Pushed images to %r, building a bigger and more menacing manifest from %r with metadata %r', target, manifests, meta)
|
||||||
# send the multiarch manifest to each tag
|
# send the multiarch manifest to each tag
|
||||||
|
|
|
@ -49,8 +49,8 @@ if DEBUG_REQUESTS:
|
||||||
# fix that. Thus, a little bit of homebrew containers code.
|
# fix that. Thus, a little bit of homebrew containers code.
|
||||||
#
|
#
|
||||||
# Essentially what we are doing in here is splatting a bunch of images into the
|
# Essentially what we are doing in here is splatting a bunch of images into the
|
||||||
# registry without tagging them (except as "temp", due to podman issues), then
|
# registry without tagging them (with a silly workaround to skopeo issues),
|
||||||
# simply sending a new composite manifest ourselves.
|
# then simply sending a new composite manifest ourselves.
|
||||||
|
|
||||||
DockerArchitecture = Literal['amd64'] | Literal['arm64']
|
DockerArchitecture = Literal['amd64'] | Literal['arm64']
|
||||||
MANIFEST_MIME = 'application/vnd.oci.image.manifest.v1+json'
|
MANIFEST_MIME = 'application/vnd.oci.image.manifest.v1+json'
|
||||||
|
@ -100,14 +100,6 @@ class OCIIndex:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def docker_architecture_from_nix_system(system: str) -> DockerArchitecture:
|
|
||||||
MAP = {
|
|
||||||
'x86_64-linux': 'amd64',
|
|
||||||
'aarch64-linux': 'arm64',
|
|
||||||
}
|
|
||||||
return MAP[system] # type: ignore
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class TaggingOperation:
|
class TaggingOperation:
|
||||||
manifest: OCIIndex
|
manifest: OCIIndex
|
||||||
|
@ -284,7 +276,7 @@ class AuthState:
|
||||||
'Authorization': 'Basic ' + creds
|
'Authorization': 'Basic ' + creds
|
||||||
}).json()
|
}).json()
|
||||||
token = resp['token']
|
token = resp['token']
|
||||||
self.token_cache[service] = token
|
self.token_cache[authority] = token
|
||||||
return token
|
return token
|
||||||
|
|
||||||
def find_credential_for(self, image_path: str):
|
def find_credential_for(self, image_path: str):
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import subprocess
|
import subprocess
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from .version import VERSION
|
||||||
|
|
||||||
|
|
||||||
def version_compare(v1: str, v2: str):
|
def version_compare(v1: str, v2: str):
|
||||||
return json.loads($(nix-instantiate --eval --json --argstr v1 @(v1) --argstr v2 @(v2) --expr '{v1, v2}: builtins.compareVersions v1 v2'))
|
return json.loads($(nix-instantiate --eval --json --argstr v1 @(v1) --argstr v2 @(v2) --expr '{v1, v2}: builtins.compareVersions v1 v2'))
|
||||||
|
|
|
@ -3,8 +3,27 @@ let
|
||||||
inherit (pkgs) lib;
|
inherit (pkgs) lib;
|
||||||
lix = hydraJobs.build.x86_64-linux;
|
lix = hydraJobs.build.x86_64-linux;
|
||||||
|
|
||||||
|
# This is all so clumsy because we can't use arguments to functions in
|
||||||
|
# flakes, and certainly not with n-e-j.
|
||||||
|
profiles = {
|
||||||
|
# Used for testing
|
||||||
|
x86_64-linux-only = {
|
||||||
systems = [ "x86_64-linux" ];
|
systems = [ "x86_64-linux" ];
|
||||||
dockerSystems = [ "x86_64-linux" ];
|
dockerSystems = [ "x86_64-linux" ];
|
||||||
|
};
|
||||||
|
all = {
|
||||||
|
systems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
"aarch64-darwin"
|
||||||
|
"x86_64-darwin"
|
||||||
|
];
|
||||||
|
dockerSystems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
doTarball =
|
doTarball =
|
||||||
{
|
{
|
||||||
|
@ -27,7 +46,8 @@ let
|
||||||
sha256sum --binary $filename | cut -f1 -d' ' > $out/$basename.sha256
|
sha256sum --binary $filename | cut -f1 -d' ' > $out/$basename.sha256
|
||||||
'';
|
'';
|
||||||
|
|
||||||
targets =
|
targetsFor =
|
||||||
|
{ systems, dockerSystems }:
|
||||||
builtins.map (system: {
|
builtins.map (system: {
|
||||||
target = hydraJobs.binaryTarball.${system};
|
target = hydraJobs.binaryTarball.${system};
|
||||||
targetName = "*.tar.xz";
|
targetName = "*.tar.xz";
|
||||||
|
@ -44,14 +64,29 @@ let
|
||||||
tar -cvzf "$out/lix-${lix.version}-manual.tar.gz" lix-${lix.version}-manual
|
tar -cvzf "$out/lix-${lix.version}-manual.tar.gz" lix-${lix.version}-manual
|
||||||
'';
|
'';
|
||||||
|
|
||||||
tarballs = pkgs.runCommand "lix-release-tarballs" { } ''
|
tarballsFor =
|
||||||
|
{ systems, dockerSystems }:
|
||||||
|
pkgs.runCommand "lix-release-tarballs" { } ''
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
${lib.concatMapStringsSep "\n" doTarball targets}
|
${lib.concatMapStringsSep "\n" doTarball (targetsFor {
|
||||||
cp ${manualTar}/*.tar.gz $out
|
inherit systems dockerSystems;
|
||||||
|
})}
|
||||||
|
${doTarball {
|
||||||
|
target = manualTar;
|
||||||
|
targetName = "lix-*.tar.gz";
|
||||||
|
}}
|
||||||
cp -r ${lix.doc}/share/doc/nix/manual $out
|
cp -r ${lix.doc}/share/doc/nix/manual $out
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
|
(builtins.mapAttrs (
|
||||||
|
_:
|
||||||
|
{ systems, dockerSystems }:
|
||||||
{
|
{
|
||||||
inherit (hydraJobs) build;
|
build = lib.filterAttrs (x: _: builtins.elem x systems) hydraJobs.build;
|
||||||
inherit tarballs;
|
tarballs = tarballsFor { inherit systems dockerSystems; };
|
||||||
|
}
|
||||||
|
) profiles)
|
||||||
|
// {
|
||||||
|
inherit (hydraJobs) build;
|
||||||
|
inherit tarballsFor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ struct EvalSettings : Config
|
||||||
Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state:
|
Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state:
|
||||||
|
|
||||||
- Restrict file system and network access to files specified by cryptographic hash
|
- Restrict file system and network access to files specified by cryptographic hash
|
||||||
- Disable [`bultins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime)
|
- Disable [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime)
|
||||||
)"
|
)"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,17 @@ struct FetchSettings : public Config
|
||||||
{}, true, Xp::Flakes};
|
{}, true, Xp::Flakes};
|
||||||
|
|
||||||
Setting<bool> acceptFlakeConfig{this, false, "accept-flake-config",
|
Setting<bool> acceptFlakeConfig{this, false, "accept-flake-config",
|
||||||
"Whether to accept nix configuration from a flake without prompting.",
|
R"(
|
||||||
|
Whether to accept Lix configuration from the `nixConfig` attribute of
|
||||||
|
a flake without prompting. This is almost always a very bad idea.
|
||||||
|
|
||||||
|
Setting this setting as a trusted user allows Nix flakes to gain root
|
||||||
|
access on your machine if they set one of the several
|
||||||
|
trusted-user-only settings that execute commands as root.
|
||||||
|
|
||||||
|
See [multi-user installations](@docroot@/installation/multi-user.md)
|
||||||
|
for more details on the Lix security model.
|
||||||
|
)",
|
||||||
{}, true, Xp::Flakes};
|
{}, true, Xp::Flakes};
|
||||||
|
|
||||||
Setting<std::string> commitLockFileSummary{
|
Setting<std::string> commitLockFileSummary{
|
||||||
|
|
|
@ -232,7 +232,7 @@ std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, co
|
||||||
if (S_ISDIR(st.st_mode)) {
|
if (S_ISDIR(st.st_mode)) {
|
||||||
auto prefix = file + "/";
|
auto prefix = file + "/";
|
||||||
auto i = files.lower_bound(prefix);
|
auto i = files.lower_bound(prefix);
|
||||||
return i != files.end() && (*i).starts_with(prefix);
|
return (i != files.end() && (*i).starts_with(prefix)) || files.count(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
return files.count(file);
|
return files.count(file);
|
||||||
|
|
|
@ -57,7 +57,7 @@ MixCommonArgs::MixCommonArgs(const std::string & programName)
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "log-format",
|
.longName = "log-format",
|
||||||
.description = "Set the format of log output; one of `raw`, `internal-json`, `bar` or `bar-with-logs`.",
|
.description = "Set the format of log output; one of `raw`, `internal-json`, `bar`, `bar-with-logs`, `multiline` or `multiline-with-logs`.",
|
||||||
.category = loggingCategory,
|
.category = loggingCategory,
|
||||||
.labels = {"format"},
|
.labels = {"format"},
|
||||||
.handler = {[](std::string format) { setLogFormat(format); }},
|
.handler = {[](std::string format) { setLogFormat(format); }},
|
||||||
|
|
|
@ -17,6 +17,10 @@ LogFormat parseLogFormat(const std::string & logFormatStr) {
|
||||||
return LogFormat::bar;
|
return LogFormat::bar;
|
||||||
else if (logFormatStr == "bar-with-logs")
|
else if (logFormatStr == "bar-with-logs")
|
||||||
return LogFormat::barWithLogs;
|
return LogFormat::barWithLogs;
|
||||||
|
else if (logFormatStr == "multiline")
|
||||||
|
return LogFormat::multiline;
|
||||||
|
else if (logFormatStr == "multiline-with-logs")
|
||||||
|
return LogFormat::multilineWithLogs;
|
||||||
throw Error("option 'log-format' has an invalid value '%s'", logFormatStr);
|
throw Error("option 'log-format' has an invalid value '%s'", logFormatStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +39,17 @@ Logger * makeDefaultLogger() {
|
||||||
logger->setPrintBuildLogs(true);
|
logger->setPrintBuildLogs(true);
|
||||||
return logger;
|
return logger;
|
||||||
}
|
}
|
||||||
|
case LogFormat::multiline: {
|
||||||
|
auto logger = makeProgressBar();
|
||||||
|
logger->setPrintMultiline(true);
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
case LogFormat::multilineWithLogs: {
|
||||||
|
auto logger = makeProgressBar();
|
||||||
|
logger->setPrintMultiline(true);
|
||||||
|
logger->setPrintBuildLogs(true);
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ enum class LogFormat {
|
||||||
internalJSON,
|
internalJSON,
|
||||||
bar,
|
bar,
|
||||||
barWithLogs,
|
barWithLogs,
|
||||||
|
multiline,
|
||||||
|
multilineWithLogs,
|
||||||
};
|
};
|
||||||
|
|
||||||
void setLogFormat(const std::string & logFormatStr);
|
void setLogFormat(const std::string & logFormatStr);
|
||||||
|
|
|
@ -73,6 +73,8 @@ private:
|
||||||
|
|
||||||
std::map<ActivityType, ActivitiesByType> activitiesByType;
|
std::map<ActivityType, ActivitiesByType> activitiesByType;
|
||||||
|
|
||||||
|
int lastLines = 0;
|
||||||
|
|
||||||
uint64_t filesLinked = 0, bytesLinked = 0;
|
uint64_t filesLinked = 0, bytesLinked = 0;
|
||||||
|
|
||||||
uint64_t corruptedPaths = 0, untrustedPaths = 0;
|
uint64_t corruptedPaths = 0, untrustedPaths = 0;
|
||||||
|
@ -89,6 +91,7 @@ private:
|
||||||
std::condition_variable quitCV, updateCV;
|
std::condition_variable quitCV, updateCV;
|
||||||
|
|
||||||
bool printBuildLogs = false;
|
bool printBuildLogs = false;
|
||||||
|
bool printMultiline = false;
|
||||||
bool isTTY;
|
bool isTTY;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -103,7 +106,7 @@ public:
|
||||||
while (state->active) {
|
while (state->active) {
|
||||||
if (!state->haveUpdate)
|
if (!state->haveUpdate)
|
||||||
state.wait_for(updateCV, nextWakeup);
|
state.wait_for(updateCV, nextWakeup);
|
||||||
nextWakeup = draw(*state);
|
nextWakeup = draw(*state, {});
|
||||||
state.wait_for(quitCV, std::chrono::milliseconds(50));
|
state.wait_for(quitCV, std::chrono::milliseconds(50));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -165,8 +168,7 @@ public:
|
||||||
void log(State & state, Verbosity lvl, std::string_view s)
|
void log(State & state, Verbosity lvl, std::string_view s)
|
||||||
{
|
{
|
||||||
if (state.active) {
|
if (state.active) {
|
||||||
writeToStderr("\r\e[K" + filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n");
|
draw(state, s);
|
||||||
draw(state);
|
|
||||||
} else {
|
} else {
|
||||||
auto s2 = s + ANSI_NORMAL "\n";
|
auto s2 = s + ANSI_NORMAL "\n";
|
||||||
if (!isTTY) s2 = filterANSIEscapes(s2, true);
|
if (!isTTY) s2 = filterANSIEscapes(s2, true);
|
||||||
|
@ -354,60 +356,100 @@ public:
|
||||||
updateCV.notify_one();
|
updateCV.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::milliseconds draw(State & state)
|
std::chrono::milliseconds draw(State & state, const std::optional<std::string_view> & s)
|
||||||
{
|
{
|
||||||
auto nextWakeup = A_LONG_TIME;
|
auto nextWakeup = A_LONG_TIME;
|
||||||
|
|
||||||
state.haveUpdate = false;
|
state.haveUpdate = false;
|
||||||
if (state.paused || !state.active) return nextWakeup;
|
if (state.paused || !state.active) return nextWakeup;
|
||||||
|
|
||||||
std::string line;
|
auto windowSize = getWindowSize();
|
||||||
|
auto width = windowSize.second;
|
||||||
|
if (width <= 0) {
|
||||||
|
width = std::numeric_limits<decltype(width)>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (printMultiline && (state.lastLines >= 1)) {
|
||||||
|
// FIXME: make sure this works on windows
|
||||||
|
writeToStderr(fmt("\e[G\e[%dF\e[J", state.lastLines));
|
||||||
|
}
|
||||||
|
|
||||||
|
state.lastLines = 0;
|
||||||
|
|
||||||
|
if (s != std::nullopt)
|
||||||
|
writeToStderr("\r\e[K" + filterANSIEscapes(s.value(), !isTTY) + ANSI_NORMAL "\n");
|
||||||
|
|
||||||
|
std::string line;
|
||||||
std::string status = getStatus(state);
|
std::string status = getStatus(state);
|
||||||
if (!status.empty()) {
|
if (!status.empty()) {
|
||||||
line += '[';
|
line += '[';
|
||||||
line += status;
|
line += status;
|
||||||
line += "]";
|
line += "]";
|
||||||
}
|
}
|
||||||
|
if (printMultiline && !line.empty()) {
|
||||||
|
writeToStderr(filterANSIEscapes(line, false, width) + "\n");
|
||||||
|
state.lastLines++;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto height = windowSize.first > 0 ? windowSize.first : 25;
|
||||||
|
auto moreBuilds = 0;
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
if (!state.activities.empty()) {
|
if (!state.activities.empty()) {
|
||||||
if (!status.empty()) line += " ";
|
for (auto i = state.activities.begin(); i != state.activities.end(); ++i) {
|
||||||
auto i = state.activities.rbegin();
|
if (!(i->visible && (!i->s.empty() || !i->lastLine.empty()))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
while (i != state.activities.rend()) {
|
|
||||||
if (i->visible && (!i->s.empty() || !i->lastLine.empty())) {
|
|
||||||
/* Don't show activities until some time has
|
/* Don't show activities until some time has
|
||||||
passed, to avoid displaying very short
|
passed, to avoid displaying very short
|
||||||
activities. */
|
activities. */
|
||||||
auto delay = std::chrono::milliseconds(10);
|
auto delay = std::chrono::milliseconds(10);
|
||||||
if (i->startTime + delay < now)
|
if (i->startTime + delay >= now) {
|
||||||
break;
|
nextWakeup = std::min(
|
||||||
else
|
nextWakeup,
|
||||||
nextWakeup = std::min(nextWakeup, std::chrono::duration_cast<std::chrono::milliseconds>(delay - (now - i->startTime)));
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
}
|
delay - (now - i->startTime)
|
||||||
++i;
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i != state.activities.rend()) {
|
if (printMultiline) {
|
||||||
|
line = i->s;
|
||||||
|
} else {
|
||||||
|
line += " ";
|
||||||
line += i->s;
|
line += i->s;
|
||||||
|
}
|
||||||
|
|
||||||
if (!i->phase.empty()) {
|
if (!i->phase.empty()) {
|
||||||
line += " (";
|
line += " (";
|
||||||
line += i->phase;
|
line += i->phase;
|
||||||
line += ")";
|
line += ")";
|
||||||
}
|
}
|
||||||
if (!i->lastLine.empty()) {
|
if (!i->lastLine.empty()) {
|
||||||
if (!i->s.empty()) line += ": ";
|
if (!i->s.empty()) {
|
||||||
|
line += ": ";
|
||||||
|
}
|
||||||
line += i->lastLine;
|
line += i->lastLine;
|
||||||
}
|
}
|
||||||
|
if (printMultiline) {
|
||||||
|
if (state.lastLines < (height - 1)) {
|
||||||
|
writeToStderr(filterANSIEscapes(line, false, width) + "\n");
|
||||||
|
state.lastLines++;
|
||||||
|
} else {
|
||||||
|
moreBuilds++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto width = getWindowSize().second;
|
if (printMultiline && moreBuilds) {
|
||||||
if (width <= 0) width = std::numeric_limits<decltype(width)>::max();
|
writeToStderr(fmt("And %d more...", moreBuilds));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!printMultiline) {
|
||||||
writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K");
|
writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K");
|
||||||
|
}
|
||||||
|
|
||||||
return nextWakeup;
|
return nextWakeup;
|
||||||
}
|
}
|
||||||
|
@ -506,9 +548,8 @@ public:
|
||||||
{
|
{
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
if (state->active) {
|
if (state->active) {
|
||||||
std::cerr << "\r\e[K";
|
|
||||||
Logger::writeToStdout(s);
|
Logger::writeToStdout(s);
|
||||||
draw(*state);
|
draw(*state, {});
|
||||||
} else {
|
} else {
|
||||||
Logger::writeToStdout(s);
|
Logger::writeToStdout(s);
|
||||||
}
|
}
|
||||||
|
@ -521,7 +562,7 @@ public:
|
||||||
std::cerr << fmt("\r\e[K%s ", msg);
|
std::cerr << fmt("\r\e[K%s ", msg);
|
||||||
auto s = trim(readLine(STDIN_FILENO));
|
auto s = trim(readLine(STDIN_FILENO));
|
||||||
if (s.size() != 1) return {};
|
if (s.size() != 1) return {};
|
||||||
draw(*state);
|
draw(*state, {});
|
||||||
return s[0];
|
return s[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,6 +570,11 @@ public:
|
||||||
{
|
{
|
||||||
this->printBuildLogs = printBuildLogs;
|
this->printBuildLogs = printBuildLogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setPrintMultiline(bool printMultiline) override
|
||||||
|
{
|
||||||
|
this->printMultiline = printMultiline;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Logger * makeProgressBar()
|
Logger * makeProgressBar()
|
||||||
|
|
|
@ -44,7 +44,6 @@
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#if HAVE_SECCOMP
|
#if HAVE_SECCOMP
|
||||||
#include "linux/fchmodat2-compat.hh"
|
|
||||||
#include <seccomp.h>
|
#include <seccomp.h>
|
||||||
#endif
|
#endif
|
||||||
#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
|
#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
|
||||||
|
@ -1617,6 +1616,12 @@ void LocalDerivationGoal::chownToBuilder(const Path & path)
|
||||||
throw SysError("cannot change ownership of '%1%'", path);
|
throw SysError("cannot change ownership of '%1%'", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAVE_SECCOMP
|
||||||
|
void allowSyscall(scmp_filter_ctx ctx, int syscall) {
|
||||||
|
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall, 0) != 0)
|
||||||
|
throw SysError("unable to add seccomp rule");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void setupSeccomp()
|
void setupSeccomp()
|
||||||
{
|
{
|
||||||
|
@ -1624,7 +1629,9 @@ void setupSeccomp()
|
||||||
#if HAVE_SECCOMP
|
#if HAVE_SECCOMP
|
||||||
scmp_filter_ctx ctx;
|
scmp_filter_ctx ctx;
|
||||||
|
|
||||||
if (!(ctx = seccomp_init(SCMP_ACT_ALLOW)))
|
// Pretend that syscalls we don't yet know about don't exist.
|
||||||
|
// This is the best option for compatibility: after all, they did in fact not exist not too long ago.
|
||||||
|
if (!(ctx = seccomp_init(SCMP_ACT_ERRNO(ENOSYS))))
|
||||||
throw SysError("unable to initialize seccomp mode 2");
|
throw SysError("unable to initialize seccomp mode 2");
|
||||||
|
|
||||||
Finally cleanup([&]() {
|
Finally cleanup([&]() {
|
||||||
|
@ -1659,28 +1666,520 @@ void setupSeccomp()
|
||||||
seccomp_arch_add(ctx, SCMP_ARCH_MIPSEL64N32) != 0)
|
seccomp_arch_add(ctx, SCMP_ARCH_MIPSEL64N32) != 0)
|
||||||
printError("unable to add mips64el-*abin32 seccomp architecture");
|
printError("unable to add mips64el-*abin32 seccomp architecture");
|
||||||
|
|
||||||
/* Prevent builders from creating setuid/setgid binaries. */
|
// This list is intended for machine consumption.
|
||||||
for (int perm : { S_ISUID, S_ISGID }) {
|
// Please keep its format, order and BEGIN/END markers.
|
||||||
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(chmod), 1,
|
//
|
||||||
SCMP_A1(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0)
|
// Currently, it is up to date with libseccomp 2.5.5 and glibc 2.38.
|
||||||
|
// Run check-syscalls to determine which new syscalls should be added.
|
||||||
|
// New syscalls must be audited and handled in a way that blocks the following dangerous operations:
|
||||||
|
// * Creation of non-empty setuid/setgid files
|
||||||
|
// * Creation of extended attributes (including ACLs)
|
||||||
|
//
|
||||||
|
// BEGIN extract-syscalls
|
||||||
|
allowSyscall(ctx, SCMP_SYS(accept));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(accept4));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(access));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(acct));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(add_key));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(adjtimex));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(afs_syscall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(alarm));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(arch_prctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(arm_fadvise64_64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(arm_sync_file_range));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(bdflush));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(bind));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(bpf));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(break));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(breakpoint));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(brk));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(cachectl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(cacheflush));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(cachestat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(capget));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(capset));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(chdir));
|
||||||
|
// skip chmod (dangerous)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(chown));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(chown32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(chroot));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_adjtime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_adjtime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_getres));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_getres_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_gettime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_gettime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_nanosleep));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_nanosleep_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_settime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_settime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clone));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clone3));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(close));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(close_range));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(connect));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(copy_file_range));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(creat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(create_module));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(delete_module));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(dup));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(dup2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(dup3));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_create));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_create1));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_ctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_ctl_old));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_pwait));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_pwait2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_wait));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_wait_old));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(eventfd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(eventfd2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(execve));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(execveat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(exit));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(exit_group));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(faccessat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(faccessat2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fadvise64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fadvise64_64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fallocate));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fanotify_init));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fanotify_mark));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fchdir));
|
||||||
|
// skip fchmod (dangerous)
|
||||||
|
// skip fchmodat (dangerous)
|
||||||
|
// skip fchmodat2 (requires glibc 2.39, dangerous)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fchown));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fchown32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fchownat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fcntl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fcntl64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fdatasync));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fgetxattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(finit_module));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(flistxattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(flock));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fork));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fremovexattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fsconfig));
|
||||||
|
// skip fsetxattr (dangerous)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fsmount));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fsopen));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fspick));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fstat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fstat64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fstatat64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fstatfs));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fstatfs64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fsync));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ftime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ftruncate));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ftruncate64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(futex));
|
||||||
|
// skip futex_requeue (requires glibc 2.39)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(futex_time64));
|
||||||
|
// skip futex_wait (requires glibc 2.39)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(futex_waitv));
|
||||||
|
// skip futex_wake (requires glibc 2.39)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(futimesat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getcpu));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getcwd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getdents));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getdents64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getegid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getegid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(geteuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(geteuid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getgid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getgroups));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getgroups32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getitimer));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(get_kernel_syms));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(get_mempolicy));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getpeername));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getpgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getpgrp));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getpid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getpmsg));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getppid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getpriority));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getrandom));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getresgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getresgid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getresuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getresuid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getrlimit));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(get_robust_list));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getrusage));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getsid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getsockname));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getsockopt));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(get_thread_area));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(gettid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(gettimeofday));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(get_tls));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getuid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getxattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(gtty));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(idle));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(init_module));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(inotify_add_watch));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(inotify_init));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(inotify_init1));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(inotify_rm_watch));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_cancel));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ioctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_destroy));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_getevents));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ioperm));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_pgetevents));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_pgetevents_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(iopl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ioprio_get));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ioprio_set));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_setup));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_submit));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_uring_enter));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_uring_register));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_uring_setup));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ipc));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(kcmp));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(kexec_file_load));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(kexec_load));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(keyctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(kill));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(landlock_add_rule));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(landlock_create_ruleset));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(landlock_restrict_self));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lchown));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lchown32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lgetxattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(link));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(linkat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(listen));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(listxattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(llistxattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(_llseek));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lock));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lookup_dcookie));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lremovexattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lseek));
|
||||||
|
// skip lsetxattr (dangerous)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lstat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lstat64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(madvise));
|
||||||
|
// skip map_shadow_stack (requires glibc 2.39)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mbind));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(membarrier));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(memfd_create));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(memfd_secret));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(migrate_pages));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mincore));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mkdir));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mkdirat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mknod));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mknodat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mlock));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mlock2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mlockall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mmap));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mmap2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(modify_ldt));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mount));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mount_setattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(move_mount));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(move_pages));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mprotect));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mpx));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_getsetattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_notify));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_open));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_timedreceive));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_timedreceive_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_timedsend));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_timedsend_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_unlink));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mremap));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(msgctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(msgget));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(msgrcv));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(msgsnd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(msync));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(multiplexer));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(munlock));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(munlockall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(munmap));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(name_to_handle_at));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(nanosleep));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(newfstatat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(_newselect));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(nfsservctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(nice));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(oldfstat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(oldlstat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(oldolduname));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(oldstat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(olduname));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(open));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(openat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(openat2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(open_by_handle_at));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(open_tree));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pause));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pciconfig_iobase));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pciconfig_read));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pciconfig_write));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(perf_event_open));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(personality));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pidfd_getfd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pidfd_open));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pidfd_send_signal));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pipe));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pipe2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pivot_root));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pkey_alloc));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pkey_free));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pkey_mprotect));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(poll));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ppoll));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ppoll_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(prctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pread64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(preadv));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(preadv2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(prlimit64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(process_madvise));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(process_mrelease));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(process_vm_readv));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(process_vm_writev));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(prof));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(profil));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pselect6));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pselect6_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ptrace));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(putpmsg));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pwrite64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pwritev));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pwritev2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(query_module));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(quotactl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(quotactl_fd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(read));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(readahead));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(readdir));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(readlink));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(readlinkat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(readv));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(reboot));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(recv));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(recvfrom));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(recvmmsg));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(recvmmsg_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(recvmsg));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(remap_file_pages));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(removexattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rename));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(renameat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(renameat2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(request_key));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(restart_syscall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(riscv_flush_icache));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rmdir));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rseq));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rtas));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigaction));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigpending));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigprocmask));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigqueueinfo));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigreturn));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigsuspend));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigtimedwait));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigtimedwait_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_tgsigqueueinfo));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(s390_guarded_storage));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(s390_pci_mmio_read));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(s390_pci_mmio_write));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(s390_runtime_instr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(s390_sthyi));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_getaffinity));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_getattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_getparam));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_get_priority_max));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_get_priority_min));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_getscheduler));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_rr_get_interval));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_rr_get_interval_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_setaffinity));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_setattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_setparam));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_setscheduler));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_yield));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(seccomp));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(security));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(select));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(semctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(semget));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(semop));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(semtimedop));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(semtimedop_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(send));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sendfile));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sendfile64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sendmmsg));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sendmsg));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sendto));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setdomainname));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setfsgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setfsgid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setfsuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setfsuid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setgid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setgroups));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setgroups32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sethostname));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setitimer));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(set_mempolicy));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(set_mempolicy_home_node));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setns));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setpgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setpriority));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setregid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setregid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setresgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setresgid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setresuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setresuid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setreuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setreuid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setrlimit));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(set_robust_list));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setsid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setsockopt));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(set_thread_area));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(set_tid_address));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(settimeofday));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(set_tls));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setuid32));
|
||||||
|
// skip setxattr (dangerous)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sgetmask));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(shmat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(shmctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(shmdt));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(shmget));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(shutdown));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sigaction));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sigaltstack));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(signal));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(signalfd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(signalfd4));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sigpending));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sigprocmask));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sigreturn));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sigsuspend));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(socket));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(socketcall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(socketpair));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(splice));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(spu_create));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(spu_run));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ssetmask));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(stat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(stat64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(statfs));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(statfs64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(statx));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(stime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(stty));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(subpage_prot));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(swapcontext));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(swapoff));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(swapon));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(switch_endian));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(symlink));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(symlinkat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sync));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sync_file_range));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sync_file_range2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(syncfs));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(syscall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(_sysctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sys_debug_setcontext));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sysfs));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sysinfo));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(syslog));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sysmips));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(tee));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(tgkill));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(time));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_create));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_delete));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timerfd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timerfd_create));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timerfd_gettime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timerfd_gettime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timerfd_settime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timerfd_settime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_getoverrun));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_gettime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_gettime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_settime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_settime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(times));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(tkill));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(truncate));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(truncate64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(tuxcall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ugetrlimit));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ulimit));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(umask));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(umount));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(umount2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(uname));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(unlink));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(unlinkat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(unshare));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(uselib));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(userfaultfd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(usr26));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(usr32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ustat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(utime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(utimensat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(utimensat_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(utimes));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(vfork));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(vhangup));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(vm86));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(vm86old));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(vmsplice));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(vserver));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(wait4));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(waitid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(waitpid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(write));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(writev));
|
||||||
|
// END extract-syscalls
|
||||||
|
|
||||||
|
// chmod family: prevent adding setuid/setgid bits to existing files.
|
||||||
|
// The Nix store does not support setuid/setgid, and even their temporary creation can weaken the security of the sandbox.
|
||||||
|
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(chmod), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID | S_ISGID, 0)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(chmod), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(chmod), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fchmod), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID | S_ISGID, 0)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmod), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmod), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fchmodat), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, S_ISUID | S_ISGID, 0)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmodat), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmodat), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) != 0)
|
||||||
throw SysError("unable to add seccomp rule");
|
throw SysError("unable to add seccomp rule");
|
||||||
|
|
||||||
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmod), 1,
|
// setxattr family: prevent creation of extended attributes or ACLs.
|
||||||
SCMP_A1(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0)
|
// Not all filesystems support them, and they're incompatible with the NAR format.
|
||||||
throw SysError("unable to add seccomp rule");
|
|
||||||
|
|
||||||
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmodat), 1,
|
|
||||||
SCMP_A2(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0)
|
|
||||||
throw SysError("unable to add seccomp rule");
|
|
||||||
|
|
||||||
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), NIX_SYSCALL_FCHMODAT2, 1,
|
|
||||||
SCMP_A2(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0)
|
|
||||||
throw SysError("unable to add seccomp rule");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prevent builders from creating EAs or ACLs. Not all filesystems
|
|
||||||
support these, and they're not allowed in the Nix store because
|
|
||||||
they're not representable in the NAR serialisation. */
|
|
||||||
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(setxattr), 0) != 0 ||
|
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(setxattr), 0) != 0 ||
|
||||||
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(lsetxattr), 0) != 0 ||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(lsetxattr), 0) != 0 ||
|
||||||
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) != 0)
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) != 0)
|
||||||
|
@ -1714,11 +2213,7 @@ void LocalDerivationGoal::runChild()
|
||||||
|
|
||||||
commonChildInit();
|
commonChildInit();
|
||||||
|
|
||||||
try {
|
|
||||||
setupSeccomp();
|
setupSeccomp();
|
||||||
} catch (...) {
|
|
||||||
if (buildUser) throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setUser = true;
|
bool setUser = true;
|
||||||
|
|
||||||
|
|
|
@ -208,7 +208,7 @@ struct DerivationType {
|
||||||
/**
|
/**
|
||||||
* Impure derivation type
|
* Impure derivation type
|
||||||
*
|
*
|
||||||
* This is similar at buil-time to the content addressed, not standboxed, not fixed
|
* This is similar at build-time to the content addressed, not sandboxed, not fixed
|
||||||
* type, but has some restrictions on its usage.
|
* type, but has some restrictions on its usage.
|
||||||
*/
|
*/
|
||||||
struct Impure {
|
struct Impure {
|
||||||
|
|
|
@ -331,7 +331,7 @@ public:
|
||||||
performed by the Lix account since that would allow users to
|
performed by the Lix account since that would allow users to
|
||||||
arbitrarily modify the Nix store and database by supplying specially
|
arbitrarily modify the Nix store and database by supplying specially
|
||||||
crafted builders; and they cannot be performed by the calling user
|
crafted builders; and they cannot be performed by the calling user
|
||||||
since that would allow him/her to influence the build result.
|
since that would allow them to influence the build result.
|
||||||
|
|
||||||
Therefore, if this option is non-empty and specifies a valid group,
|
Therefore, if this option is non-empty and specifies a valid group,
|
||||||
builds will be performed under the user accounts that are a member
|
builds will be performed under the user accounts that are a member
|
||||||
|
@ -352,10 +352,17 @@ public:
|
||||||
|
|
||||||
If the build users group is empty, builds will be performed under
|
If the build users group is empty, builds will be performed under
|
||||||
the uid of the Lix process (that is, the uid of the caller if
|
the uid of the Lix process (that is, the uid of the caller if
|
||||||
`NIX_REMOTE` is empty, the uid under which the Nix daemon runs if
|
both `NIX_REMOTE` is either empty or `auto` and the Nix store is
|
||||||
`NIX_REMOTE` is `daemon`). Obviously, this should not be used
|
owned by that user, or, alternatively, the uid under which the Nix
|
||||||
|
daemon runs if `NIX_REMOTE` is `daemon` or if it is `auto` and the
|
||||||
|
store is not owned by the caller). Obviously, this should not be used
|
||||||
with a nix daemon accessible to untrusted clients.
|
with a nix daemon accessible to untrusted clients.
|
||||||
|
|
||||||
|
For the avoidance of doubt, explicitly setting this to *empty* with a
|
||||||
|
Lix daemon running as root means that builds will be executed as root
|
||||||
|
with respect to the rest of the system.
|
||||||
|
We intend to fix this: https://git.lix.systems/lix-project/lix/issues/242
|
||||||
|
|
||||||
Defaults to `nixbld` when running as root, *empty* otherwise.
|
Defaults to `nixbld` when running as root, *empty* otherwise.
|
||||||
)",
|
)",
|
||||||
{}, false};
|
{}, false};
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Determine the syscall number for `fchmodat2`.
|
|
||||||
*
|
|
||||||
* On most platforms this is 452. Exceptions can be found on
|
|
||||||
* a glibc git checkout via `rg --pcre2 'define __NR_fchmodat2 (?!452)'`.
|
|
||||||
*
|
|
||||||
* The problem is that glibc 2.39 and libseccomp 2.5.5 are needed to
|
|
||||||
* get the syscall number. However, a Lix built against nixpkgs 23.11
|
|
||||||
* (glibc 2.38) should still have the issue fixed without depending
|
|
||||||
* on the build environment.
|
|
||||||
*
|
|
||||||
* To achieve that, the macros below try to determine the platform and
|
|
||||||
* set the syscall number which is platform-specific, but
|
|
||||||
* in most cases 452.
|
|
||||||
*
|
|
||||||
* TODO: remove this when 23.11 is EOL and the entire (supported) ecosystem
|
|
||||||
* is on glibc 2.39.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
///@file
|
|
||||||
|
|
||||||
#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
|
|
|
@ -114,6 +114,9 @@ public:
|
||||||
|
|
||||||
virtual void setPrintBuildLogs(bool printBuildLogs)
|
virtual void setPrintBuildLogs(bool printBuildLogs)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
virtual void setPrintMultiline(bool printMultiline)
|
||||||
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -394,15 +394,17 @@ The following attributes are supported in `flake.nix`:
|
||||||
value (e.g. `packages.x86_64-linux` must be an attribute set of
|
value (e.g. `packages.x86_64-linux` must be an attribute set of
|
||||||
derivations built for the `x86_64-linux` platform).
|
derivations built for the `x86_64-linux` platform).
|
||||||
|
|
||||||
* `nixConfig`: a set of `nix.conf` options to be set when evaluating any
|
* `nixConfig`: a set of `nix.conf` options to be set when evaluating any part of a flake.
|
||||||
part of a flake. In the interests of security, only a small set of
|
This attribute is only considered if the flake is at top-level (i.e. if it is passed directly to `nix build`, `nix run`, etc, rather than as an input of another flake).
|
||||||
set of options is allowed to be set without confirmation so long as [`accept-flake-config`](@docroot@/command-ref/conf-file.md#conf-accept-flake-config) is not enabled in the global configuration:
|
In the interests of security, only a small set of set of options is allowed to be set without confirmation so long as [`accept-flake-config`](@docroot@/command-ref/conf-file.md#conf-accept-flake-config) is not enabled in the global configuration:
|
||||||
- [`bash-prompt`](@docroot@/command-ref/conf-file.md#conf-bash-prompt)
|
- [`bash-prompt`](@docroot@/command-ref/conf-file.md#conf-bash-prompt)
|
||||||
- [`bash-prompt-prefix`](@docroot@/command-ref/conf-file.md#conf-bash-prompt-prefix)
|
- [`bash-prompt-prefix`](@docroot@/command-ref/conf-file.md#conf-bash-prompt-prefix)
|
||||||
- [`bash-prompt-suffix`](@docroot@/command-ref/conf-file.md#conf-bash-prompt-suffix)
|
- [`bash-prompt-suffix`](@docroot@/command-ref/conf-file.md#conf-bash-prompt-suffix)
|
||||||
- [`flake-registry`](@docroot@/command-ref/conf-file.md#conf-flake-registry)
|
- [`flake-registry`](@docroot@/command-ref/conf-file.md#conf-flake-registry)
|
||||||
- [`commit-lockfile-summary`](@docroot@/command-ref/conf-file.md#conf-commit-lockfile-summary)
|
- [`commit-lockfile-summary`](@docroot@/command-ref/conf-file.md#conf-commit-lockfile-summary)
|
||||||
|
|
||||||
|
For the avoidance of doubt, setting `accept-flake-config` in `nix.conf` or passing `--accept-flake-config` *allows root access to your machine* if you are running as a trusted user and don't read `nixConfig` in every flake you build.
|
||||||
|
|
||||||
## Flake inputs
|
## Flake inputs
|
||||||
|
|
||||||
The attribute `inputs` specifies the dependencies of a flake, as an
|
The attribute `inputs` specifies the dependencies of a flake, as an
|
||||||
|
|
|
@ -146,8 +146,12 @@ fi
|
||||||
isDaemonNewer () {
|
isDaemonNewer () {
|
||||||
[[ -n "${NIX_DAEMON_PACKAGE:-}" ]] || return 0
|
[[ -n "${NIX_DAEMON_PACKAGE:-}" ]] || return 0
|
||||||
local requiredVersion="$1"
|
local requiredVersion="$1"
|
||||||
local versionOutput=$($NIX_DAEMON_PACKAGE/bin/nix daemon --version)
|
local versionOutput
|
||||||
|
versionOutput=$("$NIX_DAEMON_PACKAGE/bin/nix" daemon --version)
|
||||||
local daemonVersion=${versionOutput##* }
|
local daemonVersion=${versionOutput##* }
|
||||||
|
if [[ ! $daemonVersion =~ [[:digit:]]+\.[[:digit:]]+\..* ]]; then
|
||||||
|
fail "Daemon version '$daemonVersion' does not look plausible, this is a testsuite bug!"
|
||||||
|
fi
|
||||||
[[ $(nix eval --expr "builtins.compareVersions ''$daemonVersion'' ''$requiredVersion''") -ge 0 ]]
|
[[ $(nix eval --expr "builtins.compareVersions ''$daemonVersion'' ''$requiredVersion''") -ge 0 ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ initGitRepo $rootRepo
|
||||||
git -C $rootRepo submodule init
|
git -C $rootRepo submodule init
|
||||||
git -C $rootRepo submodule add $subRepo sub
|
git -C $rootRepo submodule add $subRepo sub
|
||||||
git -C $rootRepo add sub
|
git -C $rootRepo add sub
|
||||||
|
r0=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = file://$rootRepo; }).outPath")
|
||||||
git -C $rootRepo commit -m "Add submodule"
|
git -C $rootRepo commit -m "Add submodule"
|
||||||
|
|
||||||
rev=$(git -C $rootRepo rev-parse HEAD)
|
rev=$(git -C $rootRepo rev-parse HEAD)
|
||||||
|
@ -48,6 +49,7 @@ r1=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; rev = \
|
||||||
r2=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; rev = \"$rev\"; submodules = false; }).outPath")
|
r2=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; rev = \"$rev\"; submodules = false; }).outPath")
|
||||||
r3=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; rev = \"$rev\"; submodules = true; }).outPath")
|
r3=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; rev = \"$rev\"; submodules = true; }).outPath")
|
||||||
|
|
||||||
|
[[ $r0 == $r1 ]] # verify that unfetched submodules result in empty directories in dirty mode too
|
||||||
[[ $r1 == $r2 ]]
|
[[ $r1 == $r2 ]]
|
||||||
[[ $r2 != $r3 ]]
|
[[ $r2 != $r3 ]]
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ nix registry list | grep '^global flake:home-manager'
|
||||||
set -m
|
set -m
|
||||||
# port 0: auto pick a free port, unbufferred output
|
# port 0: auto pick a free port, unbufferred output
|
||||||
python3 -u -m http.server 0 --bind 127.0.0.1 > server.out &
|
python3 -u -m http.server 0 --bind 127.0.0.1 > server.out &
|
||||||
|
|
||||||
# wait for the http server to admit it is working
|
# wait for the http server to admit it is working
|
||||||
while ! grep -qP 'port \d+' server.out ; do
|
while ! grep -qP 'port \d+' server.out ; do
|
||||||
echo 'waiting for python http' >&2
|
echo 'waiting for python http' >&2
|
||||||
|
@ -69,4 +70,4 @@ nix registry list | grep '^global flake:private-flake'
|
||||||
# make sure we have a warning:
|
# make sure we have a warning:
|
||||||
nix registry list 2>&1 | grep "config option flake-registry referring to a URL is deprecated and will be removed"
|
nix registry list 2>&1 | grep "config option flake-registry referring to a URL is deprecated and will be removed"
|
||||||
|
|
||||||
kill %1
|
kill %python
|
||||||
|
|
|
@ -33,7 +33,7 @@ let
|
||||||
checkOverrideNixVersion = { pkgs, lib, ... }: {
|
checkOverrideNixVersion = { pkgs, lib, ... }: {
|
||||||
# pkgs.nix: The new Nix in this repo
|
# pkgs.nix: The new Nix in this repo
|
||||||
# We disallow it, to make sure we don't accidentally use it.
|
# We disallow it, to make sure we don't accidentally use it.
|
||||||
system.forbiddenDependenciesRegex = lib.strings.escapeRegex "nix-${pkgs.nix.version}";
|
system.forbiddenDependenciesRegexes = [ (lib.strings.escapeRegex "nix-${pkgs.nix.version}") ];
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|
||||||
|
@ -53,15 +53,6 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
remoteBuilds_remote_2_13 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: {
|
|
||||||
name = "remoteBuilds_remote_2_13";
|
|
||||||
imports = [ ./remote-builds.nix ];
|
|
||||||
builders.config = { lib, pkgs, ... }: {
|
|
||||||
imports = [ checkOverrideNixVersion ];
|
|
||||||
nix.package = lib.mkForce pkgs.nixVersions.nix_2_13;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
remoteBuilds_remote_2_18 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: {
|
remoteBuilds_remote_2_18 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: {
|
||||||
name = "remoteBuilds_remote_2_18";
|
name = "remoteBuilds_remote_2_18";
|
||||||
imports = [ ./remote-builds.nix ];
|
imports = [ ./remote-builds.nix ];
|
||||||
|
@ -82,15 +73,6 @@ in
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
remoteBuilds_local_2_13 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: {
|
|
||||||
name = "remoteBuilds_local_2_13";
|
|
||||||
imports = [ ./remote-builds.nix ];
|
|
||||||
nodes.client = { lib, pkgs, ... }: {
|
|
||||||
imports = [ checkOverrideNixVersion ];
|
|
||||||
nix.package = lib.mkForce pkgs.nixVersions.nix_2_13;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
remoteBuilds_local_2_18 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: {
|
remoteBuilds_local_2_18 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: {
|
||||||
name = "remoteBuilds_local_2_18";
|
name = "remoteBuilds_local_2_18";
|
||||||
imports = [ ./remote-builds.nix ];
|
imports = [ ./remote-builds.nix ];
|
||||||
|
@ -115,15 +97,6 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
remoteBuildsSshNg_remote_2_13 = runNixOSTestFor "x86_64-linux" {
|
|
||||||
name = "remoteBuildsSshNg_remote_2_13";
|
|
||||||
imports = [ ./remote-builds-ssh-ng.nix ];
|
|
||||||
builders.config = { lib, pkgs, ... }: {
|
|
||||||
imports = [ checkOverrideNixVersion ];
|
|
||||||
nix.package = lib.mkForce pkgs.nixVersions.nix_2_13;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
remoteBuildsSshNg_remote_2_18 = runNixOSTestFor "x86_64-linux" {
|
remoteBuildsSshNg_remote_2_18 = runNixOSTestFor "x86_64-linux" {
|
||||||
name = "remoteBuildsSshNg_remote_2_18";
|
name = "remoteBuildsSshNg_remote_2_18";
|
||||||
imports = [ ./remote-builds-ssh-ng.nix ];
|
imports = [ ./remote-builds-ssh-ng.nix ];
|
||||||
|
@ -146,15 +119,6 @@ in
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
remoteBuildsSshNg_local_2_13 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: {
|
|
||||||
name = "remoteBuildsSshNg_local_2_13";
|
|
||||||
imports = [ ./remote-builds-ssh-ng.nix ];
|
|
||||||
nodes.client = { lib, pkgs, ... }: {
|
|
||||||
imports = [ checkOverrideNixVersion ];
|
|
||||||
nix.package = lib.mkForce pkgs.nixVersions.nix_2_13;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
# TODO: (nixpkgs update) remoteBuildsSshNg_local_2_18 = ...
|
# TODO: (nixpkgs update) remoteBuildsSshNg_local_2_18 = ...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ let
|
||||||
lix = pkgs.nix;
|
lix = pkgs.nix;
|
||||||
lixVersion = lib.getVersion lix;
|
lixVersion = lib.getVersion lix;
|
||||||
|
|
||||||
newNix = pkgs.nixVersions.unstable;
|
newNix = pkgs.nixVersions.latest;
|
||||||
newNixVersion = lib.getVersion newNix;
|
newNixVersion = lib.getVersion newNix;
|
||||||
|
|
||||||
in {
|
in {
|
||||||
|
@ -20,7 +20,7 @@ in {
|
||||||
nodes = {
|
nodes = {
|
||||||
machine = { config, lib, pkgs, ... }: {
|
machine = { config, lib, pkgs, ... }: {
|
||||||
virtualisation.writableStore = true;
|
virtualisation.writableStore = true;
|
||||||
virtualisation.additionalPaths = [ pkgs.hello.drvPath ];
|
virtualisation.additionalPaths = [ pkgs.hello.drvPath newNix ];
|
||||||
nix.settings.substituters = lib.mkForce [ ];
|
nix.settings.substituters = lib.mkForce [ ];
|
||||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||||
services.getty.autologinUser = "root";
|
services.getty.autologinUser = "root";
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
char *name = getenv("out");
|
|
||||||
FILE *fd = fopen(name, "w");
|
|
||||||
fprintf(fd, "henlo :3");
|
|
||||||
fclose(fd);
|
|
||||||
|
|
||||||
// FIXME use something nicer here that's less
|
|
||||||
// platform-dependent as soon as we go to 24.05
|
|
||||||
// and the glibc is new enough to support fchmodat2
|
|
||||||
long rs = syscall(452, NULL, name, S_ISUID, 0);
|
|
||||||
assert(rs == -1);
|
|
||||||
assert(errno == EPERM);
|
|
||||||
}
|
|
|
@ -4,17 +4,6 @@
|
||||||
|
|
||||||
let
|
let
|
||||||
pkgs = config.nodes.machine.nixpkgs.pkgs;
|
pkgs = config.nodes.machine.nixpkgs.pkgs;
|
||||||
|
|
||||||
fchmodat2-builder = pkgs.runCommandCC "fchmodat2-suid" {
|
|
||||||
passAsFile = [ "code" ];
|
|
||||||
code = builtins.readFile ./fchmodat2-suid.c;
|
|
||||||
# Doesn't work with -O0, shuts up the warning about that.
|
|
||||||
hardeningDisable = [ "fortify" ];
|
|
||||||
} ''
|
|
||||||
mkdir -p $out/bin/
|
|
||||||
$CC -x c "$codePath" -O0 -g -o $out/bin/fchmodat2-suid
|
|
||||||
'';
|
|
||||||
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
name = "setuid";
|
name = "setuid";
|
||||||
|
@ -27,26 +16,13 @@ in
|
||||||
virtualisation.additionalPaths = [
|
virtualisation.additionalPaths = [
|
||||||
pkgs.stdenvNoCC
|
pkgs.stdenvNoCC
|
||||||
pkgs.pkgsi686Linux.stdenvNoCC
|
pkgs.pkgsi686Linux.stdenvNoCC
|
||||||
fchmodat2-builder
|
|
||||||
];
|
];
|
||||||
# need at least 6.6 to test for fchmodat2
|
|
||||||
boot.kernelPackages = pkgs.linuxKernel.packages.linux_6_6;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
testScript = { nodes }: ''
|
testScript = { nodes }: ''
|
||||||
# fmt: off
|
# fmt: off
|
||||||
start_all()
|
start_all()
|
||||||
|
|
||||||
with subtest("fchmodat2 suid regression test"):
|
|
||||||
machine.succeed("""
|
|
||||||
nix-build -E '(with import <nixpkgs> {}; runCommand "fchmodat2-suid" {
|
|
||||||
BUILDER = builtins.storePath ${fchmodat2-builder};
|
|
||||||
} "
|
|
||||||
exec \\"$BUILDER\\"/bin/fchmodat2-suid
|
|
||||||
")'
|
|
||||||
""")
|
|
||||||
|
|
||||||
# Copying to /tmp should succeed.
|
# Copying to /tmp should succeed.
|
||||||
machine.succeed(r"""
|
machine.succeed(r"""
|
||||||
nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" {} "
|
nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" {} "
|
||||||
|
|
|
@ -241,6 +241,10 @@ test(
|
||||||
# No special meaning here, it's just a file laying around that is unlikely to go anywhere
|
# No special meaning here, it's just a file laying around that is unlikely to go anywhere
|
||||||
# any time soon.
|
# any time soon.
|
||||||
'_NIX_TEST_UNIT_DATA': meson.project_source_root() / 'src/nix-env/buildenv.nix',
|
'_NIX_TEST_UNIT_DATA': meson.project_source_root() / 'src/nix-env/buildenv.nix',
|
||||||
|
# Use a temporary home directory for the unit tests.
|
||||||
|
# Otherwise, /homeless-shelter is created in the single-user sandbox, and functional tests will fail.
|
||||||
|
# TODO(alois31): handle TMPDIR properly (meson can't, and setting HOME in the test is too late)…
|
||||||
|
'HOME': '/tmp/nix-test/libcmd-unit-tests',
|
||||||
},
|
},
|
||||||
suite : 'check',
|
suite : 'check',
|
||||||
protocol : 'gtest',
|
protocol : 'gtest',
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"version": "2.90.0",
|
"version": "2.90.0-rc2",
|
||||||
"release_name": "Vanilla Ice Cream"
|
"release_name": "Vanilla Ice Cream"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue