Skip to content

nes-emulator: add package - #30350

Open
communism420 wants to merge 1 commit into
openwrt:masterfrom
communism420:codex/add-nes-emulator
Open

nes-emulator: add package#30350
communism420 wants to merge 1 commit into
openwrt:masterfrom
communism420:codex/add-nes-emulator

Conversation

@communism420

@communism420 communism420 commented Aug 21, 2026

Copy link
Copy Markdown

📦 Package Details

Maintainer: @communism420

Description:

Add nes-emulator, a router-side NES emulation service for OpenWrt.

The daemon provides authenticated HTTP and WebSocket streaming. No ROM or BIOS
files are included.

The recipe downloads the canonical SemVer-tagged project source and the pinned
FCEUmm commit as two independently SHA-256-verified archives. The two required
FCEUmm changes are feed-local, reviewable patches with authorship, DCO sign-off,
and upstream-status metadata. The daemon is built as a dynamically linked PIE
with OpenWrt hardening enabled.

The generic savestate error-propagation change was merged upstream through
libretro-fceumm#653 as
commit 3db086e,
so patch 001 is marked as a backport. The package deliberately retains the
audited FCEUmm pin 76f68314: the merge tip also contains an unrelated
savestate-format transition to 9900, while project v1.0.0 builds the embedded
core with FCEU_VERSION_NUMERIC=9813. That coordinated transition belongs in a
later project release.

The ROM-buffer change remains downstream-specific: FCEUmm advertises
need_fullpath=true, under which generic libretro frontends may leave data
and size invalid, while nesd deliberately supplies the path and the exact
buffer it hashed to preserve its hash/load invariant.

The feed owns and reviews the procd init script and UCI defaults directly. The
standard OpenWrt package lifecycle invokes the init script on a live root, and
the package-owned data tree is provisioned before the disabled-service early
return.

Existing external paths are never chowned or chmodded. For a newly created
external directory, ownership/mode repair is best-effort: a failure is logged
at daemon.warn because filesystems such as vfat/exfat may reject
chown/chmod while mount
uid, gid, and dmask options already provide the required access. The final
decision is always the effective-access check using the numeric nesd uid and
all effective groups. rom_dir and save_dir require write/search access;
system_dir requires read/search access; every ancestor must be searchable.
Failures report the required access and the relevant identity/mount guidance.
Metadata repair remains strict for the package-owned /etc/nes-emulator tree.

The init script and the companion
luci-app-nes-emulator RPCD bridge
coordinate authentication-token updates through
/var/run/nes-emulator/auth.token.lock. The shared runtime directory is
validated as root-owned mode 0700; lock files must be root-owned regular
single-link files with mode 0600. Files are created with no-clobber semantics,
opened catchably without truncation, and revalidated after opening.

The RPCD phase of token rotation permits at most seven one-second lock-wait
sleeps: two waits before the pre-dispatch token check, two inside rotation, and
three for the startup lock. A contended native restart can add at most nine
native token-lock sleeps, so the coordinated lock-wait bound is 16 rather than
seven. The companion LuCI view gives the complete non-cancellable operation a
120-second RPC budget.

This native package is the dependency of the separate LuCI submission.


🧪 Run Testing Details

  • OpenWrt Version: 25.12.5 on hardware; current SNAPSHOT SDK validation
  • OpenWrt Target/Subtarget: mediatek/filogic; x86/64; armsr/armv8
  • OpenWrt Device: ASUS RT-AX52 Pro (aarch64_cortex-a53)
  • Current review heads: native 62ec2dff9; LuCI 038ad57ad8

The complete native/LuCI pair was tested on the router using native PR head
b4424df4 and LuCI PR head 5028c07a. Package installation and dependency
resolution, service control, ROM upload/loading, video, audio, controller
input, save/load state, and the FPS OSD all worked on the device.

The hardware-tested emulator and FCEUmm sources remain unchanged. Current heads
add the review-driven lifecycle, external-storage, protected-lock, bounded-wait,
isolated/unbatched long-RPC, and complete file-descriptor fixes without changing
the native binary source.

The source revision exporting these heads is covered by the successful
project CI run.
That run includes:

  • plain ShellCheck and the complete scripts/check.sh suite
  • official packages/LuCI export and template contracts
  • vfat/exfat-style metadata-repair failure with both usable and unusable
    effective-access outcomes
  • owner, supplementary-group, other, ancestor, and read-vs-write permission
    precedence
  • protected lock creation, symlink/hardlink rejection, post-open validation,
    catchable opens, bounded waits, and rotation/start race contracts
  • per-method isolated/unbatched LuCI long-RPC timeout, shared-module, and
    lock-fd inheritance contracts
  • verified FCEUmm downloads/patches, a clean native build, and the complete
    black-box regression suite

