#!/opt/lnxall_app/bin/lua

-- Created by jiaqiang.ye@lnxall.com
-- Simple `/lib/dyiot/initd/XXX script
-- 2023/04/13

-- load external modules
local ubus = require "ubus"
local cjson = require "cjson"
local posix = require "posix"
local uloop = require "uloop"

local PATHSTR = '/opt/lnxall_app/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin'

-- global variables
local g_ucon = nil        -- ubus connection handle
local g_appname = nil     -- application name

local function badstr(s)
	if type(s) ~= "string" then return true end
	if string.len(s) == 0 then return true end
	return false
end

local function print_help()
	io.stderr:write([[
Syntax: /lib/dyiot/initd/XXX [command]

Available commands:
	start           Start the service
	stop            Stop the service
	restart         Restart the service
	reload          Reload configuration files (actuall identical to restart)
	enable          Enable service autostart
	disable         Disable service autostart
	status          Service status
	addfile         Load seperate application from JSON file
	delfile         Stop and delete application from JSON file
]])
	io.stderr:flush()
end

local function initd_prepare(apppath, arg1)
	uloop.init() -- initialize uloop first
	-- setup PATH environment variable
	if posix.setenv('PATH', PATHSTR, 1) ~= 0 then
		io.stderr:write("Error, failed to setup PATH environment variable!\n")
		io.stderr:flush()
		return false
	end

	if badstr(apppath) then
		io.stderr:write("Error, cannot determine application name.\n")
		io.stderr:flush()
		return false
	end

	local appn = string.match(apppath, "/([^/]+)$")
	if not appn or string.len(appn) == 0 then appn = apppath end
	g_appname = appn
	if appn == "initd.lua" then
		if badstr(arg1) then
			io.stderr:write("Error, application name not specified.\n")
			print_help()
			return false
		end
		g_appname = arg1
		table.remove(arg, 1)
	end

	-- connect to ubus daemon
	g_ucon = ubus.connect(nil, 2) -- timeout in 2 seconds
	if not g_ucon then
		io.stderr:write("Error, failed to connect to ubus daemon!\n")
		io.stderr:flush()
		return false
	end
	return true
end

local function process_action(action, filepath)
	local appn = g_appname
	local reply, eval = g_ucon:call('initd', string.format("app_%s", action),
		{ ["appname"] = appn, ["filepath"] = filepath })
	if not reply then
		if type(eval) ~= "string" then eval = tostring(eval) end
		io.stderr:write(string.format("%s %s has failed: %s\n",
			appn, action, eval))
		io.stderr:flush()
		return 1
	end
	if type(reply) == "table" then reply = cjson.encode(reply) end
	if type(reply) == "string" then
		io.stdout:write(string.format("%s %s => %s\n", appn, action, reply))
		io.stdout:flush()
	end
	return 0
end

local function iter_args(args)
	local rval = 0
	local argc, done = 0, 0
	while true do
		argc = argc + 0x1
		local argn, argm = args[argc], nil
		if badstr(argn) then break end
		if argn == "addfile" or argn == "delfile" then
			argc = argc + 0x1
			argm = args[argc]
			if badstr(argm) then
				io.stderr:write(string.format("Error, json-file not specified for %s\n", argn))
				io.stderr:flush()
				os.exit(2)
			end
		end
		done = done + 0x1
		rval = rval + process_action(argn, argm)
	end
	if done == 0 then
		print_help()
		os.exit(1)
	end
	os.exit(rval)
end

if initd_prepare(arg[0], arg[1]) then
	iter_args(arg)
end
os.exit(2)
