feat: usb-automount feature — udev+systemd auto-mount of USB sticks, flag-gated like autostart

features/usb-automount.sh (installed via ./install.sh --feature, flag
usb-automount) mounts every unmounted removable block device at
/media/<label> — world-writable via -o umask=000 (fallback plain mount),
label-collision bump -2/-3, no-label -> usb-<name>. First root run
self-installs the hotplug udev rule (/etc/udev/rules.d/99-usb-automount.rules,
SYSTEMD_WANTS=usb-automount.service) + udevadm reload/trigger; an existing
rule is never overwritten. systemd/usb-automount.service (Type=oneshot,
WantedBy=multi-user.target) covers boot + hotplug + manual start, gated in
postinstall.sh's systemd loop exactly like autostart. Purpose: a plugged-in
stick is ready for pos system backup's post-verify USB copy without manual
mounting. Docs: SYSTEMD/SCRIPTS/README/AGENT_Context (tree, filetable —
postinstall.sh count corrected 152->163, it was already stale). Verified:
stub suite /tmp/opencode/usb-automount-test 47/47 green; make gen && make check.
This commit is contained in:
Your Name
2026-08-13 03:40:13 -04:00
parent d4d38ad901
commit a3c01a0394
8 changed files with 217 additions and 19 deletions
+17 -15
View File
@@ -10,19 +10,19 @@
<!-- GEN:START docmap -->
| ## 1. Project Overview | 2843 |
| ## 2. Directory Structure | 44189 |
| ## 3. Installation Flow | 190242 |
| ## 4. The `pos` CLI System | 243312 |
| ## 5. Shared Library — `lib/common.sh` | 313344 |
| ## 6. Docker Compose / ScaleTail | 345387 |
| ## 7. Optional Apps (`apps/`) | 388417 |
| ## 8. Entertainment Module | 418431 |
| ## 9. Systemd Services | 432444 |
| ## 10. Configuration Files | 445471 |
| ## 11. Coding Conventions | 472504 |
| ## 12. Development Workflow | 505557 |
| ## 13. Key File Quick Reference | 558614 |
| ## 14. Common Tasks for Agents | 615644 |
| ## 2. Directory Structure | 44190 |
| ## 3. Installation Flow | 191243 |
| ## 4. The `pos` CLI System | 244313 |
| ## 5. Shared Library — `lib/common.sh` | 314345 |
| ## 6. Docker Compose / ScaleTail | 346388 |
| ## 7. Optional Apps (`apps/`) | 389418 |
| ## 8. Entertainment Module | 419432 |
| ## 9. Systemd Services | 433445 |
| ## 10. Configuration Files | 446472 |
| ## 11. Coding Conventions | 473505 |
| ## 12. Development Workflow | 506558 |
| ## 13. Key File Quick Reference | 559616 |
| ## 14. Common Tasks for Agents | 617646 |
<!-- GEN:END docmap -->
## 1. Project Overview
@@ -100,7 +100,8 @@ Linux_post_install/
│ └── ssh-load-all # Legacy wrapper → pos ssh load-keys
├── features/ # User-customizable scripts (installed via --feature)
── autostart.sh # Boot-time script (via systemd, flag-gated)
── autostart.sh # Boot-time script (via systemd, flag-gated)
│ └── usb-automount.sh # USB automount (udev rule + flag-gated service)
├── entertainment/ # Public-API plugins for pos entertainment send (→ /usr/local/bin)
│ ├── weather.sh # Current weather via Open-Meteo (no API key)
@@ -561,7 +562,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
|------|-------|---------|
| `install.sh` | 206 | Main orchestrator — 4 phases with CLI flags, `--feature`, prebuilt arch bins |
| `preinstall.sh` | 73 | System packages + hotspot deps + yt-dlp + fail2ban |
| `postinstall.sh` | 152 | fail2ban config, PATH, bash completion, systemd (flag-gated) |
| `postinstall.sh` | 163 | fail2ban config, PATH, bash completion, systemd (flag-gated) |
| `lib/common.sh` | 144 | Shared library (log/warn/err/run/spawn, dry-run aware, `load_system_env`) |
| `lib/flags.sh` | 60 | Feature flag store (set/clear/is_set/value/list/status) |
| `lib/notify.sh` | 76 | Multi-platform alerting (`notify_send`) — opt-in source, silent-fails |
@@ -571,6 +572,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
| `bin/flag-set` | 21 | Set a flag (optionally with a value) |
| `bin/flag-clear` | 21 | Unset a flag |
| `features/autostart.sh` | 14 | Boot-time feature (moved from `bin/`, flag-gated service) |
| `features/usb-automount.sh` | 134 | USB automount feature (udev rule + flag-gated service) |
<!-- GEN:START filetable -->
| `bin/pos` | 292 | CLI dispatcher with smart arg matching + logging + category help |
| `bin/pos-ai-gemini` | 311 | Chat with Google Gemini (ask, chat, models, sessions) |
+2 -2
View File
@@ -5,10 +5,10 @@ Everything in this folder is reference material for the `Linux_post_install` pro
| Document | What it covers |
|----------|----------------|
| [HOWTO.md](HOWTO.md) | Hands-on guides per category (ai, network, docker, media, system, ssh, share, communication, entertainment): flags, recipes, config, troubleshooting |
| [SCRIPTS.md](SCRIPTS.md) | Core installer scripts: `install.sh`, `preinstall.sh`, `postinstall.sh`, `lib/common.sh`, `lib/flags.sh`, `lib/entertainment-lib.sh`, `features/autostart.sh` — purpose, how each works, configuration |
| [SCRIPTS.md](SCRIPTS.md) | Core installer scripts: `install.sh`, `preinstall.sh`, `postinstall.sh`, `lib/common.sh`, `lib/flags.sh`, `lib/entertainment-lib.sh`, `features/autostart.sh`, `features/usb-automount.sh` — purpose, how each works, configuration |
| [POS.md](POS.md) | The `pos` CLI: dispatcher, every `pos-*` command, Docker Compose / ScaleTail config, legacy wrappers, flag CLIs |
| [APPS.md](APPS.md) | Optional apps: `apps/install.sh` picker, installer conventions, full app catalog |
| [SYSTEMD.md](SYSTEMD.md) | Systemd units (`autostart.service`, `ssh-agent.service`), feature-flag gating, bash completion |
| [SYSTEMD.md](SYSTEMD.md) | Systemd units (`autostart.service`, `usb-automount.service`, `ssh-agent.service`), feature-flag gating, bash completion |
| [DEV.md](DEV.md) | Developer guide: architecture, conventions, how to add tools/apps/features, commit guidelines |
| [AGENT_Context_Project.md](AGENT_Context_Project.md) | Single-source context doc for AI agents working on the repo |
| [algorithm.md](algorithm.md) | ASCII diagrams: install flow, `pos` dispatch, compose `up`, config cascade, logging, vbox lifecycle |
+22 -1
View File
@@ -13,6 +13,7 @@ Everything that runs during the bootstrap install: `install.sh`, `preinstall.sh`
- [lib/flags.sh — feature flags](#libflagssh--feature-flags)
- [lib/notify.sh — multi-platform alerting](#libnotifysh--multi-platform-alerting)
- [features/autostart.sh — boot-time feature](#featuresautostartsh--boot-time-feature)
- [features/usb-automount.sh — USB automount feature](#featuresusb-automountsh--usb-automount-feature)
- [x64_bin/ — precompiled binaries](#x64_bin--precompiled-binaries)
---
@@ -108,7 +109,7 @@ Add or remove package names here. `nmap` and `fail2ban` are used later by `pos n
2. **PATH** — appends a `PATH` line to `~/.bashrc` if not already present.
3. **pos bash completion** — installs `completions/pos.bash` to `/usr/local/share/bash-completion/completions/` and sources it from `~/.bashrc`.
4. **SSH authorized keys** — if `config/authorized_keys` exists, appends missing keys to `~/.ssh/authorized_keys` (skips comments and duplicates, chmod 600).
5. **systemd services** — copies `systemd/*.service` to `/etc/systemd/system/`, daemon-reloads, then enables each service. **`autostart.service` is only enabled when the `autostart` feature flag is set** (see [lib/flags.sh](#libflagssh--feature-flags)); otherwise it's skipped with a hint to run `./install.sh --feature`.
5. **systemd services** — copies `systemd/*.service` to `/etc/systemd/system/`, daemon-reloads, then enables each service. **`autostart.service` is only enabled when the `autostart` feature flag is set**, and **`usb-automount.service` only when the `usb-automount` flag is set** (see [lib/flags.sh](#libflagssh--feature-flags)); otherwise they're skipped with a hint to run `./install.sh --feature`.
### Configuration
@@ -221,6 +222,26 @@ Appends timestamped lines to `~/.autostart.log`:
---
## features/usb-automount.sh — USB automount feature
**File:** `features/usb-automount.sh` (installed to `/usr/local/bin/usb-automount.sh` by `./install.sh --feature`)
**Purpose:** mounts USB storage automatically — at boot and on hotplug — so a plugged-in stick is immediately ready for `pos system backup`'s post-verify USB copy without manual mounting.
### How it works
1. Every run (boot via `usb-automount.service`, hotplug via its self-installed udev rule, or manual `sudo usb-automount.sh`) scans `lsblk -J` for unmounted removable block devices — partitions and raw whole-disk filesystems — and mounts each at `/media/<label>`.
2. vfat/exfat/ntfs mounts are world-writable (`-o umask=000`) so a non-root user can write; filesystems that reject `umask` fall back to a plain mount.
3. Idempotent: already-mounted devices are skipped, and repeated runs are no-ops.
4. First root run installs the hotplug rule `/etc/udev/rules.d/99-usb-automount.rules` (`SYSTEMD_WANTS="usb-automount.service"`) and reloads udev — an edited rule is never overwritten.
### Configuration
- Log file: `$HOME/.usb-automount.log` (edit the `LOG` variable at the top).
- Mount point: `/media/<label>` (bumps to `-2`, `-3` when the label is already in use as a mountpoint); mount options are tuned in `mount_one`.
- The script is user-customizable like any feature — a plain reinstall never overwrites your edits.
---
## x64_bin/ — precompiled binaries
**Folder:** `x64_bin/` (future: `arm64_bin/`)
+26 -1
View File
@@ -4,6 +4,7 @@ The units installed and enabled by `postinstall.sh`, plus the `pos` bash complet
- [Services](#services)
- [`autostart.service`](#autostartservice)
- [`usb-automount.service`](#usb-automountservice)
- [`ssh-agent.service`](#ssh-agentservice)
- [`pos-health.service`](#pos-healthservice)
- [Per-user units (`pos network download`)](#per-user-units-pos-network-download)
@@ -38,6 +39,25 @@ WantedBy=multi-user.target
**Configuration:** point `ExecStart` at your boot script. Because the target script is a *feature*, this unit is only **enabled** when the `autostart` flag is set — the file is still copied, but a skipped feature leaves the unit present-but-disabled.
### usb-automount.service
**Purpose:** mount USB storage automatically — at boot (via `WantedBy=multi-user.target`) and on hotplug (a udev rule installed by the feature script triggers this unit). Each run mounts every unmounted removable block device at `/media/<label>`, world-writable (`-o umask=000`), so `pos system backup`'s post-verify USB copy finds the stick and the unprivileged user can write to it.
```ini
[Unit]
Description=Auto-mount USB storage (usb-automount feature)
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/usb-automount.sh
[Install]
WantedBy=multi-user.target
```
**Configuration:** `Type=oneshot` — each start (boot, hotplug, manual `systemctl start usb-automount`) does one idempotent scan. `features/usb-automount.sh` self-installs its udev rule (`/etc/udev/rules.d/99-usb-automount.rules`, `SYSTEMD_WANTS="usb-automount.service"`) on first root run and reloads udev, so plugging in a stick fires the mount with no extra setup; an edited rule is never overwritten. Because the target script is a *feature*, this unit is only **enabled** when the `usb-automount` flag is set — the file is still copied, but a skipped feature leaves the unit present-but-disabled. Hotplug is restricted to USB (`ENV{ID_BUS}=="usb"`); the boot scan covers all removable media.
### ssh-agent.service
**Purpose:** a system-wide SSH agent, one shared socket for all sessions (so `pos ssh load-keys` and everyday ssh work without per-login agents).
@@ -116,19 +136,24 @@ enable-linger` warning so the user units survive logout.
## Feature-flag gating
The systemd loop in `postinstall.sh` special-cases two units:
The systemd loop in `postinstall.sh` special-cases three units:
```bash
if [ "$svc_name" = "autostart.service" ] && ! flag_is_set autostart; then
warn "autostart feature not installed — skipping autostart.service (run ./install.sh --feature)"
continue
fi
if [ "$svc_name" = "usb-automount.service" ] && ! flag_is_set usb-automount; then
warn "usb-automount feature not installed — skipping usb-automount.service (run ./install.sh --feature)"
continue
fi
if [ "$svc_name" = "pos-health.service" ]; then
# substitute User= and enable pos-health.timer only if Telegram is configured
fi
```
- `autostart.service` is **enabled** only when the `autostart` feature flag is set (`./install.sh --feature` or `flag-set autostart`). See [SCRIPTS.md → lib/flags.sh](SCRIPTS.md#libflagssh--feature-flags).
- `usb-automount.service` is **enabled** only when the `usb-automount` feature flag is set — same mechanism.
- `pos-health.service` is **not** enabled at all — `postinstall.sh` enables `pos-health.timer` instead, and only when a Telegram config already exists.
---