The recipe and unchanged native binary were also validated with current x86/64
and armsr/armv8 SDKs:

  • both codeload hashes match and both feed-local patches apply cleanly
  • make package/feeds/packages/nes-emulator/refresh V=s was run twice; both
    runs left the patch series byte-identical
  • both refreshed patches apply in order with git am to the pinned FCEUmm tree
  • package check and compilation complete successfully
  • generated APK metadata, maintainer, dependencies, files, and scriptlets were
    inspected
  • nesd --version reports 1.0.0
  • packaged daemons are dynamically linked PIE executables with GNU RELRO,
    BIND_NOW, stack protection, and non-executable stacks

✅ Formalities

  • I have reviewed the CONTRIBUTING.md file for detailed contributing guidelines.

If your PR contains a patch:

  • It can be applied using git am
  • It has been refreshed to avoid offsets, fuzzes, etc., using
    make package/feeds/packages/nes-emulator/refresh V=s
  • It is structured in a way that it is potentially upstreamable

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit. The commit message matches the diff, and v1.0.0-r19 does exist in the upstream repository, so the source reference checks out. Formality checks are green; nothing CI-related to add.

Of the four inline notes, two look like they should be settled before merge: the postinst can fail the install transaction because preflight supplies its exit status, and the runtime directories ship root:root 0750 while the daemon is meant to run as nesd. The third — init script and UCI defaults living in the release tarball instead of multimedia/nes-emulator/files/ — is a feed-convention point, but it also means the parts of this package that actually determine its runtime behaviour are not in the diff, which is worth resolving on a new-package submission. The last one is a nit.


Generated by Claude Code

Comment thread multimedia/nes-emulator/Makefile Outdated
Comment thread multimedia/nes-emulator/Makefile Outdated
Comment thread multimedia/nes-emulator/Makefile Outdated
Comment thread multimedia/nes-emulator/Makefile Outdated
Comment thread multimedia/nes-emulator/Makefile Outdated
@communism420
communism420 force-pushed the codex/add-nes-emulator branch from 5358636 to 658d505 Compare August 22, 2026 10:15
@communism420
communism420 marked this pull request as ready for review August 22, 2026 10:19
Comment thread multimedia/nes-emulator/Makefile Outdated
Comment thread multimedia/nes-emulator/Makefile Outdated
Comment thread multimedia/nes-emulator/Makefile Outdated
Comment thread multimedia/nes-emulator/Makefile Outdated
@communism420
communism420 force-pushed the codex/add-nes-emulator branch from 658d505 to 933164c Compare August 22, 2026 11:00
@communism420
communism420 force-pushed the codex/add-nes-emulator branch from 933164c to af1380f Compare August 22, 2026 11:15
@communism420
communism420 marked this pull request as draft August 22, 2026 11:21
@communism420
communism420 marked this pull request as ready for review August 22, 2026 11:21
@communism420
communism420 force-pushed the codex/add-nes-emulator branch from af1380f to b4424df Compare August 22, 2026 12:42
@communism420

Copy link
Copy Markdown
Author

Follow-up on the patch-submission checklist in b4424df:

  • added a quilt target for the separately unpacked FCEUmm tree and ran make package/feeds/packages/nes-emulator/refresh V=s twice; the second run was byte-identical
  • verified both refreshed patches with git am, then rebuilt the x86_64 package successfully
  • submitted the generic savestate fix upstream as state: propagate savestate parse errors libretro/libretro-fceumm#653 and marked it Submitted
  • marked the ROM-buffer patch Inappropriate [nesd frontend specific] with the need_fullpath/hash-load rationale in the patch itself
  • restored all three patch checklist items in the PR description

@communism420
communism420 force-pushed the codex/add-nes-emulator branch from b4424df to 227f4cb Compare August 22, 2026 19:25
@communism420

communism420 commented Aug 22, 2026

Copy link
Copy Markdown
Author

Hardware testing update for native head 227f4cb3 and LuCI head 5028c07a: the pair was installed and tested successfully on an ASUS RT-AX52 Pro running OpenWrt 25.12.5 (mediatek/filogic, aarch64_cortex-a53). Installation and dependency resolution, service control, ROM upload/loading, video, audio, controller input, save/load state, and the FPS OSD all worked on-device. Native head 227f4cb3 differed from the directly tested b4424df4 only in patch 001's upstream-status/reference after libretro-fceumm#653 was merged; its pin, hunks, sources, recipe, runtime files, rebuilt APK, and nesd were unchanged.

