The work moved to titan (macOS), where the gate failed for reasons older than M5a: sockets under macOS's long TMPDIR pass its 104-byte limit, gatewayd reads /proc/self for its uid, and /tmp is /private/tmp. Task 16 sets TMPDIR=/tmp for every process Cargo runs (.cargo/config.toml [env]), has gatewayd take its uid from the owner of a pipe it makes (no dependency, no unsafe), and hands over a toolkit shell.rs that compares resolved paths. On a scratch tree the gate passes on titan and on talos with 936 tests, and a wrong uid fails 7 gatewayd tests. Tasks 16 to 19 are renumbered 17 to 20. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
113 lines
4.4 KiB
Markdown
113 lines
4.4 KiB
Markdown
# M5a task 16: the gate passes on macOS as well as on Linux
|
|
|
|
**Branch:** `m5a` (run `git switch m5a`; `git status --short` must be empty, otherwise stop)
|
|
**Commit subject:** `gate: pass on macOS: tests use /tmp, gatewayd finds its uid without /proc`
|
|
|
|
## Goal
|
|
|
|
Work moved to titan, a Mac, on 2026-09-24. There `make gate` fails although the code is right,
|
|
because three places assume Linux. The harness itself still runs on straylight (Linux); this
|
|
task only makes the gate pass on both. No behaviour change on Linux, and no new test.
|
|
|
|
1. **Unix socket paths are too long.** Tests put sockets under `std::env::temp_dir()`. On macOS
|
|
that is `/var/folders/…/T/`, about 50 bytes, and a socket's whole path may be at most 104
|
|
bytes there, so some tests' daemons cannot bind (for example brokerd's
|
|
`serve_runner.rs` and gatewayd's `serve_restart.rs`). A new `.cargo/config.toml` sets
|
|
`TMPDIR=/tmp` for every process Cargo runs, tests included.
|
|
2. **`gatewayd` reads `/proc/self`** for the user id it runs as, when it checks a secret file's
|
|
owner. macOS has no `/proc`. `std` has no call for the user id, and `unsafe` (for `libc`) is
|
|
not allowed, so a new function `user_id` takes the owner of a pipe the process has just made,
|
|
which is the user the process runs as on Linux and on macOS (checked on both: 1000 on talos,
|
|
501 on titan, the same as `id -u`).
|
|
3. **`toolkit`'s `shell.rs` test** expects `pwd` to print `/tmp`; on macOS `/tmp` is a link to
|
|
`/private/tmp`, and `pwd` prints the resolved path. A new version of that given test compares
|
|
with the resolved path.
|
|
|
|
## Files
|
|
|
|
- Create: `.cargo/config.toml` (at the repository root, next to `Cargo.toml`)
|
|
- Copy: a **new version** of the given `crates/toolkit/tests/shell.rs`, from
|
|
`docs/plans/M5a/files/crates/toolkit/tests/shell.rs`
|
|
- Modify: `crates/gatewayd/src/secrets.rs`, `docs/implementer-log.md`
|
|
|
|
## The changes, exactly
|
|
|
|
**1. Create `.cargo/config.toml`** with exactly this content:
|
|
|
|
```toml
|
|
# Tests put Unix sockets under `std::env::temp_dir()`. macOS's is long (/var/folders/…/T/), and a
|
|
# socket's path there can pass the 104 bytes macOS allows, so every process Cargo runs, tests
|
|
# included, uses /tmp.
|
|
[env]
|
|
TMPDIR = { value = "/tmp", force = true }
|
|
```
|
|
|
|
**2. `crates/gatewayd/src/secrets.rs`**, in `read_checked`. Nothing else in the file changes.
|
|
|
|
**a.** In the comment, the line
|
|
|
|
```rust
|
|
// on `named`): the uid of "/proc/self"; "<path> is not owned by the user gatewayd runs as";
|
|
```
|
|
|
|
becomes
|
|
|
|
```rust
|
|
// on `named`): the uid of `user_id()`; "<path> is not owned by the user gatewayd runs as";
|
|
```
|
|
|
|
**b.** The three lines
|
|
|
|
```rust
|
|
let owner = std::fs::metadata("/proc/self")
|
|
.map_err(|e| format!("cannot read /proc/self: {}", e))?
|
|
.uid();
|
|
```
|
|
|
|
become the one line
|
|
|
|
```rust
|
|
let owner = user_id()?;
|
|
```
|
|
|
|
**c.** Insert this function immediately above the line
|
|
`/// The text without one trailing newline; not empty; UTF-8.` (the doc comment of `value`), with
|
|
one blank line between them:
|
|
|
|
```rust
|
|
/// The user id this process runs as: the owner of a pipe it has just made. `std` has no call for
|
|
/// it, and `/proc/self` is Linux only.
|
|
fn user_id() -> Result<u32, String> {
|
|
let (reader, _writer) = std::io::pipe().map_err(|e| format!("cannot make a pipe: {e}"))?;
|
|
let pipe = std::fs::File::from(std::os::fd::OwnedFd::from(reader));
|
|
let meta = pipe
|
|
.metadata()
|
|
.map_err(|e| format!("cannot read a pipe: {e}"))?;
|
|
Ok(meta.uid())
|
|
}
|
|
```
|
|
|
|
No `use` line changes: `MetadataExt` (for `.uid()`) is already imported, and the rest is named in
|
|
full.
|
|
|
|
## Steps
|
|
|
|
- [ ] **1. Create `.cargo/config.toml`**, then copy the test:
|
|
`cp docs/plans/M5a/files/crates/toolkit/tests/shell.rs crates/toolkit/tests/`
|
|
- [ ] **2. Make the three edits to `secrets.rs`.** Run `cargo fmt --all`.
|
|
- [ ] **3. Run the tests.** `cargo test -p gatewayd -p toolkit -p brokerd`: every suite passes.
|
|
- [ ] **4. Run the gate.** `make gate`. Expected last line: `gate: ok`, with 936 tests, the same
|
|
as before this task. On macOS it failed before this task, with 8 tests failing; now it passes.
|
|
- [ ] **5. Log and commit.**
|
|
`git add .cargo/config.toml crates/gatewayd crates/toolkit docs/implementer-log.md && git commit`
|
|
|
|
## Done when
|
|
|
|
- `make gate` prints `gate: ok` with 936 tests.
|
|
|
|
## Stop and report if
|
|
|
|
- Any line quoted above is not in the file exactly as shown.
|
|
- `cargo-deny` is missing (`error: no such command: deny`): report it, do not install anything.
|
|
- Any test fails after the changes. Do not change a test.
|