#!/opt/lnxall_app/bin/lua -- Created by jiaqiang.ye@lnxall.com -- Simple System Monitor for Non-openwrt system -- 2023/07/13 local bit = require 'bit' local uci = require 'uci' local cjson = require "cjson" local posix = require "posix" local invoker = require "invoker" local tasklua = require "tasklua" -- global variables local CHECK_INTERVAL = 120 -- 120 seconds local gfmt = string.format local board_name = 'unknown' -- name of board, RK3568/EM1000 local g_iccid = "/tmp/simcard_iccid" local function network_enabled(which) local netw = uci.cursor('/etc/config') local netcfg = netw:get('network', which) if netcfg ~= "interface" then netw:close(); netw = nil return false end local dis = netw:get('network', which, 'disabled') netw:close(); netw = nil if dis == nil then return true end if dis == "0" then return true end return false end local function lan_ports_contain(netdev) local netw = uci.cursor('/etc/config') local ports = netw:get('network', '@device[0]', 'ports') netw:close(); netw = nil if type(ports) ~= "table" then return false end ports = table.concat(ports, " ") if type(ports) == "string" then return string.find(ports, netdev, 1, true) end return false end local function is_rk3568() local output = invoker.readfile("/proc/device-tree/model") if not output or not string.find(output, "RK3568", 1, true) then return false end output = invoker.readfile("/tmp/sysinfo/board_name", invoker.TRIMEND) return output == 'WLOSANY_RK3568' end local function is_imx6ul() local output = invoker.readfile("/proc/cpuinfo") if not output then return false end if not string.find(output, "Freescale i.MX6", 1, true) then return false end return true end local function check_filesize(fpath, maxsize, action) local fst = posix.stat(fpath) if type(fst) ~= "table" then return false end if fst['size'] < 0 or fst['size'] >= maxsize then if type(action) == "table" then io.stdout:write(gfmt("Warning, taking action for '%s'...\n", fpath)) io.stdout:flush() invoker.invoke(0, action) return true end io.stdout:write(gfmt("Warning: removing file %s...\n", fpath)) io.stdout:flush() posix.unlink(fpath) -- just remove the file posix.sync() return true end return false end local function ping_host(ipaddr, netdev) local cmds = { "ping", "-c3", "-w12" } if type(netdev) == "string" then cmds[#cmds + 1] = string.format("-I%s", netdev) end cmds[#cmds + 1] = ipaddr local eval, output = invoker.invoke(invoker.OUTPUT + invoker.NOSTDIO, cmds) if type(output) == "string" and string.find(output, 'packet loss', 1, true) then if string.find(output, '100% packet loss', 1, true) then return false end -- io.stdout:write(string.format("[%s] Ping %s OK.\n", os.date(), ipaddr)) -- io.stdout:flush() return true end if eval == 0 then return true end return false end local function get_pinghost(cfgfile) local dhost, dport = "mqtt.lnxall.com", 3883 local filp = io.open(cfgfile, "rb") if not filp then return dhost, dport end local fild = filp:read("*a") filp:close(); filp = nil if not fild or #fild == 0 then return dhost, dport end local okay, jdat = pcall(cjson.decode, fild) if not okay or type(jdat) ~= "table" then return dhost, dport end local host = jdat["host"] local port = jdat["port"] if type(host) == "string" and type(port) == "number" then return host, port else return dhost or dport end end local function isat_time(when) local nowt = os.date("*t") return nowt["hour"] == when end local function nslookup(hostn) local pid, outfd = invoker.invoke(invoker.NOWAIT + invoker.OUTPUT, "nslookup", hostn) if type(pid) ~= "number" then return nil end while true do if invoker.waitpid(pid, true) then break end tasklua.delay(3000) end local output = invoker.readfd(outfd, 4096) if type(outfd) == "number" and outfd >= 0 then posix.close(outfd); outfd = -1 end if type(output) ~= "string" then return nil end local iptab, ipn, ans = {}, 0, false for line in string.gmatch(output, "[^\r\n]+") do if not ans then if string.find(line, "answer:", 1, true) then ans = true end else local ipv4 = string.match(line, "Address:%s+([%d%.]+)") if ipv4 and invoker.isipv4(ipv4) then ipn = ipn + 1 iptab[ipn] = ipv4 end end end if ipn == 0 then return nil end return iptab end local function entry_netping() local disc = nil -- try to read ICCID before ping while disc == nil do disc = invoker.readfile(g_iccid, invoker.TRIMEND) if disc and #disc >= 0x8 then break end disc = nil; tasklua.delay(15000) end disc = 0 local phost, pport = get_pinghost('/app/config/mqtt_server.json') if not invoker.isipv4(phost) then -- resolve address as IPv4 address while true do local ips = nslookup(phost) if ips then io.stdout:write(gfmt("hostname resolved: %s => %s\n", phost, ips[1])) io.stdout:flush() phost = ips[1] break end tasklua.delay(15000) end end local nowt = invoker.uptime() local thet = nowt + CHECK_INTERVAL while true do if ping_host(phost) or invoker.tcpcheck(phost, pport, 2000) then if disc > 0 then disc = 0 end else disc = disc + 1 io.stdout:write(string.format("Network is down, cannot ping %s: %d\n", phost, disc)) io.stdout:flush() if disc > 10 then disc = 0 end end nowt = invoker.uptime() while nowt < thet do tasklua.delay((thet - nowt) * 1000) nowt = invoker.uptime() end thet = thet + CHECK_INTERVAL if thet <= nowt then thet = nowt end end end local function entry_meminfo() local lowcount = 0 while true do local memi = invoker.readfile("/proc/meminfo") if type(memi) ~= "string" or string.len(memi) == 0 then io.stderr:write("Error, failed to read meminfo\n") io.stderr:flush() tasklua.delay(10000) break end -- get available memory size, in KBytes local avail = string.match(memi, "MemAvailable:%s+(%d+)%s+kB") if type(avail) ~= "string" then avail = -1 io.stderr:write("Error, failed to get MemAvailable!\n") io.stderr:flush() else avail = tonumber(avail) -- convert to number if not avail then avail = -1 io.stderr:write("Error, invalid MemAvailable found.\n") io.stderr:flush() end end io.stdout:write(string.format("[%s] Memory available: %d kB\n", os.date(), avail)) io.stdout:flush() -- minimum memory: 4MB if avail >= 0 then if avail < 4096 then -- flush memory cache local drop = io.open("/proc/sys/vm/drop_caches", "wb") if drop then drop:write("3\n") drop:close(); drop = nil end -- increment low-memory-counter lowcount = lowcount + 0x1 if lowcount > 5 then lowcount = 0 end elseif lowcount > 0 then lowcount = 0 -- reset low-memory-counter end end tasklua.delay(120000) end return true end local function remove_logs(pattern, howold) local logs = posix.glob(pattern) if type(logs) ~= "table" or #logs == 0 then -- io.stdout:write(gfmt("Warning, no files found for pattern: %s\n", pattern)) -- io.stdout:flush() return false end local lfiles = {} for _, file in ipairs(logs) do local fst = posix.stat(file) if type(fst) ~= "table" then posix.unlink(file) io.stderr:write(gfmt("Error, failed to stat: %s\n", file)) io.stderr:flush() elseif fst['type'] == 'regular' and type(fst['mtime']) == "number" then lfiles[file] = fst['mtime'] end end local nowtim = os.time() howold = howold * 86400 -- translate days into seconds for filog, mtime in pairs(lfiles) do if nowtim > mtime and (nowtim - mtime) >= howold then posix.unlink(filog) io.stdout:write(gfmt("Remove too old log file: %s\n", filog)) io.stdout:flush() end end posix.sync() return true end local function entry_logremove() local LR_INTERVAL = 600 -- local n0, ntim = invoker.uptime(), 0 local n1 = n0 + LR_INTERVAL io.stdout:write("Log remove task up and running...\n") io.stdout:flush() local gztab = { 'dmesg.*.gz', 'syslog.*.gz', 'mail.*.gz', 'daemon.*.gz', 'kern.*.gz', 'auth.*.gz', 'user.*.gz', 'lpr.*.gz', 'cron.*.gz', 'debug.*.gz', 'messages.*.gz' } local chktab = { 'syslog', 'mail.info', 'mail.warn', 'mail.err', 'mail.log', 'daemon.log', 'kern.log', 'auth.log', 'user.log', 'lpr.log', 'cron.log', 'debug', 'messages' } local daily = invoker.uptime() while true do for _, glop in ipairs(gztab) do remove_logs(gfmt('/var/log/%s', glop), 7) end local dorot = false for _, logf in ipairs(chktab) do if check_filesize(gfmt('/var/log/%s', logf), 268435456, { "/bin/sh", "-c", "chmod 644 /etc/logrotate.d/*" }) then dorot = true break end end n0 = invoker.uptime() if dorot and (ntim == 0 or (n0 - ntim) >= 5400) then ntim, daily = n0, n0 invoker.invoke(0, "logrotate", "-f", "/etc/logrotate.conf") invoker.invoke(0, "logrotate", "-f", "/etc/logrotate.d/rsyslog") n0 = invoker.uptime() elseif (n0 - daily) >= 86400 then -- for `daily log-rotate ntim, daily = n0, n0 invoker.invoke(0, "logrotate", "-f", "/etc/logrotate.conf") invoker.invoke(0, "logrotate", "-f", "/etc/logrotate.d/rsyslog") n0 = invoker.uptime() end while n1 > n0 do tasklua.delay((n1 - n0) * 1000) n0 = invoker.uptime() end n1 = n1 + LR_INTERVAL end end local function sort_by_mtime(f0, f1) if type(f0) ~= "string" then return false end if type(f1) ~= "string" then return false end local st0 = posix.stat(f0) local st1 = posix.stat(f1) if type(st0) ~= "table" then return false end if type(st1) ~= "table" then return false end st0, st1 = st0['mtime'], st1['mtime'] if type(st0) ~= "number" then st0 = 0 end if type(st1) ~= "number" then st1 = 0 end return st0 > st1 end local function entry_coredump() local chkival = 180 * 1000 -- Every 3 minutes local sysup = invoker.uptime() posix.unlink('/core') -- remove existing coredump if any if sysup < 180 then -- for system fresh boot, delay 3 minutes tasklua.delay((180 - sysup) * 1000) end while true do local cores = posix.glob('/var/crash/coredumps/*.core') if type(cores) == "table" then local numc, numd = #cores, 2 if numc > 1 then table.sort(cores, sort_by_mtime) end while true do numd = numd + 1 local core = cores[numd] if type(core) ~= "string" then break end io.stdout:write(gfmt("Deleting core file: %s\n", core)) io.stdout:flush() posix.unlink(core) end if numc > 5 then posix.sync() end end tasklua.delay(chkival) end end -- task disabled: local function entry_space_check() local rootp = { ["EM1000"] = "/dev/mmcblk0p3", ["RK3568"] = "/dev/root", ["RK3568_XDDL"] = "/dev/root", ["LC100"] = "/dev/root" } rootp = rootp[board_name] if not rootp then io.stdout:write(gfmt("Error, cannot find root partion for %s\n", board_name)) io.stdout:flush() while true do tasklua.delay(900 * 1000) end end if invoker.uptime() < 180 then tasklua.delay(120000) end while true do local percent = 0 local okay, output = invoker.invoke(invoker.OUTPUT, "df") if okay == 0 and type(output) == "string" then for line in string.gmatch(output, "[^\r\n]+") do if string.find(line, rootp, 1, true) then local free = string.match(line, "(%d+)%%") if free then free = tonumber(free) end if not free then io.stderr:write("Disk free percentage not found.\n") io.stderr:flush() else percent = free end break end end end if percent >= 95 then local rmlist = {} local chkfiles, restart = {}, false io.stdout:write(gfmt("Warning, disk storage memory low: %d%%\n", percent)) io.stdout:flush() chkfiles[#chkfiles + 1] = "/var/log/auth.log" chkfiles[#chkfiles + 1] = "/var/log/daemon.log" chkfiles[#chkfiles + 1] = "/var/log/debug" chkfiles[#chkfiles + 1] = "/var/log/messages" chkfiles[#chkfiles + 1] = "/var/log/user.log" chkfiles[#chkfiles + 1] = "/var/log/syslog" for _, cf in ipairs(chkfiles) do if check_filesize(cf, 268435456, { "rm", "-rf", cf }) then restart = true rmlist[#rmlist + 1] = cf end local idx = 0 while idx <= 5 do local fpath = gfmt("%s.%d", cf, idx) if check_filesize(fpath, 268435456, { "rm", "-rf", fpath }) then restart = true rmlist[#rmlist + 1] = fpath end idx = idx + 1 end end if restart then invoker.invoke(invoker.NOSTDIO, "systemctl", "stop", "rsyslog") invoker.invoke(invoker.NOSTDIO, "systemctl", "stop", "syslog.socket") tasklua.delay(3000); posix.sync() -- delay 3 seconds local dflog = "/etc/free-space-remove.log" check_filesize(dflog, 256 * 1024, { "rm", "-rf", dflog }) local filp = io.open(dflog, "ab") if filp then filp:write(gfmt("%s: Removing log files...\n", os.date())) filp:write(table.concat(rmlist, "\n")) filp:write("\n=========================================\n") filp:close() end invoker.invoke(invoker.NOSTDIO, "systemctl", "restart", "rsyslog") invoker.invoke(invoker.NOSTDIO, "systemctl", "restart", "syslog.socket") end end tasklua.delay(600000) -- delay 600 seconds end end local function check_board_name() local output = invoker.readfile("/proc/device-tree/model") if type(output) ~= "string" then io.stderr:write("Error, cannot read device model for board_name!\n") io.stderr:flush() return false end if string.find(output, "AM654", 1, true) then board_name = "EM1000" return true end if string.find(output, "RK3568", 1, true) then board_name = "RK3568" return true end if string.find(output, "XDDL3568", 1, true) then board_name = "RK3568_XDDL" return true end output = invoker.readfile("/proc/cpuinfo") if type(output) ~= "string" then output = "" end if string.find(output, "i.MX6 Ultralite", 1, true) then board_name = "LC100" return true end io.stderr:write(gfmt("Error, invalid device model: %s\n", output)) io.stderr:flush() return false end local function count_true(ctab, maxn) local rval = 0 for idx = 1, maxn do if ctab[idx] then rval = rval + 1 end end return rval end -- RK3568, W5500 work as br-lan local function entry_brlan_reset() local tim0 = invoker.uptime() if tim0 < 50 then tasklua.delay((50 - tim0) * 1000) end local okfile, delay_sec = "/tmp/br-lan-ping.ok", 40 if posix.access(okfile) == 0 then delay_sec = 320 end local lanen = network_enabled('lan') and lan_ports_contain('eth2') while not lanen do -- io.stdout:write("Warning, LAN disabled!!!\n") -- io.stdout:flush() tasklua.delay(60000) lanen = network_enabled('lan') and lan_ports_contain('eth2') end tim0 = invoker.uptime() local itercnt, reset_cnt = 0, 0 local okcnt = { [1] = true, [2] = true, [3] = true, [4] = true } while lanen do local arplist, anyok = invoker.readfile("/proc/net/arp"), false if type(arplist) ~= "string" then io.stderr:write("Error, failed to read ARP file\n") io.stderr:flush() else local iplist = {} for line in string.gmatch(arplist, "[^\r\n]+") do if string.find(line, "br-lan", 1, true) then local ip = string.match(line, "^([%d%.]+)") if ip then iplist[#iplist + 1] = ip end end end for _, ip in pairs(iplist) do local okay, out = invoker.invoke(invoker.OUTPUT, "ping", "-A", "-c8", "-w4", "-Ibr-lan", ip) if okay == 0 and type(out) == "string" and string.find(out, "packet loss", 1, true) then if string.find(out, "%s0%% packet loss") then anyok = true break end end end end okcnt[1], okcnt[2], okcnt[3], okcnt[4] = okcnt[2], okcnt[3], okcnt[4], anyok itercnt = itercnt + 1 anyok = count_true(okcnt, 0x4) if anyok >= 3 and itercnt >= 4 then check_filesize(okfile, 524288) local fp = io.open(okfile, "ab") if fp then fp:write(gfmt("%s Ping okay on br-lan: %d, okcnt: %d\n", os.date(), reset_cnt, anyok)) fp:close(); fp = nil end delay_sec = 320 -- break end if anyok == 0 then reset_cnt = reset_cnt + 1 -- lock file to avoid confliction, refer to script: /opt/lnxall_app/bin/netuci: local lockfd = invoker.waitlock('/tmp/.netuci-lock') invoker.invoke(invoker.NOSTDIO, "ip", "link", "set", "dev", "eth2", "up") invoker.invoke(0, "/bin/bash", "-c", "echo -e -n '\\x00\\x00\\x55\\x00\\x00\\x00\\x55\\x00' | ethtool -E eth2 magic 0x0 offset 0x0 length 0x8") invoker.invoke(invoker.NOSTDIO, "ip", "link", "set", "dev", "eth2", "down") invoker.invoke(invoker.NOSTDIO, "nmcli", "con", "del", "lan") invoker.invoke(invoker.NOSTDIO, "nmcli", "con", "del", "lxbr_eth2") invoker.invoke(invoker.NOSTDIO, "nmcli", "con", "del", "lxbr_wlan0") tasklua.delay(3000) -- important: do not forget release netuci file lock: if type(lockfd) == "number" then invoker.closefd(lockfd) posix.close(lockfd) -- do it again, problem? end if (reset_cnt % 10) == 0 then invoker.invoke(invoker.NOSTDIO, "systemctl", "restart", "NetworkManager") tasklua.delay(3000) end local pid = invoker.invoke(invoker.NOWAIT + invoker.NOSTDIO, "netuci") while pid do tasklua.delay(1000) local term = invoker.waitpid(pid, true) if term == nil or term then pid = nil end end itercnt = 0 -- reset iteration count as zero for idx = 1, 4 do okcnt[idx] = true end end local tim1 = invoker.uptime() if tim1 >= 4000 and delay_sec < 120 then delay_sec = 120 end tim0 = tim0 + delay_sec while tim1 < tim0 do tasklua.delay((tim0 - tim1) * 1000) tim1 = invoker.uptime() end lanen = network_enabled('lan') and lan_ports_contain('eth2') end return false end local function check_ttyACM(sysp, slink) local gltp = posix.glob(sysp) if not gltp or type(gltp[1]) ~= "string" then io.stderr:write(gfmt("Error, failed to check %s\n", sysp)) io.stderr:flush() return false end local tdev = string.match(gltp[1], "/([^/]+)$") if not tdev then return false end tdev = "/dev/" .. tdev if posix.access(tdev) ~= 0 then io.stderr:write(gfmt("Error, device not found: %s\n", tdev)) io.stderr:flush() return false end local rdev = invoker.readlink(slink) if rdev ~= tdev then posix.unlink(slink) if invoker.invoke(0, "ln", "-sv", tdev, slink) == 0 then return true end io.stderr:write(gfmt("Error, failed to symlink %s => %s\n", tdev, slink)) io.stderr:flush() return false end return true end local function entry_check_electron() local now = invoker.uptime() if now < 60 then tasklua.delay((60 - now) * 1000) end local chkcnt, errcnt = 0, 0 now = invoker.uptime() while true do if not invoker.tcpcheck("127.0.0.1", 80, 1500) then errcnt = errcnt + 1 elseif errcnt > 0 then errcnt = 0 end chkcnt = chkcnt + 1 if (chkcnt % 2) == 0 and errcnt >= 2 then local logfile = "/app/electron_restart.log" check_filesize(logfile, 0x200000) local logh = io.open(logfile, "ab") if logh then logh:write(gfmt("%s: restarting lightdm due to 127.0.0.1:80 unavailable.\n", os.date())) logh:close(); logh = nil end invoker.invoke(invoker.NOSTDIO, "systemctl", "restart", "lightdm") end now = now + 30 local tim = invoker.uptime() while tim < now do tasklua.delay((now - tim) * 1000) tim = invoker.uptime() end end return true end local function entry_check_ch344() local errlist, chknum = {}, 0 while true do local found = false local _, output = invoker.invoke(invoker.OUTPUT, "/bin/sh", "-c", "dmesg | grep -E -e 'usb usb1-port1: disabled by hub.+re-enabling'") if type(output) ~= "string" then output = "" end for line in string.gmatch(output, "[^\r\n]+") do if not errlist[line] then found = true errlist[line] = true end end chknum = chknum + 1 if found then io.stdout:write("Warning, found new CH344 USB reconnect, restarting...\n") io.stdout:flush() end if found or bit.band(chknum, 0xF) == 0 then local symlinks = { "/dev/ttylnx3", "/dev/ttylnx1", "/dev/ttylnx2" } local redo, fmtstr = false, "/sys/devices/platform/fd800000.usb/usb1/1-1/1-1:1.%d/tty/*" for idx = 0, 2 do local sp = gfmt(fmtstr, idx * 2) if not check_ttyACM(sp, symlinks[idx + 1]) then redo = true break end end if redo then -- reconnect CH344 USB-TO-SERIAL CONVERTER invoker.invoke(invoker.NOSTDIO, "usbreset", "1a86:55d5") tasklua.delay(1500) for idx = 0, 2 do local sp = gfmt(fmtstr, idx * 2) check_ttyACM(sp, symlinks[idx + 1]) end end end tasklua.delay(5000) end end local function entry_imx6ul_ch344() local uptim = invoker.uptime() if uptim < 120 then tasklua.delay((120 - uptim) * 1000) end -- /dev/ttylnx1 -> /dev/ttyACM1 -- /dev/ttylnx2 -> /dev/ttyACM2 -- /dev/ttylnx3 -> /dev/ttyACM0 -- /dev/ttylnx4 -> /dev/ttyACM3 local symlinks = { "/dev/ttylnx3", "/dev/ttylnx1", "/dev/ttylnx2", "/dev/ttylnx4" } local fmtstr = "/sys/devices/platform/soc/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb1/1-1/1-1.1/1-1.1:1.%d/tty/ttyACM*" local una = posix.uname() if type(una) == "string" and string.find(una, "5.4.70", 1, true) then fmtstr = "/sys/devices/soc0/soc/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.%d/tty/ttyACM*" end while true do for idx = 0, 3 do local sp = gfmt(fmtstr, idx * 2) if not check_ttyACM(sp, symlinks[idx + 1]) then io.stderr:write(gfmt("Error, failed to check symlink for '%s'\n", symlinks[idx + 1])) io.stderr:flush() end end tasklua.delay(60000) end end local function load_can_bitrate(pcfg, candev) if not candev then return nil end local chdl = io.open(pcfg, "rb") if not chdl then return nil end local dcfg = chdl:read("*a") chdl:close(); chdl = nil local okay, jcfg = pcall(cjson.decode, dcfg) if not okay then io.stderr:write(gfmt("Error, failed to decode as json: %s\n", pcfg)) io.stderr:flush() return nil end local empty = {} local json = jcfg["dev_cfg"] if type(json) ~= "table" then json = empty end json = json["can"] if type(json) ~= "table" then json = empty end json = json["attr"] if type(json) ~= "table" then json = empty end local i = 0 while true do i = i + 1 local cancfg = json[i] if type(cancfg) ~= "table" then break end if cancfg["name"] == candev then local bitr = cancfg["baud"] if type(bitr) ~= "number" then return nil end return bitr end end return nil end -- refer to `check_can function from `/lib/dyiot/bin/supervisor.sh -- is this really necessary? local function taskentry_candev() local devs = invoker.uptime() if devs < 120 then tasklua.delay((120 - devs) * 1000) end devs = posix.glob('/sys/class/net/can*') if not devs then devs = {} io.stderr:write("Error, no can device found!\n") io.stderr:flush() end local errcnt = {} local cfgmap = { ["can0"] = "CAN1", ["can1"] = "CAN2" } local iflags, canbit = invoker.CLOSEFD + invoker.NOSTDIO, 500000 while true do tasklua.delay(8000) for _, cdev in ipairs(devs) do local stat = invoker.readfile(cdev .. '/operstate', invoker.TRIMEND) if stat == 'down' then local candev, ecnt = string.match(cdev, "/([^/]+)$"), 0 if candev then ecnt = errcnt[candev] or 0 ecnt = ecnt + 1 errcnt[candev] = ecnt end if ecnt >= 3 then io.stdout:write("try to bring up " .. candev .. "...\n") io.stdout:flush() invoker.invoke(iflags, 'ifconfig', candev, 'down') tasklua.delay(1000) local bitrate = load_can_bitrate('/app/config/port_config.json', cfgmap[candev]) if not bitrate then bitrate = canbit else canbit = bitrate end if invoker.invoke(iflags, "ip", "link", "set", "dev", candev, "type", "can", "bitrate", tostring(bitrate), "triple-sampling", "on") ~= 0 then invoker.invoke(iflags, "ip", "link", "set", "dev", candev, "type", "can", "bitrate", tostring(bitrate)) end errcnt[candev] = nil invoker.invoke(iflags, 'ifconfig', candev, 'up') end end end end return true end -- check whether we should start task `entry_brlan_reset local function check_w5500_yt6801() if invoker.readfile("/sys/bus/pci/devices/0000:01:00.0/vendor", invoker.TRIMEND) == "0x1f0a" then -- device has YT6801 PCIe network card support return false end local ofnode = invoker.readlink("/sys/bus/spi/devices/spi3.0/of_node") if not ofnode or not string.find(ofnode, "eth_w550") then -- No W5500 driver found return false end tasklua.taskadd(entry_brlan_reset, true) return true end local function fix_debian_ntp() local wrap = "/usr/lib/ntp/ntp-systemd-wrapper" if posix.access(wrap) ~= 0 then return false end local cont = invoker.readfile(wrap) if type(cont) ~= "string" or #cont == 0 then return false end local exel = "exec $DAEMON -p $PIDFILE $NTPD_OPTS" if not string.find(cont, exel, 1, true) then return false end local exef = exel .. " -I lo -L" if string.find(cont, exef, 1, true) then return false end invoker.invoke(0, "sed", "-e", gfmt("s#%s.+$#%s#g", exel, exef), "-i", wrap) io.stdout:write("Warning, NTP-server wrapper modified.\n") io.stdout:flush() invoker.invoke(invoker.NOSTDIO, "systemctl", "restart", "ntp") return true end local function disable_busyboxntp() local pinfo = invoker.readfile("/proc/cpuinfo") if type(pinfo) ~= "string" then pinfo = "what the fuck" end if string.find(pinfo, "Freescale i.MX6", 1, true) then invoker.invoke(invoker.NOSTDIO, "systemctl", "stop", "busyboxdhcpd") invoker.invoke(invoker.NOSTDIO, "systemctl", "disable", "busyboxdhcpd") end end local function fix_debian_lightdm() local okay, output = invoker.invoke(invoker.NOSTDIO + invoker.OUTPUT, "systemctl", "status", "lightdm") if okay ~= 0 then return false end if type(output) ~= "string" then output = "" end if not string.find(output, "vendor preset: enabled", 1, true) then return false end if string.find(output, "indirect;", 1, true) then return false end if not string.find(output, "static;", 1, true) then return false end io.stdout:write("Restoring lightdm service...\n") io.stdout:flush() invoker.invoke(invoker.NOSTDIO, "systemctl", "unmask", "lightdm.service") invoker.msleep(250) invoker.invoke(invoker.NOSTDIO, "systemctl", "daemon-reload") invoker.msleep(250) invoker.invoke(invoker.NOSTDIO, "dpkg-reconfigure", "lightdm"); return true end local function check_internal_watchdog() local okay, output = invoker.invoke(invoker.OUTPUT, "sh", "-c", "date '+%s' --date=\"$(uname -v | sed -r -e 's/^#.+SMP //g')\" ; exit 0") if okay ~= 0 or type(output) ~= "string" or #output == 0 then io.stderr:write("Error, failed to failed to get kernel build date\n") io.stderr:flush() return false end local rdate = tonumber(output) if not rdate or rdate < 1730267430 then io.stdout:write("Error, OLD RK3568 kernel without internal watchdog, do nothing\n") io.stdout:flush() return false end okay, output = invoker.invoke(invoker.OUTPUT, "systemctl", "status", "watchdog") if type(output) ~= "string" then output = "" end if okay == 0 and string.find(output, "active (running)", 1, true) then io.stdout:write("Disabling QYWDT...\n") io.stdout:flush() invoker.invoke(0, "systemctl", "stop", "qywdt") invoker.invoke(0, "systemctl", "disable", "qywdt") else io.stdout:write("Do not stop QYWDT!!!\n") io.stdout:flush() end end local function lsusb_has(usbid) local okay, output = invoker.invoke(invoker.OUTPUT, "lsusb") if okay == 0 and type(output) == "string" and string.find(output, usbid, 1, true) then return true end return false end local function get_md5sum(filp) local filst = posix.stat(filp) if not filst then return "" end local eval, output = invoker.invoke(invoker.OUTPUT + invoker.NOSTDIO, "md5sum", filp) if eval ~= 0 or type(output) ~= "string" or #output == 0 then return "" end local md5sum = string.match(output, "^(%x+)") if not md5sum then return "" end return md5sum end local function rk3568_touchscreen_fix() local refresh_sh = "/opt/lnxall_app/bin/hdmi-display.sh" local calconf = "/etc/X11/xorg.conf.d/99-calibration.conf" local uptime = invoker.uptime() if uptime < 90 then tasklua.delay((90 - uptime) * 1000) end while not (lsusb_has('222a:0001') or lsusb_has('27c6:0816')) do tasklua.delay(600 * 1000) end invoker.invoke(0, "/bin/bash", refresh_sh) local conf_md5 = get_md5sum(calconf) local _, hdmi_res = invoker.invoke(invoker.OUTPUT, "bash", "-c", "DISPLAY=:0 xrandr | grep -e 'HDMI-1 connected'") while lsusb_has('222a:0001') or lsusb_has('27c6:0816') do local _, new_res = invoker.invoke(invoker.OUTPUT, "bash", "-c", "DISPLAY=:0 xrandr | grep -e 'HDMI-1 connected'") if new_res ~= hdmi_res then io.stderr:write("Updating touchscreen calibration configuration...\n") io.stderr:flush() invoker.invoke(0, "/bin/bash", refresh_sh) conf_md5 = get_md5sum(calconf) _, hdmi_res = invoker.invoke(invoker.OUTPUT, "bash", "-c", "DISPLAY=:0 xrandr | grep -e 'HDMI-1 connected'") end tasklua.delay(120 * 1000) end return true end local function main_func() invoker.setname('SYSMONINTOR') check_board_name() tasklua.taskadd(entry_meminfo, true) tasklua.taskadd(entry_netping, true) tasklua.taskadd(entry_logremove, true) tasklua.taskadd(entry_coredump, true) tasklua.taskadd(entry_space_check, true) if is_rk3568() then tasklua.taskadd(entry_check_ch344, true) end if posix.access("/usr/share/lnxallinst/electron/electron") == 0 then tasklua.taskadd(entry_check_electron, true) end if posix.access('/sys/class/net/can0') == 0 or posix.access('/sys/class/net/can1') == 0 then tasklua.taskadd(taskentry_candev, true) end check_w5500_yt6801() fix_debian_ntp() disable_busyboxntp() if is_rk3568() then fix_debian_lightdm() check_internal_watchdog() tasklua.taskadd(rk3568_touchscreen_fix, true) end if is_imx6ul() and lsusb_has("1a86:8091") then tasklua.taskadd(entry_imx6ul_ch344, true) end while true do if not tasklua.taskloop(900000) then break end end end main_func(arg[1]) os.exit(1)