The later native head 3ac1d595 adds the reviewer-requested external-storage permission checks, official lifecycle cleanup, protected token lock, and unused-state removal. Those exact shell/package changes passed the new BusyBox regression contracts and a clean current Snapshot x86/64 SDK check/build; the historical router result above is intentionally scoped to the earlier byte-identical runtime revision.

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the rework since 5358636 — the init script and UCI defaults are now feed-local and reviewable, the postinst can no longer fail the install transaction, PKG_VERSION is a plain SemVer with an independent PKG_RELEASE, and the FCEUmm pin is a separately hash-verified secondary download with two properly-headed quilt patches. The custom variables, the OpenWrt-specific combined tarball and the build-time chmod 750 of empty data directories are all gone. Commit message matches the diff, and all three FormalityCheck runs are green on the head, so nothing CI-related to add.

I could not fetch libretro/libretro-fceumm from this session, so the Upstream-Status: Backport [...3db086e...] reference on patch 001 and the FCEUmm tarball hash are unverified here — not flagged, just not confirmed.

Nothing I found blocks a merge. The one I would settle first is the external-storage case in prepare_data_dir: the shipped config recommends moving rom_dir/save_dir onto mounted storage, and that path deliberately skips the ownership repair without checking or logging whether nesd can write there, so save states fail silently at runtime. The lockfile in world-writable /var/lock and the dead safety_migration option are worth a look; the rest is a redundant/no-op postinst and one nit.


Generated by Claude Code

Comment thread multimedia/nes-emulator/files/nes-emulator.init
Comment thread multimedia/nes-emulator/files/nes-emulator.config Outdated
Comment thread multimedia/nes-emulator/Makefile Outdated
Comment thread multimedia/nes-emulator/files/nes-emulator.init Outdated
Comment thread multimedia/nes-emulator/files/nes-emulator.init Outdated
@communism420
communism420 force-pushed the codex/add-nes-emulator branch from 227f4cb to 3ac1d59 Compare August 23, 2026 09:46

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changes since 227f4cb. All four points from the last round are addressed, and the mechanical parts hold up under checking:

  • command exec 8>"$TOKEN_LOCK_FILE" || return 1 really does return instead of terminating the shell — confirmed in dash, which BusyBox ash descends from — so the open failure is now catchable, and the lock sits inside the root-owned 0750 /etc/nes-emulator instead of world-writable /var/lock.
  • The new directory_is_writable_by_nesd / directory_is_searchable_by_nesd mode globs are correct. I ran them over owner/group/other precedence, setgid s vs S and sticky t vs T, and every case classified the way the kernel would, including the owner-class-denies-without-falling-through case. external_data_dir_is_usable_by_nesd walks to / and terminates.
  • AUTH_TOKEN is gone from every path and safety_migration has no remaining reference anywhere in the package.
  • Dropping Package/nes-emulator/postinst is safe: default_postinst runs "$i" start for each shipped init script on a live root (functions.sh:401-412) after add_group_and_user (package-pack.mk:545-546), and it does not propagate that start's exit status, so provisioning still happens and still cannot fail the install transaction.

The commit message matches the diff, and all three FormalityCheck runs are green on the head, so nothing CI-related to add.

Nothing here blocks a merge. The one I would settle is the flip side of the external-storage fix: rejecting an unusable path is the right behaviour, but a vfat/exfat USB stick — the storage the shipped config actually recommends — now produces a service that refuses to start behind a message that does not say which access is missing. The system_dir question and one nit are smaller.


Generated by Claude Code

Comment thread multimedia/nes-emulator/files/nes-emulator.init Outdated
Comment thread multimedia/nes-emulator/files/nes-emulator.init Outdated
Comment thread multimedia/nes-emulator/files/nes-emulator.init
@communism420
communism420 force-pushed the codex/add-nes-emulator branch from 3ac1d59 to 891dfd4 Compare August 24, 2026 10:37

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changes since 3ac1d59. Both points from the last round are addressed, and the mechanical parts check out:

  • The read / write split is correct. directory_is_readable_by_nesd uses the same owner → group → other precedence as its siblings, and its three globs (dr?[xs]??????, d???r?[xs]???, d??????r?[xt]) land on the right positions of the 10-character mode string, including the setgid s/S and sticky t/T variants. system_dir now asks for read+search while rom_dir/save_dir keep write+search, and the requirement is threaded through external_data_dir_is_usable_by_nesd without changing the ancestor walk.
  • The failure message now names the missing access, the numeric uid, the primary gid and every effective group. log_error joins its arguments through "$*", so the three-argument call renders as one line.
  • Moving the lock off /var/lock to /var/run/nes-emulator closes the world-writable-parent problem for real: base-files creates /var/run with a plain mkdir -p under the default umask and never relaxes it (boot:26-29), unlike /var/lock right above it which is explicitly chmod 1777. So the new parent is root-owned 0755, nesd cannot pre-create or squat the directory, and the drwx------ / -rw------- root-owned validation plus set -C no-clobber creation and post-open revalidation hold. <> also avoids the truncation the old 8> would have done to a lock file it did not create.

