Add kwin full source tree, greeter login, zsh, pcid service, and build system improvements
This commit is contained in:
@@ -0,0 +1,306 @@
|
||||
# Red Bear OS Greeter/Login System — Comprehensive Analysis
|
||||
|
||||
**Generated:** 2026-04-26
|
||||
**Based on:** Source code analysis of `redbear-authd`, `redbear-greeter`, `redbear-sessiond`, `redbear-session-launch`, `redbear-login-protocol`, init service configuration, and the GREETER-LOGIN-IMPLEMENTATION-PLAN.md.
|
||||
|
||||
---
|
||||
|
||||
## 1. System Architecture
|
||||
|
||||
### 1.1 Component Topology
|
||||
|
||||
```
|
||||
Qt6/QML Login Surface (redbear-greeter-ui, VT3)
|
||||
│ Unix socket /run/redbear-greeterd.sock (JSON, line-delimited)
|
||||
↓
|
||||
redbear-greeterd (orchestrator daemon, root-owned, VT3)
|
||||
│ Unix socket /run/redbear-authd.sock (AuthRequest/AuthResponse JSON)
|
||||
↓
|
||||
redbear-authd (privileged auth daemon, /etc/shadow verification)
|
||||
│ spawns via Command::
|
||||
↓
|
||||
redbear-session-launch (uid/gid drop + env bootstrap)
|
||||
│ exec's
|
||||
↓
|
||||
dbus-run-session -- redbear-kde-session → kwin_wayland_wrapper --drm + plasmashell
|
||||
|
||||
(redbear-sessiond on system D-Bus → org.freedesktop.login1 for KWin device access)
|
||||
```
|
||||
|
||||
**Key socket paths:**
|
||||
| Socket | Owner | Mode | Purpose |
|
||||
|--------|-------|------|---------|
|
||||
| `/run/redbear-authd.sock` | root | 0o600 | greeterd → authd |
|
||||
| `/run/redbear-greeterd.sock` | greeter user | 0o660 | greeter-ui → greeterd |
|
||||
| `/run/redbear-sessiond-control.sock` | root | 0o600 | authd → sessiond (JSON SessiondUpdate) |
|
||||
| `/run/seatd.sock` | root | 0o666 | seatd abstract namespace |
|
||||
|
||||
---
|
||||
|
||||
## 2. Password Verification (authd)
|
||||
|
||||
**Source:** `local/recipes/system/redbear-authd/source/src/main.rs` lines 101–214
|
||||
|
||||
**Storage:** Reads `/etc/passwd` (user/uid/gid/home/shell) and `/etc/shadow` (password hash).
|
||||
|
||||
**Format detection:** Both Redox-style (`;`-delimited) and Unix-style (`:`-delimited) passwd/shadow/group entries are auto-detected per-line (line 88–99 in authd main.rs).
|
||||
|
||||
**Hash verification (lines 183–193):**
|
||||
```rust
|
||||
fn verify_shadow_password(password: &str, shadow_hash: &str) -> Result<bool, VerifyError> {
|
||||
if shadow_hash.starts_with("$6$") || shadow_hash.starts_with("$5$") {
|
||||
// SHA-512 or SHA-256 crypt (sha-crypt crate, pure Rust)
|
||||
return Ok(ShaCrypt::default().verify_password(password.as_bytes(), shadow_hash).is_ok());
|
||||
}
|
||||
if shadow_hash.starts_with("$argon2") {
|
||||
// Argon2id (rust-argon2 crate)
|
||||
return Ok(verify_encoded(shadow_hash, password.as_bytes()).unwrap_or(false));
|
||||
}
|
||||
Err(VerifyError::UnsupportedHashFormat)
|
||||
}
|
||||
```
|
||||
|
||||
**Plain-text fallback:** Non-`$` hash strings are compared directly (line 213). Used for unshadowed entries.
|
||||
|
||||
**Lockout policy (lines 237–270):**
|
||||
- 5 failures in 60s → 30-second lockout
|
||||
- Rejects locked accounts (`!` or `*` prefix)
|
||||
- UID < 1000 rejected (except UID 0)
|
||||
|
||||
**Approval system (lines 216–287):**
|
||||
- Successful auth stores 15-second in-memory approval keyed to `username + VT`
|
||||
- Session start requires valid (non-expired, VT-matched) approval ticket
|
||||
|
||||
---
|
||||
|
||||
## 3. Communication: UI ↔ greeterd ↔ authd
|
||||
|
||||
**Protocol:** `redbear-login-protocol` crate (`local/recipes/system/redbear-login-protocol/source/src/lib.rs`)
|
||||
|
||||
```rust
|
||||
// greeterd → authd
|
||||
AuthRequest::Authenticate { request_id, username, password, vt }
|
||||
AuthRequest::StartSession { request_id, username, session: "kde-wayland", vt }
|
||||
AuthRequest::PowerAction { request_id, action: "shutdown"|"reboot" }
|
||||
|
||||
// authd → greeterd
|
||||
AuthResponse::AuthenticateResult { request_id, ok, message }
|
||||
AuthResponse::SessionResult { request_id, ok, exit_code, message }
|
||||
AuthResponse::PowerResult { request_id, ok, message }
|
||||
AuthResponse::Error { request_id, message }
|
||||
```
|
||||
|
||||
```rust
|
||||
// UI → greeterd
|
||||
GreeterRequest::SubmitLogin { username, password }
|
||||
|
||||
// greeterd → UI
|
||||
GreeterResponse::LoginResult { ok, state, message }
|
||||
GreeterResponse::ActionResult { ok, message }
|
||||
```
|
||||
|
||||
**greeterd state machine:**
|
||||
```
|
||||
Starting → GreeterReady → Authenticating → LaunchingSession → SessionRunning
|
||||
↓
|
||||
ReturningToGreeter → GreeterReady
|
||||
↓
|
||||
FatalError (after 3 restarts/60s)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Session Launch
|
||||
|
||||
**Source:** `local/recipes/system/redbear-session-launch/source/src/main.rs` lines 352–385
|
||||
|
||||
1. Reads `/etc/passwd` + `/etc/group` for uid/gid/groups
|
||||
2. Creates `XDG_RUNTIME_DIR` (`/run/user/$UID` or `/tmp/run/user/$UID`), chown 0700
|
||||
3. Builds clean env: `HOME`, `USER`, `LOGNAME`, `SHELL`, `PATH=/usr/bin:/bin`, `XDG_RUNTIME_DIR`, `WAYLAND_DISPLAY=wayland-0`, `XDG_SEAT=seat0`, `XDG_VTNR`, `LIBSEAT_BACKEND=seatd`, `SEATD_SOCK=/run/seatd.sock`, `XDG_SESSION_TYPE=wayland`, `XDG_CURRENT_DESKTOP=KDE`, `KDE_FULL_SESSION=true`, `XDG_SESSION_ID=c1`
|
||||
4. `env_clear()` → setuid + setgid + setgroups
|
||||
5. `exec /usr/bin/dbus-run-session -- /usr/bin/redbear-kde-session`
|
||||
6. Fallback: direct `redbear-kde-session` if `dbus-run-session` absent
|
||||
|
||||
**redbear-kde-session** (from `docs/05-KDE-PLASMA-ON-REDOX.md`):
|
||||
```bash
|
||||
export WAYLAND_DISPLAY=wayland-0
|
||||
export XDG_RUNTIME_DIR=/tmp/run/user/0
|
||||
dbus-daemon --system &
|
||||
eval $(dbus-launch --sh-syntax)
|
||||
kwin_wayland_wrapper --drm &
|
||||
sleep 2 && plasmashell &
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Init Service Wiring
|
||||
|
||||
**From `config/redbear-full.toml`:**
|
||||
|
||||
```
|
||||
Service order:
|
||||
12_dbus.service (system D-Bus)
|
||||
13_redbear-sessiond.service (org.freedesktop.login1 broker)
|
||||
13_seatd.service (seat management)
|
||||
19_redbear-authd.service (auth daemon, /usr/bin/redbear-authd)
|
||||
20_greeter.service (greeterd, /usr/bin/redbear-greeterd, VT=3)
|
||||
29_activate_console.service (inputd -A 2 → VT2 fallback)
|
||||
30_console.service (getty 2, respawn)
|
||||
31_debug_console.service (getty debug, respawn)
|
||||
```
|
||||
|
||||
`20_greeter.service`:
|
||||
```toml
|
||||
cmd = "/usr/bin/redbear-greeterd"
|
||||
envs = { VT = "3", REDBEAR_GREETER_USER = "greeter" }
|
||||
type = "oneshot_async"
|
||||
```
|
||||
|
||||
**Greeter user account** (redbear-full.toml):
|
||||
```toml
|
||||
[users.greeter]
|
||||
password = ""
|
||||
uid = 101
|
||||
gid = 101
|
||||
home = "/nonexistent"
|
||||
shell = "/usr/bin/ion"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. D-Bus Integration
|
||||
|
||||
**redbear-sessiond** — `org.freedesktop.login1` on **system D-Bus** via `zbus`:
|
||||
- `Manager.ListSessions`, `Manager.GetSeat`, `PrepareForShutdown` signal
|
||||
- `Seat.SwitchTo(vt)` → `inputd -A <vt>`
|
||||
- `Session.TakeDevice`/`ReleaseDevice` → DRM/input device fd passing
|
||||
- `Session.TakeControl`/`ReleaseControl`
|
||||
- Service file: `/usr/share/dbus-1/system-services/org.freedesktop.login1.service`
|
||||
|
||||
**authd and greeterd are NOT D-Bus activated** — started directly by init services.
|
||||
|
||||
**greeter compositor** starts a **session D-Bus** via `dbus-launch`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Quality and Robustness Assessment
|
||||
|
||||
### 7.1 Strengths
|
||||
|
||||
| Area | Assessment | Detail |
|
||||
|------|------------|--------|
|
||||
| **Hash algorithm** | ✅ Excellent | SHA-512 (`$6$`), SHA-256 (`$5$`), Argon2id — all pure-Rust crates, no MD5/DES |
|
||||
| **Constant-time comparison** | ✅ Good | `sha-crypt::verify_password` and `argon2::verify_encoded` are constant-time by design |
|
||||
| **Approval windowing** | ✅ Good | 15s approval between auth and session start, VT-bound |
|
||||
| **Lockout policy** | ✅ Good | 5 attempts / 60s → 30s lockout |
|
||||
| **Socket permissions** | ✅ Good | authd socket = 0o600, greeterd socket = 0o660 |
|
||||
| **UID restriction** | ✅ Good | UID < 1000 (non-root) disallowed |
|
||||
| **Restart bounding** | ✅ Good | 3 restarts/60s → FatalError, fallback consoles preserved |
|
||||
| **Protocol type safety** | ✅ Good | `redbear-login-protocol` crate is single source of truth for all JSON types |
|
||||
| **Plain-text fallback** | ⚠️ Acceptable | Non-`$` hash strings compared directly — OK for initial dev users |
|
||||
| **Fail-closed on unknown hash** | ✅ Good | `UnsupportedHashFormat` → login rejected, not bypassed |
|
||||
| **Greeter isolates UI crash** | ✅ Good | compositor survives UI crash; respawns UI only |
|
||||
|
||||
### 7.2 Weaknesses and Risks
|
||||
|
||||
| # | Issue | Severity | Location | Impact |
|
||||
|---|-------|----------|-----------|--------|
|
||||
| W1 | **No PAM integration** | Medium | authd is custom narrow auth | Limits enterprise use, no pluggable auth modules |
|
||||
| W2 | **Approval in-memory only** | Medium | authd `HashMap` | authd crash → approvals lost; session start fails after crash |
|
||||
| W3 | **No password quality enforcement** | Low | authd only checks lockout | Weak passwords accepted (acceptable for Phase 2) |
|
||||
| W4 | **Hardcoded `kde-wayland` session** | Low | authd line 301, session-launch line 335 | No session chooser, no alternative desktops |
|
||||
| W5 | **greeterd not respawned by init** | Medium | `20_greeter.service` type=oneshot_async | If greeterd crashes, system stuck at console (no auto-recovery) |
|
||||
| W6 | **No seatd watchdog** | Medium | seatd service has no internal restart | seatd crash → compositor immediately fails |
|
||||
| W7 | **Static device_map.rs** | Medium | major/minor hardcoded table | Non-static hardware (USB GPUs, etc.) not discovered |
|
||||
| W8 | **No session tracking via D-Bus** | Low | authd → sessiond via raw JSON socket | `SetSession`/`ResetSession` bypass login1 surface |
|
||||
| W9 | **Power action fallbacks missing** | Low | authd calls `/usr/bin/shutdown`, `/usr/bin/reboot` | May not exist on Redox; failure is silent |
|
||||
| W10 | **greeterd socket path hardcoded** | Low | `/run/redbear-greeterd.sock` vs XDG_RUNTIME_DIR | Works for single-seat; breaks in multi-seat |
|
||||
| W11 | **greeter init service is `true` stub** | **Critical** | `redbear-greeter-services.toml` → `20_greeter.service cmd = "true"` | Real greeter only in `redbear-full.toml`; mini/grub don't have it |
|
||||
| W12 | **redbear-greeter-compositor missing from image** | **Critical** | recipe.toml installs to `/usr/bin/` but path referenced as `COMPOSITOR_BIN_PATH` | greeterd fails to start compositor |
|
||||
| W13 | **`dbus-run-session` may not exist in image** | **Critical** | session-launch fallback is direct `redbear-kde-session` | D-Bus session bus may not start without explicit dbus-daemon |
|
||||
|
||||
### 7.3 Critical Blockers for Greeter Reaching Login Screen
|
||||
|
||||
| Blocker | Source | Fix |
|
||||
|---------|--------|-----|
|
||||
| greeter init service stub in greeter-services.toml | `20_greeter.service cmd = "true"` | Use `redbear-full.toml` service definition (already correct there) |
|
||||
| compositor binary path mismatch | `COMPOSITOR_BIN_PATH = /usr/bin/redbear-greeter-compositor` but recipe installs to `/usr/share/` first then `/usr/bin/` | Verify binary at `/usr/bin/redbear-greeter-compositor` in image |
|
||||
| seatd not in image | seatd recipe may not have been cooked | Ensure `seatd` package is in config and cooked |
|
||||
| redbear-authd not in image | authd recipe may not have been cooked | Ensure `redbear-authd` package is in config and cooked |
|
||||
| redbear-sessiond not in image | sessiond recipe may not have been cooked | Ensure `redbear-sessiond` package is in config and cooked |
|
||||
| greeter user account missing | TOML `[users.greeter]` may not be applied | Verify greeter user uid=101 exists in /etc/passwd in image |
|
||||
| compositor requires DRM but QEMU has none | `kwin_wayland_wrapper --drm` fails in VM | Use `--virtual` in VM; compositor script already handles this |
|
||||
|
||||
---
|
||||
|
||||
## 8. File Path Reference
|
||||
|
||||
| Artifact | Path |
|
||||
|---|---|
|
||||
| authd binary | `/usr/bin/redbear-authd` |
|
||||
| authd socket | `/run/redbear-authd.sock` |
|
||||
| greeterd socket | `/run/redbear-greeterd.sock` |
|
||||
| greeterd binary | `/usr/bin/redbear-greeterd` |
|
||||
| greeter-ui binary | `/usr/bin/redbear-greeter-ui` |
|
||||
| compositor script | `/usr/bin/redbear-greeter-compositor` |
|
||||
| compositor (share) | `/usr/share/redbear/greeter/redbear-greeter-compositor` |
|
||||
| session-launch binary | `/usr/bin/redbear-session-launch` |
|
||||
| sessiond binary | `/usr/bin/redbear-sessiond` |
|
||||
| greeterd init service | `/usr/lib/init.d/20_greeter.service` |
|
||||
| authd init service | `/usr/lib/init.d/19_redbear-authd.service` |
|
||||
| sessiond init service | `/usr/lib/init.d/13_redbear-sessiond.service` |
|
||||
| seatd init service | `/usr/lib/init.d/13_seatd.service` |
|
||||
| greeter background | `/usr/share/redbear/greeter/background.png` |
|
||||
| greeter icon | `/usr/share/redbear/greeter/icon.png` |
|
||||
| sessiond control socket | `/run/redbear-sessiond-control.sock` |
|
||||
| seatd socket | `/run/seatd.sock` |
|
||||
| passwd file | `/etc/passwd` (redox `;` or unix `:` delimited) |
|
||||
| shadow file | `/etc/shadow` |
|
||||
| group file | `/etc/group` |
|
||||
| greeter user account | uid=101, gid=101 in /etc/passwd |
|
||||
|
||||
---
|
||||
|
||||
## 9. Improvement Recommendations (Priority Order)
|
||||
|
||||
### P0 — Make Greeter Actually Reach Login Screen
|
||||
|
||||
1. **Fix greeter init service**: Ensure `20_greeter.service` in `redbear-full.toml` (not the stub in greeter-services.toml) is the canonical one. greeter-services.toml is a bounded proof fragment; the real service lives in redbear-full.toml.
|
||||
2. **Verify all 5 greeter packages are in redbear-full.toml**: `seatd`, `redbear-authd`, `redbear-sessiond`, `redbear-session-launch`, `redbear-greeter`
|
||||
3. **Verify compositor binary at `/usr/bin/redbear-greeter-compositor`** in the built image
|
||||
4. **Verify greeter user (uid=101) exists** in /etc/passwd in image
|
||||
5. **Add compositor fallback** to `--virtual` when `--drm` fails (script already does this)
|
||||
|
||||
### P1 — Hardening
|
||||
|
||||
6. **Add respawn to greeterd init service**: `type = "oneshot_async", respawn = true` — greeterd crash shouldn't leave system at console
|
||||
7. **Add seatd respawn**: same logic
|
||||
8. **Fix redbear-sessiond `Seat::SwitchTo`** to return error rather than silently ignore failures
|
||||
9. **Add watchdog for greeterd** — if greeterd crashes, init should restart it
|
||||
|
||||
### P2 — Security Hardening
|
||||
|
||||
10. **Add password quality enforcement**: minimum length, entropy check before accepting
|
||||
11. **Rate-limit by source IP/VT**: prevent VT-based brute force
|
||||
12. **Add audit log for auth failures**: log to syslog or dedicated auth log
|
||||
13. **Add session listing via control socket**: currently authd writes `SetSession`/`ResetSession` but there's no readback mechanism
|
||||
|
||||
### P3 — Architectural
|
||||
|
||||
14. **Implement `TakeDevice`/`ReleaseDevice` fully**: current session.rs has the skeleton but device fd passing needs verification
|
||||
15. **Dynamic device enumeration**: replace static device_map.rs with udev-shim runtime queries
|
||||
16. **Add greeter watchdog daemon**: separate from greeterd, monitors and restarts it
|
||||
17. **D-Bus activate greeterd and authd**: remove init service startup dependency, use D-Bus activation instead
|
||||
18. **Add power action binaries**: create `/usr/bin/shutdown` and `/usr/bin/reboot` symlinks or wrappers that call init system
|
||||
19. **Implement `PrepareForShutdown`/`PrepareForSleep` signals**: for session cleanup on system power events
|
||||
|
||||
### P4 — Future
|
||||
|
||||
20. **Add PAM integration** via a minimal PAM-like module system in authd
|
||||
21. **Add session chooser** (console vs kde-wayland) via greeter UI
|
||||
22. **Multi-seat support**: XDG_RUNTIME_DIR per seat, greeterd socket per seat
|
||||
23. **Fingerprint/webauthn support**: extend authd protocol + greeter UI
|
||||
|
||||
---
|
||||
|
||||
*End of Analysis*
|
||||
@@ -0,0 +1,100 @@
|
||||
# Red Bear OS Patch Governance
|
||||
|
||||
## Purpose
|
||||
|
||||
This document prevents loss of implemented work. It establishes rules that AI agents
|
||||
and human contributors must follow when modifying patches, recipes, or build configs.
|
||||
|
||||
## Incident: 2026-04-26 Driver Code Loss
|
||||
|
||||
A previous agent session removed 8 patches and 9 BINS entries from
|
||||
`recipes/core/base/recipe.toml` to make the build succeed, instead of fixing
|
||||
patch conflicts. This deleted GPIO/I2C/UCSI driver source code that took a full
|
||||
day to implement (commits `dc3f1f996`, `3054adc5d`).
|
||||
|
||||
The code was recovered from git history, but this must never happen again.
|
||||
|
||||
## Rules
|
||||
|
||||
### 1. Never remove patches to fix build failures
|
||||
|
||||
When a patch fails to apply:
|
||||
|
||||
- **Rebase the patch** against the current cumulative state
|
||||
- **Fix the context lines** so the hunk applies cleanly
|
||||
- **Split the patch** if only some hunks fail (keep the working hunks)
|
||||
- **Document** the failure reason in the patch file header
|
||||
|
||||
Do NOT remove the patch from the recipe.toml patches list without explicit
|
||||
user approval. If a patch must be temporarily disabled, comment it with a TODO
|
||||
explaining why and what needs to be fixed.
|
||||
|
||||
### 2. Never remove BINS entries to fix build failures
|
||||
|
||||
When a driver binary fails to compile:
|
||||
|
||||
- **Fix the compilation error** in the driver source
|
||||
- **Add the driver to EXISTING_BINS** filter if source is incomplete
|
||||
- **Document** the failure
|
||||
|
||||
Do NOT remove the driver from the BINS array without explicit user approval.
|
||||
|
||||
### 3. Patch ordering matters
|
||||
|
||||
Patches in `recipes/core/base/recipe.toml` must be applied in the listed order.
|
||||
Some patches have interdependencies:
|
||||
|
||||
- `P2-acpi-i2c-resources.patch` must apply before `P2-daemon-hardening.patch`
|
||||
(workspace entries reference source files created by the former)
|
||||
- `P2-boot-runtime-fixes.patch` modifies hwd/acpi.rs (must apply cleanly to upstream)
|
||||
- `P2-init-acpid-wiring.patch` adds 41_acpid.service and pcid-spawner retry logic
|
||||
(acpid spawn removal is in P2-boot-runtime-fixes, do NOT duplicate)
|
||||
|
||||
When reordering patches, test the FULL chain: remove source, rebuild, verify.
|
||||
|
||||
### 4. Recipe.toml is tracked, source trees are not
|
||||
|
||||
`recipes/core/base/recipe.toml` is git-tracked. Changes to it are durable.
|
||||
`recipes/core/base/source/` is a fetched working copy — destroyed by `make clean`,
|
||||
`make distclean`, source refresh, and sync-upstream.
|
||||
|
||||
Any change to source/ MUST be preserved as a patch in `local/patches/base/`.
|
||||
|
||||
### 5. Before removing anything, check git history
|
||||
|
||||
```bash
|
||||
git log --oneline --all -- <file>
|
||||
```
|
||||
|
||||
If a previous commit added substantial work (driver implementations, features),
|
||||
the removal MUST be approved by the user. Agent sessions MUST NOT delete
|
||||
implemented work to bypass build failures.
|
||||
|
||||
### 6. Build validation after patch changes
|
||||
|
||||
After ANY change to the patches list or patch files:
|
||||
|
||||
1. Remove the source tree: `rm -rf recipes/core/base/source`
|
||||
2. Full rebuild: `REDBEAR_ALLOW_PROTECTED_FETCH=1 CI=1 make r.base`
|
||||
3. Verify NO "FAILED" or "rejects" in output
|
||||
4. Verify all expected binaries in stage: `ls stage/usr/bin/ stage/usr/lib/drivers/`
|
||||
5. Full image build: `CI=1 make all CONFIG_NAME=redbear-full`
|
||||
|
||||
## Known Issues
|
||||
|
||||
| Patch | Status | Notes |
|
||||
|-------|--------|-------|
|
||||
| P2-acpid-core-refactor.patch | Needs rebasing | 13/15 hunks fail on acpid/scheme.rs; removed from recipe.toml with TODO |
|
||||
| P2-acpi-i2c-resources.patch | Stripped | Removed stale acpid hunks that duplicated P2-boot-runtime-fixes; driver source hunks intact |
|
||||
| P2-boot-runtime-fixes.patch | Needs rebasing | Context lines from monolith split are stale; hwd/acpi.rs hunk fails on clean upstream |
|
||||
| P2-init-acpid-wiring.patch | Deduplicated | Removed acpi.rs hunk that duplicated P2-boot-runtime-fixes |
|
||||
|
||||
## Recipe.toml Fix Log
|
||||
|
||||
| Date | Change | Why |
|
||||
|------|--------|-----|
|
||||
| 2026-04-26 | Restored 8 removed patches | Agent deleted them to bypass conflicts; restored all from git HEAD |
|
||||
| 2026-04-26 | Restored 9 BINS entries | Agent deleted i2cd, gpiod, ucsid, etc. to bypass missing sources |
|
||||
| 2026-04-26 | Added EXISTING_BINS grep loop | Gracefully handles missing driver source instead of build failure |
|
||||
| 2026-04-26 | Fixed grep/find variables | `${GREP}` and `${FIND}` are unset in redoxer env; use bare `grep`/`find` |
|
||||
| 2026-04-26 | Fixed TOML escaping | `\"` in TOML triple-quotes becomes `"` in bash; use `\\\"` for literal `"` |
|
||||
@@ -0,0 +1,320 @@
|
||||
# Zsh Porting Plan for Red Bear OS
|
||||
|
||||
**Status:** ✅ FULLY IMPLEMENTED — Production recipe builds, configs updated, WIP removed
|
||||
**Target:** zsh 5.9 (upstream stable tag `zsh-5.9`)
|
||||
**Recipe:** `recipes/shells/zsh/`
|
||||
**Build Result:** `cook zsh - successful` (CI=1, non-interactive)
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
Zsh 5.9 has been successfully ported to Red Bear OS. The build produces a working `zsh` binary for `x86_64-unknown-redox` with:
|
||||
|
||||
- Full interactive shell support (ZLE line editor)
|
||||
- Completion system (`zsh/complete` built-in)
|
||||
- Parameter module (`zsh/parameter` built-in)
|
||||
- History and prompt expansion
|
||||
- Job control primitives (`setpgid`, `tcsetpgrp`)
|
||||
- Multibyte / UTF-8 support (`--enable-multibyte`)
|
||||
- System `malloc` (no custom allocator)
|
||||
- Static modules (no dynamic `.so` loading)
|
||||
- Manjaro-style system-wide configuration (`/etc/zsh/`, `/etc/skel/`)
|
||||
|
||||
The port required **one source patch** (`redox.patch`, ~150 lines) plus a deterministic `signames.c` generation step in the build script to work around cross-compilation limitations.
|
||||
|
||||
---
|
||||
|
||||
## 2. What Was Done
|
||||
|
||||
### 2.1 Recipe Created
|
||||
|
||||
**Location:** `recipes/shells/zsh/`
|
||||
|
||||
```
|
||||
recipes/shells/zsh/
|
||||
├── recipe.toml # Production recipe (custom template)
|
||||
├── redox.patch # Redox-specific source patches
|
||||
├── README.md # Redox-specific build and usage notes
|
||||
└── etc/ # Manjaro-style system-wide config files
|
||||
├── zsh/
|
||||
│ ├── zshenv
|
||||
│ ├── zprofile
|
||||
│ └── zshrc
|
||||
└── skel/
|
||||
├── .zprofile
|
||||
└── .zshrc
|
||||
```
|
||||
|
||||
### 2.2 Source
|
||||
|
||||
- **URL:** `https://github.com/zsh-users/zsh/archive/refs/tags/zsh-5.9.tar.gz`
|
||||
- **BLAKE3:** `a15b94fae03e87aba6fc6a27df3c98e610b85b0c7c0fc90248f07fdcb8816860`
|
||||
- **Patches applied:** `redox.patch`
|
||||
|
||||
### 2.3 Build Configuration
|
||||
|
||||
The recipe uses the `custom` template with explicit configure flags:
|
||||
|
||||
```bash
|
||||
COOKBOOK_CONFIGURE_FLAGS+=(
|
||||
--disable-gdbm
|
||||
--disable-pcre
|
||||
--disable-cap
|
||||
zsh_cv_sys_elf=no
|
||||
)
|
||||
```
|
||||
|
||||
**Rationale:**
|
||||
- `--disable-gdbm` — No gdbm package in base system.
|
||||
- `--disable-pcre` — PCRE library not wired as dependency for initial build; can be re-enabled later.
|
||||
- `--disable-cap` — No libcap (Linux capabilities).
|
||||
- `zsh_cv_sys_elf=no` — Redox does not use ELF-style shared library versioning.
|
||||
|
||||
**Signames workaround:** The cross-compilation environment cannot run the `signames1.awk` → `cpp` → `signames2.awk` pipeline natively. The build script pre-generates `signames.c` and `sigcount.h` deterministically using the host `gawk` and cross-compiler.
|
||||
|
||||
### 2.4 Patch Summary (`redox.patch`)
|
||||
|
||||
| File | Change | Reason |
|
||||
|------|--------|--------|
|
||||
| `configure.ac` | Cache `ac_cv_func_times=no` | `times()` missing in relibc |
|
||||
| `configure.ac` | Cache `ac_cv_func_setpgrp=no` | BSD `setpgrp()` missing; zsh falls back to `setpgid` |
|
||||
| `configure.ac` | Cache `ac_cv_func_killpg=no` | `killpg()` missing; zsh defines `kill(-pgrp,sig)` fallback |
|
||||
| `configure.ac` | Cache `ac_cv_func_initgroups=no` | Not available in relibc |
|
||||
| `configure.ac` | Cache `ac_cv_func_pathconf=no` | Not available in relibc |
|
||||
| `configure.ac` | Cache `ac_cv_func_sysconf=no` | Not available in relibc |
|
||||
| `configure.ac` | Cache `ac_cv_func_getrlimit=no` | Relibc has it, but configure probe may misdetect; safe to cache |
|
||||
| `configure.ac` | Cache `ac_cv_func_tcgetsid=no` | Relibc has it, but configure probe may misdetect; safe to cache |
|
||||
| `configure.ac` | Cache `ac_cv_func_tgetent=yes` | Available via ncursesw |
|
||||
| `configure.ac` | Cache `ac_cv_func_tigetflag=yes` | Available via ncursesw |
|
||||
| `configure.ac` | Cache `ac_cv_func_tigetnum=yes` | Available via ncursesw |
|
||||
| `configure.ac` | Cache `ac_cv_func_tigetstr=yes` | Available via ncursesw |
|
||||
| `configure.ac` | Cache `ac_cv_func_setupterm=yes` | Available via ncursesw |
|
||||
| `configure.ac` | Remove `AC_SEARCH_LIBS([tgetent], [tinfo curses ncurses])` | Redox uses ncursesw directly |
|
||||
| `configure.ac` | Remove `AC_SEARCH_LIBS([tigetstr], [tinfo curses ncurses])` | Redox uses ncursesw directly |
|
||||
| `configure.ac` | Remove `AC_SEARCH_LIBS([setupterm], [tinfo curses ncurses])` | Redox uses ncursesw directly |
|
||||
| `configure.ac` | Remove `AC_SEARCH_LIBS([del_curterm], [tinfo curses ncurses])` | Redox uses ncursesw directly |
|
||||
| `Src/rlimits.c` | Define `RLIM_NLIMITS` fallback | Relibc header may not define it |
|
||||
| `Src/rlimits.c` | Define `RLIM_SAVED_CUR` / `RLIM_SAVED_MAX` fallbacks | Relibc header may not define them |
|
||||
| `Src/rlimits.c` | Define `RLIMIT_NPTS` / `RLIMIT_SWAP` / `RLIMIT_KQUEUES` stubs | BSD-only limits not in relibc |
|
||||
| `Src/rlimits.c` | Define `RLIMIT_RTTIME` stub | Linux-only limit not in relibc |
|
||||
| `Src/rlimits.c` | Define `RLIMIT_NICE` / `RLIMIT_MSGQUEUE` / `RLIMIT_RTPRIO` stubs | Linux-only limits not in relibc |
|
||||
| `Src/rlimits.c` | Define `RLIMIT_NLIMITS` as 16 if still undefined | Final fallback |
|
||||
| `Src/params.c` | Guard `getpwnam`/`getpwuid` return value | Relibc returns basic structs; add NULL checks |
|
||||
| `Src/Modules/termcap.c` | Link against `ncursesw` not `termcap` | Redox has ncursesw, not standalone termcap |
|
||||
| `Src/Modules/clone.c` | Disable `clone` module | `clone()` / `unshare()` not available on Redox |
|
||||
| `Src/Modules/zpty.c` | Disable `zpty` module | `openpty` / `forkpty` not available on Redox |
|
||||
|
||||
### 2.5 Config Files Updated
|
||||
|
||||
- `config/redbear-full.toml` — Added `"zsh"` to `[packages]`
|
||||
- `config/redbear-mini.toml` — Added `"zsh"` to `[packages]`
|
||||
|
||||
### 2.6 WIP Recipe Removed
|
||||
|
||||
- `recipes/wip/shells/zsh/` — Removed after successful migration to production.
|
||||
|
||||
---
|
||||
|
||||
## 3. Build Verification
|
||||
|
||||
### 3.1 Build Command
|
||||
|
||||
```bash
|
||||
CI=1 ./target/release/repo cook zsh
|
||||
```
|
||||
|
||||
### 3.2 Build Output
|
||||
|
||||
```
|
||||
cook zsh - successful
|
||||
repo - publishing zsh
|
||||
repo - generating repo.toml
|
||||
```
|
||||
|
||||
### 3.3 Staged Artifacts
|
||||
|
||||
```
|
||||
stage/
|
||||
├── etc/
|
||||
│ ├── zsh/
|
||||
│ │ ├── zshenv # System-wide env setup
|
||||
│ │ ├── zprofile # System-wide profile
|
||||
│ │ └── zshrc # System-wide interactive config
|
||||
│ └── skel/
|
||||
│ ├── .zprofile # New-user template
|
||||
│ └── .zshrc # New-user interactive config
|
||||
└── usr/
|
||||
├── bin/
|
||||
│ ├── zsh # → zsh-5.9 (symlink)
|
||||
│ └── zsh-5.9 # Actual binary (~1.2 MB stripped)
|
||||
└── share/
|
||||
└── zsh/
|
||||
├── 5.9/
|
||||
│ └── functions/ # 800+ completion functions
|
||||
└── site-functions/ # Site-local completions
|
||||
```
|
||||
|
||||
### 3.4 Binary Check
|
||||
|
||||
```bash
|
||||
$ file zsh
|
||||
zsh: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
|
||||
|
||||
$ ls -la zsh
|
||||
-rwxr-xr-x 1 kellito kellito 1267176 Apr 26 02:14 zsh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. POSIX Dependency Matrix (Actual vs Planned)
|
||||
|
||||
| API / Feature | Planned Action | Actual Result |
|
||||
|---------------|---------------|---------------|
|
||||
| `getrlimit` / `setrlimit` | Remove obsolete cache | Cached `no` for safety; relibc has it |
|
||||
| `times` | Cache `ac_cv_func_times=no` | ✅ Cached; zsh uses `getrusage` fallback |
|
||||
| `tcgetsid` | Remove obsolete cache | Cached `no` for safety; relibc has it |
|
||||
| `setpgrp()` | Cache `ac_cv_func_setpgrp=no` | ✅ Cached; zsh falls back to `setpgid` |
|
||||
| `killpg` | Cache `ac_cv_func_killpg=no` | ✅ Cached; zsh defines `kill(-pgrp,sig)` |
|
||||
| `initgroups` | Cache if missing | ✅ Cached `no` |
|
||||
| `pathconf` / `sysconf` | Cache if missing | ✅ Cached `no` |
|
||||
| `RLIM_NLIMITS` | Patch if missing | ✅ Defined fallback in `rlimits.c` |
|
||||
| `tgetent` / `setupterm` | Cache `yes` | ✅ Cached `yes`; linked via ncursesw |
|
||||
| `dlopen` / `dlsym` | Start with `--disable-dynamic` | ✅ Static build; dynamic deferred |
|
||||
| `pcre_compile` | Start without, then enable | ✅ Disabled for initial build |
|
||||
| `locale` / `nl_langinfo` | `--enable-multibyte` | ✅ Enabled by default |
|
||||
| `getpwnam` / `getpwuid` | Add NULL guards | ✅ Patched in `params.c` |
|
||||
| `zpty` module | Disable if needed | ✅ Disabled in `zpty.c` |
|
||||
| `clone` module | Disable if needed | ✅ Disabled in `clone.c` |
|
||||
|
||||
---
|
||||
|
||||
## 5. Deviations from Original Plan
|
||||
|
||||
| Original Plan | What Actually Happened | Reason |
|
||||
|---------------|------------------------|--------|
|
||||
| Use `configure` template | Used `custom` template | Needed deterministic `signames.c` generation step |
|
||||
| Depend on `pcre` | No `pcre` dependency | Simpler initial build; can add later |
|
||||
| `--disable-dynamic` | Implicitly static | No `--enable-dynamic` flag passed; modules are built-in |
|
||||
| `--enable-zsh-mem=no` | Not needed | Default behavior uses system malloc |
|
||||
| `--enable-zsh-secure-free=no` | Not needed | Default behavior is safe |
|
||||
| `--with-tcsetpgrp` | Not needed | Auto-detected correctly |
|
||||
| Separate `config.site` | Patches embedded in `redox.patch` | Cleaner single-file approach |
|
||||
| `git` source | `tar` source with BLAKE3 | Faster fetch, reproducible builds |
|
||||
|
||||
---
|
||||
|
||||
## 6. Runtime Validation (Pending)
|
||||
|
||||
The following acceptance criteria have **not yet been verified** in QEMU/bare metal:
|
||||
|
||||
| # | Criterion | Status |
|
||||
|---|-----------|--------|
|
||||
| 1 | `zsh` binary compiles and links for `x86_64-unknown-redox` | ✅ Verified |
|
||||
| 2 | `zsh -c 'echo hello'` runs in QEMU without crash | ⏳ Pending |
|
||||
| 3 | Interactive prompt (`zsh -f`) accepts input and executes commands | ⏳ Pending |
|
||||
| 4 | `ulimit`, `cd`, `echo`, `for`, `if`, `function` builtins work | ⏳ Pending |
|
||||
| 5 | History file (`HISTFILE`) persists across sessions | ⏳ Pending |
|
||||
| 6 | Tab completion (`zle`) functions without crash | ⏳ Pending |
|
||||
| 7 | Job control (`set -m`, `fg`, `bg`, `jobs`) works | ⏳ Pending |
|
||||
| 8 | PCRE module (`zsh/pcre`) loads and `=~` works | ⏳ Deferred |
|
||||
| 9 | Dynamic modules load via `zmodload` | ⏳ Deferred |
|
||||
| 10 | Added to `redbear-full.toml` and `redbear-mini.toml` | ✅ Done |
|
||||
|
||||
### 6.1 Runtime Test Commands
|
||||
|
||||
```bash
|
||||
# Build full image
|
||||
make all CONFIG_NAME=redbear-full
|
||||
|
||||
# Run in QEMU
|
||||
make qemu CONFIG_NAME=redbear-full
|
||||
|
||||
# Inside QEMU:
|
||||
zsh -c 'echo hello' # Basic execution
|
||||
zsh -f # Interactive without user config
|
||||
print -P '%n@%m %~ %# ' # Prompt expansion
|
||||
for i in 1 2 3; do echo $i; done # Loop
|
||||
function hello { echo "hi $1" }; hello world # Function
|
||||
ulimit -a # Resource limits
|
||||
bindkey # Key bindings
|
||||
echo "test" > /tmp/hist; fc -R /tmp/hist # History
|
||||
touch /tmp/file{A,B,C}; ls /tmp/file<TAB> # Completion
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Future Work
|
||||
|
||||
### 7.1 Feature Expansion
|
||||
|
||||
| Feature | Action | Priority |
|
||||
|---------|--------|----------|
|
||||
| PCRE support | Add `pcre` dependency, enable `--enable-pcre` | Low |
|
||||
| Dynamic modules | Enable `--enable-dynamic`, verify `dlopen` | Low |
|
||||
| `zpty` module | Implement `openpty` in relibc or patch zpty | Low |
|
||||
| `clone` module | Implement `clone` in relibc or keep disabled | Low |
|
||||
| GDBM support | Add `gdbm` recipe, enable `--enable-gdbm` | Very Low |
|
||||
|
||||
### 7.2 Integration
|
||||
|
||||
| Task | Location | Status |
|
||||
|------|----------|--------|
|
||||
| Add `/usr/bin/zsh` to `/etc/shells` | `recipes/core/userutils` or `local/recipes/branding/redbear-release` | ⏳ Pending |
|
||||
| `chsh` support | `recipes/core/userutils` | ⏳ Pending |
|
||||
| Set zsh as default shell | `config/redbear-full.toml` `[users]` section | ⏳ Pending |
|
||||
|
||||
---
|
||||
|
||||
## 8. Files
|
||||
|
||||
### Created
|
||||
|
||||
```
|
||||
recipes/shells/zsh/recipe.toml
|
||||
recipes/shells/zsh/redox.patch
|
||||
recipes/shells/zsh/README.md
|
||||
recipes/shells/zsh/etc/zsh/zshenv
|
||||
recipes/shells/zsh/etc/zsh/zprofile
|
||||
recipes/shells/zsh/etc/zsh/zshrc
|
||||
recipes/shells/zsh/etc/skel/.zprofile
|
||||
recipes/shells/zsh/etc/skel/.zshrc
|
||||
```
|
||||
|
||||
### Modified
|
||||
|
||||
```
|
||||
config/redbear-full.toml
|
||||
config/redbear-mini.toml
|
||||
local/docs/ZSH-PORTING-PLAN.md
|
||||
```
|
||||
|
||||
### Removed
|
||||
|
||||
```
|
||||
recipes/wip/shells/zsh/ (entire directory)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Quick Reference
|
||||
|
||||
```bash
|
||||
# Build zsh
|
||||
CI=1 ./target/release/repo cook zsh
|
||||
|
||||
# Build full image with zsh
|
||||
make all CONFIG_NAME=redbear-full
|
||||
|
||||
# Test in QEMU
|
||||
make qemu CONFIG_NAME=redbear-full
|
||||
|
||||
# Clean and rebuild
|
||||
rm -rf recipes/shells/zsh/target
|
||||
CI=1 ./target/release/repo cook zsh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Document version: 2.0 — Implementation complete*
|
||||
*Last updated: 2026-04-26*
|
||||
Reference in New Issue
Block a user