alfred: add new package - #30161
Conversation
|
@BKPepe I think we had that before. @simonwunderlich is the maintainer - as stated in the Makefile |
|
Right, sorry — fixed the PR description, @simonwunderlich is the maintainer as stated in the Makefile. Same for batctl (#30163) and batman-adv (#30164). |
openwrt-ai
left a comment
There was a problem hiding this comment.
The Makefile cleanup looks right: the define blocks use the indentation each kind actually needs (two spaces for the metadata block, tabs for install and config, none for conffiles), PKG_RELEASE:=1 is correct for a new package, PKG_MAINTAINER is set, and BuildPackage is called with the literal name rather than $(PKG_NAME).
Everything I flagged is a carry-over from the routing feed rather than something this PR introduces, so none of it blocks the move. The two worth a look — before or in a follow-up — are the loadstring on mesh-sourced data and the unchecked io.open in bat-hosts.lua; the second one is a plain crash on any node with the bonding kmod loaded. The rest are marked nit:.
Two things I could not check from here, flagging so nobody assumes they were verified: PKG_HASH and the existence of the 2026.2 tarball (downloads.open-mesh.org is not reachable in this environment), and byte-for-byte parity with the current routing-feed master (no access to openwrt/routing). CI on the head commit is still pending, so there is nothing to read from it yet.
Generated by Claude Code
| local output = fd:read("*a") | ||
| fd:close() | ||
| if output and output ~= "" then | ||
| assert(loadstring("rows = {" .. output .. "}"))() |
There was a problem hiding this comment.
loadstring executes the output of alfred -r 64 as Lua code, and that output is assembled from payloads published by every other node on the mesh. If a remote node can get a " or a \ through alfred's escaping into a type-64 payload, it breaks out of the string literal and runs arbitrary Lua as root on every node with PACKAGE_ALFRED_BATHOSTS enabled — and this script is re-run from cron every 5 minutes, see the crontab line installed at alfred.init:81.
alfred's client does escape quotes/backslashes/non-printables as \xNN when it prints the { "mac", "data" } records, so this is probably safe in practice — but it makes a remote-input trust boundary depend entirely on the exact escaping of another program, with no local check. Parsing the records with a string.gmatch pattern (the MAC is already validated with ^%x%x:... at line 44) instead of evaluating them would remove the question entirely.
Is relying on alfred's escaping here deliberate?
Generated by Claude Code
| function get_interface_address(name) | ||
| local addressfile = io.open("/sys/class/net/"..name.."/address", "r") | ||
| local ret_string = addressfile:read() | ||
| addressfile:close() | ||
| return ret_string | ||
| end |
There was a problem hiding this comment.
io.open is not checked before addressfile:read() is called on it. /sys/class/net/ does not contain only interfaces: with the bonding kmod loaded it also contains the plain file bonding_masters, which the ls -1 at line 15 happily returns and which has no address below it. io.open then yields nil, nil:read() raises "attempt to index a nil value", and the whole facter dies — so no bat-hosts data gets published at all on such a node.
| function get_interface_address(name) | |
| local addressfile = io.open("/sys/class/net/"..name.."/address", "r") | |
| local ret_string = addressfile:read() | |
| addressfile:close() | |
| return ret_string | |
| end | |
| function get_interface_address(name) | |
| local addressfile = io.open("/sys/class/net/"..name.."/address", "r") | |
| if not addressfile then return nil end | |
| local ret_string = addressfile:read() | |
| addressfile:close() | |
| return ret_string | |
| end |
Returning nil needs a matching guard at the caller: line 40 would otherwise do ifaces[nil] = i, which raises "table index is nil". if address and not ifaces[address] then ifaces[address] = i end covers it.
get_hostname at line 6 has the same unchecked-io.open shape, though /proc/sys/kernel/hostname is always there in practice.
Generated by Claude Code
| config_get batmanif "$section" batmanif | ||
| append args "-b $batmanif" | ||
|
|
||
| append alfred_args "$args" |
There was a problem hiding this comment.
config_foreach runs alfred_start for every alfred section, but the results are accumulated into one flat alfred_args that feeds a single procd instance. Two enabled sections therefore produce alfred -f -i eth0 -m -b bat0 -f -i eth1 -b bat1 — one daemon with repeated -i/-b, not two daemons. vis_args at line 52 has the same shape, and run_facters ends up holding whatever the last section set.
Is a single section the only supported configuration? If so, opening one procd instance per section inside alfred_start (instance name derived from $section) would make the multi-section case actually work, or the config_foreach could go away in favour of a single named section so the limitation is visible in the script.
Generated by Claude Code
| alfred_start() { | ||
| local args="" | ||
| local section="$1" | ||
| local disabled interface mode |
There was a problem hiding this comment.
nit: batmanif and start_vis are used only inside alfred_start (lines 43-52) but are not declared local, unlike their neighbours on this line, so they leak into the rc.common shell and carry over between sections.
| local disabled interface mode | |
| local disabled interface mode batmanif start_vis |
run_facters has to stay global — start_service reads it at line 78.
Generated by Claude Code
| # REMOVE THIS LINE TO ENABLE ALFRED | ||
| option disabled '1' |
There was a problem hiding this comment.
nit: "THIS LINE" reads as the comment itself, but deleting the comment does nothing — it's the option disabled '1' below that has to go.
| # REMOVE THIS LINE TO ENABLE ALFRED | |
| option disabled '1' | |
| # REMOVE THE LINE BELOW TO ENABLE ALFRED | |
| option disabled '1' |
Generated by Claude Code
|
|
||
| define Package/alfred/conffiles | ||
| /etc/config/alfred | ||
| /etc/alfred/bat-hosts.lua |
There was a problem hiding this comment.
nit: this entry is unconditional, but the file is only installed when CONFIG_PACKAGE_ALFRED_BATHOSTS is set (lines 84-85), and it goes in via $(INSTALL_BIN) as an executable facter script rather than as a config file.
Nothing breaks — the packaging step skips conffiles that aren't in the image (package-pack.mk: [ -f $(IDIR)/$file ] || continue) — so this is only worth confirming the entry is intentional rather than a leftover.
Generated by Claude Code
openwrt-ai
left a comment
There was a problem hiding this comment.
Reviewed the one new commit since 0ef968b (alfred: handle unreadable files in bat-hosts.lua). The commit message matches what the diff does, and the get_interface_address path is now correct — nil is returned and the caller skips those entries, so a node with the bonding kmod loaded no longer kills the facter.
One loose end: the get_hostname guard has no caller-side counterpart, so it trades "attempt to index a nil value" for "attempt to concatenate a nil value" a few lines later. Inline comment with a suggestion. Not a blocker — /proc/sys/kernel/hostname is always present in practice, same as before this commit.
The earlier threads on loadstring, the init script's single-instance config_foreach, and the nit: items are untouched by this commit and still open; nothing to add there.
CI on 2bb2c4d is green across all ten build targets and all three FormalityCheck jobs. Still not verified from here: PKG_HASH / the 2026.2 tarball (downloads.open-mesh.org unreachable in this environment) and byte-for-byte parity with the current routing-feed master.
Generated by Claude Code
| local n, i | ||
| local ifaces, ret = {}, {} | ||
|
|
||
| local hostname = get_hostname() |
There was a problem hiding this comment.
The get_interface_address half of this fix is complete — nil is returned and the caller now skips it. The get_hostname half is not: the new if not hostfile then return nil end at line 7 has no matching guard here, so hostname can be nil and mac.." "..hostname.."_"..iname at line 49 raises "attempt to concatenate a nil value". The guard converts one crash into a different crash rather than removing it.
Same applies when the file opens but is empty — hostfile:read() returns nil too, which the if not hostfile check does not cover.
| local hostname = get_hostname() | |
| local hostname = get_hostname() | |
| if not hostname then return ret end |
Bailing out with the empty ret means publish_bat_hosts writes an empty chunk instead of dying, which matches how the rest of the script degrades.
Generated by Claude Code
The package has been moved to the openwrt/packages feed, as discussed in openwrt#184. See openwrt/packages#30161. Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
openwrt-ai
left a comment
There was a problem hiding this comment.
Reviewed 1 new commit; no new issues found.
The branch was force-pushed since my last review at 2bb2c4d. The only content change in the whole range 2bb2c4d..3d3e34c is one line added to define Package/alfred in net/alfred/Makefile:
+ SUBMENU:=Wireless
folded into the amended first commit; alfred: handle unreadable files in bat-hosts.lua is byte-identical to what I reviewed before. SUBMENU:=Wireless is an existing submenu under CATEGORY:=Network in this feed, and it matches what the sibling move uses — Package/batctl/Default in #30163 sets the same value — so the family stays together in menuconfig. Both commit subjects still describe what their diffs do.
The open threads from the earlier reviews are untouched by this change and still stand: the loadstring on mesh-sourced data, the get_hostname guard with no caller-side counterpart, the init script's single-instance config_foreach, and the nit: items.
CI on 3d3e34c is green — all ten build targets and all three FormalityCheck jobs. Still not verified from here: PKG_HASH and the existence of the 2026.2 tarball (downloads.open-mesh.org is unreachable in this environment), and byte-for-byte parity with the current routing-feed master. Note the PR body's "the content matches the current routing feed master" is now true only modulo this SUBMENU line, which is presumably part of the openwrt/routing#1192 cleanup the body mentions.
Generated by Claude Code
alfred is a user space daemon for distributing arbitrary local information (e.g. hostnames or vis data) over a batman-adv mesh network in a decentralized fashion via IPv6 link-local multicast. Moved from the openwrt/routing feed, as discussed in openwrt/routing#184. The one deviation from the feed copy is SUBMENU:=Wireless, which the feed copy does not set at all: without it the package sits ungrouped at the top of the Network menu. Wireless keeps it next to mesh11sd and batctl, which move across with the same value. A dedicated Mesh submenu is the better home for all three and is worth adding once enough of the mesh packages have landed to justify it. Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
get_interface_address() calls io.open() and dereferences the result without checking it. /sys/class/net does not contain interfaces only: with the bonding module loaded it also holds the plain file bonding_masters, which the `ls -1` in get_interfaces_names() returns and which has no address below it. io.open() then returns nil and the script dies with "attempt to index a nil value", so no bat-hosts data is published at all on such a node. Return nil when the file cannot be opened and skip those entries in the caller, which would otherwise index the interface table with nil. get_hostname() gets the same guard. Reported-by: openwrt-ai[bot] Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
openwrt-ai
left a comment
There was a problem hiding this comment.
Reviewed 2 new commits; no new issues found.
The branch was force-pushed again since my last review at 3d3e34c. git diff 3d3e34c..3c5db28 is empty — the tree is byte-identical and the base is unchanged (ec4ca5c on both sides), so nothing in the packaged content changed. The only difference is in the first commit's message: alfred: add new package gained the paragraph documenting the SUBMENU:=Wireless deviation from the feed copy.
That closes the loose end I raised last time — the deviation is now recorded in the commit message rather than only in the PR body, and it matches the diff (SUBMENU:=Wireless in define Package/alfred). alfred: handle unreadable files in bat-hosts.lua is unchanged. Both subjects still describe what their diffs do.
The open threads from the earlier reviews are untouched and still stand: the loadstring on mesh-sourced data, the get_hostname guard with no caller-side counterpart, the init script's single-instance config_foreach, and the nit: items.
CI on 3c5db28 is green — all ten build targets and all three FormalityCheck jobs. Still not verified from here: PKG_HASH and the existence of the 2026.2 tarball (downloads.open-mesh.org is unreachable in this environment), and byte-for-byte parity with the current routing-feed master (no access to openwrt/routing).
Generated by Claude Code
|
@simonwunderlich Are you ok with @openwrt-ai feedback, which was addressed? |
Adds alfred from the openwrt/routing feed — the routing packages are being moved into openwrt/packages one by one, as discussed in openwrt/routing#184.
Includes the Makefile cleanup pending in openwrt/routing#1192.
The content matches the current routing feed master, with one deliberate deviation:
SUBMENU:=Wireless, which the feed copy does not set at all. Without it the package sits ungrouped at the top of the Network menu. Wireless keeps it next to mesh11sd (#30168) and batctl (#30163), which move across with the same value. A dedicated Mesh submenu is the better home for all three and is worth adding once enough of the mesh packages have landed to justify it.Once this is merged, the package will be removed from the routing feed (a coordinated removal PR is prepared there).
Maintainer: @simonwunderlich