The commit message matches the diff, and all three FormalityCheck runs are green on the head, so nothing CI-related to add.

Nothing here blocks a merge, but the first inline is worth settling: the external-storage path still has a silent hard failure in front of the diagnostic this round added, and on vfat/exfat it triggers even when the stick is mounted exactly as the shipped config advises. The other is a nit about a number in the PR body.


Generated by Claude Code

Comment thread multimedia/nes-emulator/files/nes-emulator.init Outdated
Comment thread multimedia/nes-emulator/files/nes-emulator.init
@communism420
communism420 force-pushed the codex/add-nes-emulator branch from 891dfd4 to 6e9d006 Compare August 25, 2026 10:42

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changes since 891dfd4. Two hunks, both of which hold up:

  • The best-effort metadata repair is the fix the last round asked for, and I traced both external-storage outcomes through the new control flow. A stick mounted the way nes-emulator.config:3-4 advises — uid=/gid= set, default dmask — now gets chmod 0750 refused, logs the warning, and then directory_is_writable_by_nesd matches drwxr-xr-x on the owner branch, so the service starts. A default uid=0,gid=0 mount fails the chown, falls through to the other class with no w, and reaches the actionable "lacks write/search access" message added last round instead of the silent return 1. Strict repair is unchanged for /etc/nes-emulator and /etc/nes-emulator/*, and the ancestor walk is untouched.
  • sleep 1 8>&- keeps the held lock descriptor out of the wait child, matching the exec 8>&- already used in the prepare_lock_directory / prepare_token_lock_file subshells. ash saves and restores per-command redirections, so fd 8 is still open for the next flock -n 8 iteration and for release_token_lock. (Several short-lived children inside the locked region — uci, mktemp, mv — still inherit fd 8; no consequence, since they exit before the lock is released.)

The lock-wait accounting in the PR body now matches the code: 2+2+3 = 7 on the RPCD side, and TOKEN_LOCK_MAX_ATTEMPTS=10 yields nine sleep 1 calls here, for the stated coordinated bound of 16.

The commit message matches the diff, and all three FormalityCheck runs are green on the head, so nothing CI-related to add. I still cannot reach libretro/libretro-fceumm from this session, so the Upstream-Status: Backport reference on patch 001 and the FCEUmm tarball hash remain unverified here — not flagged, just not confirmed.

Nothing blocks a merge from my side this round; the single inline is a nit about log priority.


Generated by Claude Code

Comment thread multimedia/nes-emulator/files/nes-emulator.init Outdated
Add a router-side NES emulator service with HTTP and WebSocket
streaming.

Download the tagged host source and pinned FCEUmm core independently,
apply the reviewed feed-local core patches, and install the procd
service, UCI configuration, and an unprivileged service account.

Upstream project:
https://github.com/communism420/openwrt-nes-emulator

Signed-off-by: Yaroslav Vereshchagin <yarik.vereshchagin1996@gmail.com>
@communism420
communism420 force-pushed the codex/add-nes-emulator branch from 6e9d006 to 62ec2df Compare August 25, 2026 22:27

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit; no new issues found.

The changes since 6e9d006a are two hunks in nes-emulator.init and nothing else — git diff reports 5 insertions, 1 deletion in a single file — and both are the nit from the last round:

  • log_warn() mirrors log_error() exactly apart from the priority (-p daemon.warn vs -p daemon.err), keeps the same -t "$NAME" tag and the same -- guard before "$*", so the multi-argument call still renders as one line. It is defined at the top of the script alongside its sibling, well before its only call site.
  • That call site is the recoverable branch: the best-effort repair_directory_metadata failure for a newly created external directory, which the script deliberately continues from and where external_data_dir_is_usable_by_nesd remains the decider. Every one of the other 24 logging calls in the file is still log_error, and each of those precedes a non-zero return, so daemon.err now consistently means "nesd will not start". The unusable-path message two lines below is untouched and still goes out at err.

The strict-repair branches for /etc/nes-emulator and /etc/nes-emulator/*, the ancestor walk, the read/write requirement plumbing, and the lock handling are all byte-identical to the revision I checked last round, so nothing there needed re-verification.

The commit message still matches the diff, and all three FormalityCheck runs are green on the head, so nothing CI-related to add.

As in previous rounds, this session can only reach openwrt/packages, so the Upstream-Status: Backport [...3db086e...] reference on patch 001, the v1.0.0 project tag and both codeload hashes remain unverified here — not flagged, just not confirmed.

Nothing blocks a merge from my side.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants