b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 1 | #!/usr/bin/lua |
| 2 | |
| 3 | local type_id = 64 -- bat-hosts |
| 4 | |
| 5 | function get_hostname() |
| 6 | local hostfile = io.open("/proc/sys/kernel/hostname", "r") |
| 7 | local ret_string = hostfile:read() |
| 8 | hostfile:close() |
| 9 | return ret_string |
| 10 | end |
| 11 | |
| 12 | function get_interfaces_names() |
| 13 | local ret = {} |
| 14 | |
| 15 | for name in io.popen("ls -1 /sys/class/net/"):lines() do |
| 16 | table.insert(ret, name) |
| 17 | end |
| 18 | |
| 19 | return ret |
| 20 | end |
| 21 | |
| 22 | function get_interface_address(name) |
| 23 | local addressfile = io.open("/sys/class/net/"..name.."/address", "r") |
| 24 | local ret_string = addressfile:read() |
| 25 | addressfile:close() |
| 26 | return ret_string |
| 27 | end |
| 28 | |
| 29 | |
| 30 | local function generate_bat_hosts() |
| 31 | -- get hostname and interface macs/names |
| 32 | -- then return a table containing valid bat-hosts lines |
| 33 | local n, i |
| 34 | local ifaces, ret = {}, {} |
| 35 | |
| 36 | local hostname = get_hostname() |
| 37 | |
| 38 | for n, i in ipairs(get_interfaces_names()) do |
| 39 | local address = get_interface_address(i) |
| 40 | if not ifaces[address] then ifaces[address] = i end |
| 41 | end |
| 42 | |
| 43 | for mac, iname in pairs(ifaces) do |
| 44 | if mac:match("^%x%x:%x%x:%x%x:%x%x:%x%x:%x%x$") and not mac:match("00:00:00:00:00:00") then |
| 45 | table.insert(ret, mac.." "..hostname.."_"..iname.."\n") |
| 46 | end |
| 47 | end |
| 48 | |
| 49 | return ret |
| 50 | end |
| 51 | |
| 52 | local function publish_bat_hosts() |
| 53 | -- pass a raw chunk of data to alfred |
| 54 | local fd = io.popen("alfred -s " .. type_id, "w") |
| 55 | if fd then |
| 56 | local ret = generate_bat_hosts() |
| 57 | if ret then |
| 58 | fd:write(table.concat(ret)) |
| 59 | end |
| 60 | fd:close() |
| 61 | end |
| 62 | end |
| 63 | |
| 64 | local function write_bat_hosts(rows) |
| 65 | local content = { "### /tmp/bat-hosts generated by alfred-bat-hosts\n", |
| 66 | "### /!\\ This file is overwritten every 5 minutes /!\\\n", |
| 67 | "### (To keep manual changes, replace /etc/bat-hosts symlink with a static file)\n" } |
| 68 | |
| 69 | -- merge the chunks from all nodes, de-escaping newlines |
| 70 | for _, row in ipairs(rows) do |
| 71 | local node, value = unpack(row) |
| 72 | table.insert(content, "# Node ".. node .. "\n") |
| 73 | table.insert(content, value:gsub("\x0a", "\n") .. "\n") |
| 74 | end |
| 75 | |
| 76 | -- write parsed content down to disk |
| 77 | local fd = io.open("/tmp/bat-hosts", "w") |
| 78 | if fd then |
| 79 | fd:write(table.concat(content)) |
| 80 | fd:close() |
| 81 | end |
| 82 | |
| 83 | -- try to make a symlink in /etc pointing to /tmp, |
| 84 | -- if it exists, ln will do nothing. |
| 85 | os.execute("ln -ns /tmp/bat-hosts /etc/bat-hosts 2>/dev/null") |
| 86 | end |
| 87 | |
| 88 | local function receive_bat_hosts() |
| 89 | -- read raw chunks from alfred, convert them to a nested table and call write_bat_hosts |
| 90 | -- "alfred -r" can fail in slave nodes (returns empty stdout), so: |
| 91 | -- check output is not null before writing /tmp/bat-hosts, and retry 3 times before giving up. |
| 92 | for n = 1, 3 do |
| 93 | local fd = io.popen("alfred -r " .. type_id) |
| 94 | --[[ this command returns something like |
| 95 | { "54:e6:fc:b9:cb:37", "00:11:22:33:44:55 ham_wlan0\x0a00:22:33:22:33:22 ham_eth0\x0a" }, |
| 96 | { "90:f6:52:bb:ec:57", "00:22:33:22:33:23 spam\x0a" }, |
| 97 | ]]-- |
| 98 | |
| 99 | if fd then |
| 100 | local output = fd:read("*a") |
| 101 | fd:close() |
| 102 | if output and output ~= "" then |
| 103 | assert(loadstring("rows = {" .. output .. "}"))() |
| 104 | write_bat_hosts(rows) |
| 105 | break |
| 106 | end |
| 107 | end |
| 108 | end |
| 109 | end |
| 110 | |
| 111 | publish_bat_hosts() |
| 112 | receive_bat_hosts() |