#!/opt/lnxall_app/bin/lua -- created by jiaqiang.ye@lnxall.com -- Simple command-line utility for EMS2.0 packapp -- 2024/08/30 local cjson = require "cjson" local posix = require "posix" local invoker = require "invoker" local gfmt = string.format -- global variables local g_packdir = '/root/.packapp' local g_packapp = '/root/.packapp.tar.gz' local g_packapp_h, g_ipaddr_h = nil, "127.0.0.1" local g_ipaddr, g_gwsn, g_cmd, g_4arg = nil, nil, nil, nil local g_upinfo = "/tmp/.packapp-info.json" local g_uplock = "/tmp/.packapp-uplock.txt" local g_sshcmd = { "sshpass", "-p", "lnxall123", "ssh", '-oStrictHostKeyChecking=no', '-oUserKnownHostsFile=/dev/null', } local function help() local err = io.stderr err:write("Usage: packapp IPADDR GWSN start|stop|query|restart|upgrade|upgraden\n") err:write("Examples:\n") err:write(" packapp IPADDR GWSN start # 开始EMS/LC打包\n") err:write(" packapp IPADDR GWSN stop # 中止EMS/LC打包\n") err:write(" packapp IPADDR GWSN query # 查询EMS/LC打包状态\n") err:write(" packapp IPADDR GWSN restart # 重启从机上的EMS/EMU服务\n") err:write(" packapp IPADDR GWSN reboot # 重启从机网关\n") err:write(" packapp IPADDR GWSN upgrade path/to/ELF-PACKAGE # 复制ELF包到从机并在从机上安装运行\n") err:write(" packapp IPADDR GWSN upgraden path/to/ELF-PACKAGE # 复制ELF包到从机并在从机上安装运行(带-n运行)\n") err:flush() end local function load_json(filp) local filh = io.open(filp, "rb") if not filh then return nil end local fild = filh:read("*a") filh:close(); filh = nil if type(fild) ~= "string" or #fild == 0 then return nil end local okay, jdat = pcall(cjson.decode, fild) if not okay then return nil end if type(jdat) ~= "table" then return nil end return jdat end local function validate_args(arg0, arg1, arg2, arg3, arg4) g_ipaddr, g_gwsn, g_cmd = arg1, arg2, arg3 if type(g_ipaddr) ~= "string" then g_ipaddr = 'unknown' end if not invoker.isipv4(g_ipaddr) then io.stderr:write(gfmt("ERROR: invalid IPv4 address specified: %s\n", g_ipaddr)) io.stderr:flush() if arg1 == nil or arg1 == "-h" or arg1 == "--help" then help() end return false end if type(g_gwsn) ~= "string" then g_gwsn = "" end if #g_gwsn == 0 then io.stderr:write(gfmt("ERROR: invalid Gateway SN specified: %s\n", g_gwsn)) io.stderr:flush() return false end if g_cmd ~= "start" and g_cmd ~= "stop" and g_cmd ~= "query" and g_cmd ~= "reboot" and g_cmd ~= "restart" and g_cmd ~= "upgrade" and g_cmd ~= "upgraden" then if type(g_cmd) ~= "string" then g_cmd = "" end io.stderr:write(gfmt("ERROR: invalid command for packapp: %s\n", g_cmd)) io.stderr:flush() return false end g_4arg = arg4 if type(arg0) == "string" then arg0 = invoker.realpath(arg0) end if type(arg0) == "string" then arg0 = string.match(arg0, "^(.+)/") end if type(arg0) ~= "string" or posix.access(arg0) ~= 0 then io.stderr:write("ERROR: packapp script not found.\n") io.stderr:flush() return false end g_packapp_h = gfmt("%s/packapp.tar.gz", arg0) -- initialize environment variables posix.setenv("EMS_IP") posix.setenv("SSH_CLIENT") posix.setenv("SSH_CONNECTION") invoker.invoke(invoker.NOSTDIO, "mkdir", "-p", "/opt/elect_resources/resources/app/web/dist/exdata") return true end local function shell_cmd(flag, cmdline) if type(flag) ~= "number" then flag = invoker.NOSTDIO + invoker.OUTPUT end local ok, out = invoker.invoke(flag, "/bin/sh", "-c", cmdline) return ok, out end local function scp_cmd(flag, cmdlist) if #cmdlist ~= 0x2 then io.stdout:write("ERROR: invalid number of argument for scp.\n") io.stdout:flush() return nil end local list, nl = {}, 1 while type(g_sshcmd[nl]) == "string" do if g_sshcmd[nl] == "ssh" then list[nl] = "scp" else list[nl] = g_sshcmd[nl] end nl = nl + 1 end list[nl] = "-r"; nl = nl + 1 list[nl] = cmdlist[1]; nl = nl + 1 list[nl] = gfmt("root@%s:%s", g_ipaddr, cmdlist[2]) if type(flag) ~= "number" then flag = invoker.NOSTDIO + invoker.OUTPUT end local ok, out = invoker.invoke(flag, list) return ok, out end local function ssh_cmd(flag, cmdlist) local list, nl = {}, 1 while type(g_sshcmd[nl]) == "string" do list[nl] = g_sshcmd[nl] nl = nl + 1 end list[nl] = "root@" .. g_ipaddr nl = nl + 1 for _, cmd in ipairs(cmdlist) do list[nl] = cmd nl = nl + 1 end if type(flag) ~= "number" then flag = invoker.NOSTDIO + invoker.OUTPUT end local ok, out = invoker.invoke(flag, list) return ok, out end -- check whether we can upgrade given GWSN local function check_upgrade() local lfd = invoker.waitlock(g_uplock) if not lfd then io.stderr:write("ERROR: failed to acquire file lock.\n") io.stderr:flush() lfd = -1 end local jdat = load_json(g_upinfo) if type(jdat) == "table" then jdat = jdat["upgrade"] end if type(jdat) ~= "table" then posix.close(lfd) return true end local lastup = jdat[g_gwsn] if not lastup then posix.close(lfd) return true end if (lastup + 30) >= invoker.uptime() then posix.close(lfd) return false end posix.close(lfd) return true end local function write_upgrade_time(when) local lfd = invoker.waitlock(g_uplock) if not lfd then io.stderr:write("ERROR: failed to acquire file lock.\n") io.stderr:flush() lfd = -1 end local oldi = load_json(g_upinfo) if not oldi then oldi = {} end local upin = oldi["upgrade"] if type(upin) ~= "table" then upin = {} end if not when then when = invoker.uptime() end upin[g_gwsn] = when oldi["upgrade"] = upin local uph = io.open(g_upinfo, "wb") if uph then uph:write(cjson.encode(oldi)) uph:close(); uph = nil end posix.close(lfd) return true end local function do_sysupgrade(hasn) if type(g_4arg) ~= "string" or posix.access(g_4arg) ~= 0 then io.stderr:write("ERROR: upgrade ELF file does not exist.\n") io.stderr:flush() return false end if not check_upgrade() then io.stderr:write(gfmt("ERROR: cannot upgrade within 30 seconds: %s\n", g_gwsn)) io.stderr:flush() return false end io.stdout:write(gfmt("INFO: copying ELF package for %s...\n", g_gwsn)) io.stdout:flush() if g_ipaddr == g_ipaddr_h then if shell_cmd(nil, gfmt("cp -v '%s' /root/.LNXALL_APP.elf", g_4arg)) ~= 0 then io.stderr:write(gfmt("ERROR, failed to copy %s\n", g_4arg)) io.stderr:flush() return false end else -- target NOT EMS device -- copy ELF package via `scp if scp_cmd(invoker.NOSTDIO, { [1] = g_4arg, [2] = "/root/.LNXALL_APP.elf" }) ~= 0 then io.stderr:write(gfmt("ERROR: failed to copy %s to %s\n", g_4arg, g_gwsn)) io.stderr:flush() return false end end local okay = nil write_upgrade_time() local cmd = gfmt("%s/%s %s %s", g_packdir, "cmd_upgrade.sh", g_gwsn, hasn and "-n" or "") if g_ipaddr == g_ipaddr_h then okay = shell_cmd(0, cmd) else okay = ssh_cmd(0, { [1] = cmd }) end if okay ~= 0 then okay = 3 end os.exit(okay) end local function check_packapp() if not invoker.tcpcheck(g_ipaddr, 22, 2500) then io.stderr:write(gfmt("ERROR: gateway %s not online: %s\n", g_ipaddr, g_gwsn)) io.stderr:flush() os.exit(2) end local md50, md51 = nil, nil local okay, output = invoker.invoke(invoker.NOSTDIO + invoker.OUTPUT, "md5sum", g_packapp_h) if okay == 0 and type(output) == "string" then md50 = string.match(output, "^([%d%x]+)") end if type(md50) ~= "string" or #md50 ~= 32 then io.stderr:write(gfmt("ERROR: cannot get md5sum for %s\n", g_packapp_h)) io.stderr:flush() os.exit(3) end local tried = false if g_ipaddr == g_ipaddr_h then while true do okay, output = shell_cmd(nil, gfmt("[ -d %s ] && md5sum %s", g_packdir, g_packapp)) if okay ~= 0 then output = "" end md51 = string.match(output, "^([%d%x]+)") if (md51 == md50 or tried) and posix.access(g_packdir) == 0 then break end tried = true invoker.invoke(invoker.NOSTDIO, "/bin/sh", "-c", gfmt("rm -rf %s %s", g_packdir, g_packapp)) invoker.invoke(invoker.NOSTDIO, "cp", "-f", g_packapp_h, g_packapp) invoker.invoke(invoker.NOSTDIO, "/bin/sh", "-c", string.format("cd /root && tar -zxf %s", g_packapp)) end if md51 ~= md50 then io.stderr:write(gfmt("ERROR: cannot copy packapp to %s, %s\n", g_gwsn, g_ipaddr)) io.stderr:flush() os.exit(4) end return true end while true do okay, output = ssh_cmd(nil, { [1] = gfmt("md5sum %s", g_packapp) }) if okay ~= 0 then output = "" end md51 = string.match(output, "^([%d%x]+)") if md51 == md50 or tried then break end tried = true ssh_cmd(nil, { [1] = gfmt("rm -rf %s %s", g_packapp, g_packdir ) }) if scp_cmd(invoker.NOSTDIO, { [1] = g_packapp_h, [2] = g_packapp }) == 0 then ssh_cmd(nil, { [1] = gfmt("cd /root && tar -zxf %s", g_packapp) }) end end if md51 ~= md50 then io.stderr:write(gfmt("ERROR: cannot copy packapp to %s, %s\n", g_gwsn, g_ipaddr)) io.stderr:flush() os.exit(5) end return true end local function mainfunc_op() local cmd = nil if g_ipaddr == g_ipaddr_h then posix.setenv("EMS_IP", g_ipaddr_h) end if g_cmd == "start" then cmd = gfmt("%s/cmd_start.sh %s", g_packdir, g_gwsn) elseif g_cmd == "stop" then cmd = gfmt("%s/cmd_stop.sh %s", g_packdir, g_gwsn) elseif g_cmd == "query" then cmd = gfmt("%s/cmd_query.sh %s", g_packdir, g_gwsn) elseif g_cmd == "reboot" then cmd = gfmt("%s/cmd_reboot.sh %s", g_packdir, g_gwsn) elseif g_cmd == "restart" then cmd = gfmt("%s/cmd_restart.sh %s", g_packdir, g_gwsn) elseif g_cmd == "upgrade" then return do_sysupgrade(false) elseif g_cmd == "upgraden" then return do_sysupgrade(true) else io.stderr:write(gfmt("ERROR: invalid command: %s\n", g_cmd)) io.stderr:flush() os.exit(5) end local okay, output = nil, nil if g_ipaddr == g_ipaddr_h then okay, output = shell_cmd(nil, cmd) else okay, output = ssh_cmd(nil, { [1] = cmd }) end if type(okay) ~= "number" or okay ~= 0 then if type(okay) ~= "number" then okay = 90 end if type(output) == "string" then io.stdout:write(output) else io.stdout:write(gfmt("ERROR: command '%s' has failed with: %d\n", g_cmd, okay)) end io.stdout:flush() os.exit(okay) end if type(output) ~= "string" or #output == 0 then output = gfmt("INFO: command %s executed successfully, without output.", g_cmd) end io.stdout:write(output) io.stdout:flush() os.exit(0) end if not validate_args(arg[0], arg[1], arg[2], arg[3], arg[4]) then os.exit(1) end check_packapp() mainfunc_op() os.exit(2)