mirror of
https://github.com/kenzok8/openwrt-packages
synced 2025-01-05 11:36:38 +08:00
update 2022-07-18 20:18:11
This commit is contained in:
parent
088d77842e
commit
694fac32eb
@ -71,7 +71,7 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_Xray
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Trojan
|
||||
bool "Include Trojan"
|
||||
default n
|
||||
default y if i386||x86_64||arm||aarch64
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy
|
||||
bool "Include NaiveProxy"
|
||||
|
@ -161,7 +161,7 @@ gen_config_file(){
|
||||
tcp|udp|nf) smode=nat;;
|
||||
socks) smode=client;;
|
||||
esac
|
||||
lua $BIN_DIR/gentrojanconfig $1 $smode $lport $serv_ip $threads >$config_file
|
||||
lua $BIN_DIR/gen_config $1 $smode $lport $ssport $serv_ip >$config_file
|
||||
sed -i 's/\\//g' $config_file
|
||||
;;
|
||||
esac
|
||||
|
@ -296,7 +296,7 @@ local hysteria = {
|
||||
protocol = server.hysteria_protocol,
|
||||
up_mbps = tonumber(server.uplink_capacity),
|
||||
down_mbps = tonumber(server.downlink_capacity),
|
||||
socks5 = (proto:find("tcp") and tonumber(socks_port) and tonumber(socks_port) ~= "0") and {
|
||||
socks5 = (proto:find("tcp") and tonumber(socks_port) and tonumber(socks_port) ~= 0) and {
|
||||
listen = "0.0.0.0:" .. tonumber(socks_port),
|
||||
timeout = 300,
|
||||
disable_udp = false
|
||||
|
@ -7,16 +7,16 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=LuCI based ipk store
|
||||
LUCI_DESCRIPTION:=luci-app-store is a ipk store developed by LinkEase team
|
||||
LUCI_DEPENDS:=+curl +opkg +luci-base +tar +coreutils +coreutils-stat +libuci-lua +mount-utils
|
||||
LUCI_DEPENDS:=+curl +opkg +luci-base +tar +coreutils +coreutils-stat +libuci-lua +mount-utils +luci-lib-taskd
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
PKG_VERSION:=0.1.10-7
|
||||
PKG_VERSION:=0.1.11-1
|
||||
# PKG_RELEASE MUST be empty for luci.mk
|
||||
PKG_RELEASE:=
|
||||
|
||||
ISTORE_UI_VERSION:=0.1.10
|
||||
ISTORE_UI_RELEASE:=7
|
||||
PKG_HASH:=ba784985b9b3aa90c704c03b16611922e7df2e1f0e18c4a4ec9bff0e09688426
|
||||
ISTORE_UI_VERSION:=0.1.11
|
||||
ISTORE_UI_RELEASE:=1
|
||||
PKG_HASH:=e78b07c257c38892f028faeaea3a64ee47e11137523e1c046df95c87be7abf90
|
||||
|
||||
PKG_SOURCE_URL_FILE:=v$(ISTORE_UI_VERSION)-$(ISTORE_UI_RELEASE).tar.gz
|
||||
PKG_SOURCE:=istore-ui-$(PKG_SOURCE_URL_FILE)
|
||||
|
@ -1,6 +1,7 @@
|
||||
module("luci.controller.store", package.seeall)
|
||||
|
||||
local myopkg = "is-opkg"
|
||||
local is_backup = "/usr/libexec/istore/backup"
|
||||
local page_index = {"admin", "store", "pages"}
|
||||
|
||||
function index()
|
||||
@ -24,25 +25,26 @@ function index()
|
||||
entry({"admin", "store", "check_self_upgrade"}, call("check_self_upgrade"))
|
||||
entry({"admin", "store", "do_self_upgrade"}, post("do_self_upgrade"))
|
||||
|
||||
entry({"admin", "store", "get_support_backup_features"}, call("get_support_backup_features"))
|
||||
entry({"admin", "store", "light_backup"}, post("light_backup"))
|
||||
entry({"admin", "store", "get_light_backup_file"}, call("get_light_backup_file"))
|
||||
entry({"admin", "store", "local_backup"}, post("local_backup"))
|
||||
entry({"admin", "store", "light_restore"}, post("light_restore"))
|
||||
entry({"admin", "store", "local_restore"}, post("local_restore"))
|
||||
entry({"admin", "store", "get_backup_app_list_file_path"}, call("get_backup_app_list_file_path"))
|
||||
entry({"admin", "store", "get_backup_app_list"}, call("get_backup_app_list"))
|
||||
entry({"admin", "store", "get_available_backup_file_list"}, call("get_available_backup_file_list"))
|
||||
entry({"admin", "store", "set_local_backup_dir_path"}, post("set_local_backup_dir_path"))
|
||||
entry({"admin", "store", "get_local_backup_dir_path"}, call("get_local_backup_dir_path"))
|
||||
entry({"admin", "store", "get_block_devices"}, call("get_block_devices"))
|
||||
|
||||
for _, action in ipairs({"update", "install", "upgrade", "remove"}) do
|
||||
store_api(action, true)
|
||||
end
|
||||
for _, action in ipairs({"status", "installed"}) do
|
||||
store_api(action, false)
|
||||
end
|
||||
if nixio.fs.access("/usr/libexec/istore/backup") then
|
||||
entry({"admin", "store", "get_support_backup_features"}, call("get_support_backup_features"))
|
||||
entry({"admin", "store", "light_backup"}, post("light_backup"))
|
||||
entry({"admin", "store", "get_light_backup_file"}, call("get_light_backup_file"))
|
||||
entry({"admin", "store", "local_backup"}, post("local_backup"))
|
||||
entry({"admin", "store", "light_restore"}, post("light_restore"))
|
||||
entry({"admin", "store", "local_restore"}, post("local_restore"))
|
||||
entry({"admin", "store", "get_backup_app_list_file_path"}, call("get_backup_app_list_file_path"))
|
||||
entry({"admin", "store", "get_backup_app_list"}, call("get_backup_app_list"))
|
||||
entry({"admin", "store", "get_available_backup_file_list"}, call("get_available_backup_file_list"))
|
||||
entry({"admin", "store", "set_local_backup_dir_path"}, post("set_local_backup_dir_path"))
|
||||
entry({"admin", "store", "get_local_backup_dir_path"}, call("get_local_backup_dir_path"))
|
||||
entry({"admin", "store", "get_block_devices"}, call("get_block_devices"))
|
||||
end
|
||||
end
|
||||
|
||||
local function user_id()
|
||||
@ -74,7 +76,7 @@ local function vue_lang()
|
||||
return lang
|
||||
end
|
||||
|
||||
local function is_exec(cmd)
|
||||
local function is_exec(cmd, async)
|
||||
local nixio = require "nixio"
|
||||
local os = require "os"
|
||||
local fs = require "nixio.fs"
|
||||
@ -93,6 +95,9 @@ local function is_exec(cmd)
|
||||
return 255, "", "Lock failed: " .. msg
|
||||
end
|
||||
|
||||
if async then
|
||||
cmd = "/etc/init.d/tasks task_add istore " .. luci.util.shellquote(cmd)
|
||||
end
|
||||
local r = os.execute(cmd .. " >/var/log/istore.stdout 2>/var/log/istore.stderr")
|
||||
local e = fs.readfile("/var/log/istore.stderr")
|
||||
local o = fs.readfile("/var/log/istore.stdout")
|
||||
@ -115,7 +120,13 @@ function redirect_index()
|
||||
end
|
||||
|
||||
function store_index()
|
||||
luci.template.render("store/main", {prefix=luci.dispatcher.build_url(unpack(page_index)),id=user_id(),lang=vue_lang()})
|
||||
local fs = require "nixio.fs"
|
||||
local features = { "_lua_force_array_" }
|
||||
if fs.access("/usr/libexec/istore/backup") then
|
||||
features[#features+1] = "backup"
|
||||
end
|
||||
|
||||
luci.template.render("store/main", {prefix=luci.dispatcher.build_url(unpack(page_index)),id=user_id(),lang=vue_lang(),features=features})
|
||||
end
|
||||
|
||||
function store_dev()
|
||||
@ -169,17 +180,15 @@ end
|
||||
|
||||
-- Internal action function
|
||||
local function _action(exe, cmd, ...)
|
||||
local os = require "os"
|
||||
local fs = require "nixio.fs"
|
||||
|
||||
local pkg = ""
|
||||
for k, v in pairs({...}) do
|
||||
pkg = pkg .. " '" .. v:gsub("'", "") .. "'"
|
||||
pkg = pkg .. " " .. luci.util.shellquote(v)
|
||||
end
|
||||
|
||||
local c = "%s %s %s" %{ exe, cmd, pkg }
|
||||
|
||||
return is_exec(c)
|
||||
return is_exec(c, true)
|
||||
end
|
||||
|
||||
function store_action(param)
|
||||
@ -241,11 +250,6 @@ function store_action(param)
|
||||
code, out, err = _action(myopkg, action, unpack(pkgs))
|
||||
else -- remove
|
||||
code, out, err = _action(myopkg, action, unpack(pkgs))
|
||||
if code ~= 0 then
|
||||
code, out0, err0 = _action(myopkg, action, unpack(pkgs))
|
||||
out = out .. out0
|
||||
err = err .. err0
|
||||
end
|
||||
fs.unlink("/tmp/luci-indexcache")
|
||||
end
|
||||
end
|
||||
@ -295,15 +299,15 @@ function store_upload()
|
||||
out = ""
|
||||
if finished then
|
||||
if string.lower(string.sub(path, -4, -1)) == ".run" then
|
||||
code, out, err = _action("sh", "-c", "ls -l \"%s\"; md5sum \"%s\" 2>/dev/null; chmod 755 \"%s\" && \"%s\"" %{ path, path, path, path })
|
||||
code, out, err = _action("sh", "-c", "ls -l \"%s\"; md5sum \"%s\" 2>/dev/null; chmod 755 \"%s\" && \"%s\"; RET=$?; rm -f \"%s\"; exit $RET" %{ path, path, path, path, path })
|
||||
else
|
||||
code, out, err = _action("opkg", "install", path)
|
||||
code, out, err = _action("sh", "-c", "opkg install \"%s\"; RET=$?; rm -f \"%s\"; exit $RET" %{ path, path })
|
||||
end
|
||||
else
|
||||
code = 500
|
||||
err = "upload failed!"
|
||||
end
|
||||
nixio.fs.unlink(path)
|
||||
--nixio.fs.unlink(path)
|
||||
local ret = {
|
||||
code = code,
|
||||
stdout = out,
|
||||
@ -355,8 +359,8 @@ end
|
||||
function get_support_backup_features()
|
||||
local jsonc = require "luci.jsonc"
|
||||
local error_ret = {code = 500, msg = "Unknown"}
|
||||
local success_ret = {code = 200,msg = "Unknown"}
|
||||
local r,o,e = is_exec(myopkg .. " get_support_backup_features")
|
||||
local success_ret = {code = 200, msg = "Unknown"}
|
||||
local r,o,e = is_exec(is_backup .. " get_support_backup_features")
|
||||
if r ~= 0 then
|
||||
error_ret.msg = e
|
||||
luci.http.prepare_content("application/json")
|
||||
@ -374,7 +378,7 @@ function light_backup()
|
||||
local jsonc = require "luci.jsonc"
|
||||
local error_ret = {code = 500, msg = "Unknown"}
|
||||
local success_ret = {code = 200,msg = "Unknown"}
|
||||
local r,o,e = is_exec(myopkg .. " backup")
|
||||
local r,o,e = is_exec(is_backup .. " backup")
|
||||
|
||||
if r ~= 0 then
|
||||
error_ret.msg = e
|
||||
@ -432,13 +436,13 @@ function local_backup()
|
||||
code,out,err = is_exec("findmnt -T " .. path .. " -o TARGET|sed -n 2p")
|
||||
if out:gsub("[\r\n]", "") == "/" or out:gsub("[\r\n]", "") == "/tmp" then
|
||||
-- error
|
||||
error_ret = {code = 500, msg = "Path Error,Can not be / or tmp."}
|
||||
error_ret = {code = 500, stderr = "Path Error,Can not be / or tmp."}
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(error_ret)
|
||||
else
|
||||
-- update local backup path
|
||||
update_local_backup_path(path)
|
||||
code,out,err = is_exec(myopkg .. " backup " .. path)
|
||||
code,out,err = _action(is_backup, "backup", path)
|
||||
ret = {
|
||||
code = code,
|
||||
stdout = out,
|
||||
@ -449,7 +453,7 @@ function local_backup()
|
||||
end
|
||||
else
|
||||
-- error
|
||||
error_ret = {code = 500, msg = "Path Unknown"}
|
||||
error_ret = {code = 500, stderr = "Path Unknown"}
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(error_ret)
|
||||
end
|
||||
@ -481,8 +485,9 @@ function light_restore()
|
||||
|
||||
if finished then
|
||||
is_exec("rm /etc/istore/app.list;tar -xzf " .. path .. " -C /")
|
||||
nixio.fs.unlink(path)
|
||||
if nixio.fs.access("/etc/istore/app.list") then
|
||||
code,out,err = is_exec(myopkg .. " restore")
|
||||
code,out,err = _action(is_backup, "restore")
|
||||
ret = {
|
||||
code = code,
|
||||
stdout = out,
|
||||
@ -491,14 +496,12 @@ function light_restore()
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(ret)
|
||||
else
|
||||
local error_ret = {code = 500, msg = "File is error!"}
|
||||
local error_ret = {code = 500, stderr = "File is error!"}
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(error_ret)
|
||||
end
|
||||
-- remove file
|
||||
is_exec("rm " .. path)
|
||||
else
|
||||
ret = {code = 500, msg = "upload failed!"}
|
||||
ret = {code = 500, stderr = "upload failed!"}
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(ret)
|
||||
end
|
||||
@ -509,7 +512,7 @@ function local_restore()
|
||||
local path = luci.http.formvalue("path")
|
||||
local code, out, err, ret
|
||||
if path ~= "" then
|
||||
code,out,err = is_exec(myopkg .. " restore " .. path)
|
||||
code,out,err = _action(is_backup, "restore", path)
|
||||
ret = {
|
||||
code = code,
|
||||
stdout = out,
|
||||
@ -519,7 +522,7 @@ function local_restore()
|
||||
luci.http.write_json(ret)
|
||||
else
|
||||
-- error
|
||||
error_ret = {code = 500, msg = "Path Unknown"}
|
||||
error_ret = {code = 500, stderr = "Path Unknown"}
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(error_ret)
|
||||
end
|
||||
@ -530,7 +533,7 @@ function get_backup_app_list_file_path()
|
||||
local jsonc = require "luci.jsonc"
|
||||
local error_ret = {code = 500, msg = "Unknown"}
|
||||
local success_ret = {code = 200,msg = "Unknown"}
|
||||
local r,o,e = is_exec(myopkg .. " get_backup_app_list_file_path")
|
||||
local r,o,e = is_exec(is_backup .. " get_backup_app_list_file_path")
|
||||
if r ~= 0 then
|
||||
error_ret.msg = e
|
||||
luci.http.prepare_content("application/json")
|
||||
@ -548,7 +551,7 @@ function get_backup_app_list()
|
||||
local jsonc = require "luci.jsonc"
|
||||
local error_ret = {code = 500, msg = "Unknown"}
|
||||
local success_ret = {code = 200,msg = "Unknown"}
|
||||
local r,o,e = is_exec(myopkg .. " get_backup_app_list")
|
||||
local r,o,e = is_exec(is_backup .. " get_backup_app_list")
|
||||
if r ~= 0 then
|
||||
error_ret.msg = e
|
||||
luci.http.prepare_content("application/json")
|
||||
@ -572,7 +575,7 @@ function get_available_backup_file_list()
|
||||
if path ~= "" then
|
||||
-- update local backup path
|
||||
update_local_backup_path(path)
|
||||
r,o,e = is_exec(myopkg .. " get_available_backup_file_list " .. path)
|
||||
r,o,e = is_exec(is_backup .. " get_available_backup_file_list " .. path)
|
||||
if r ~= 0 then
|
||||
error_ret.msg = e
|
||||
luci.http.prepare_content("application/json")
|
||||
|
@ -1,4 +1,7 @@
|
||||
<%+header%>
|
||||
<%
|
||||
local jsonc = require "luci.jsonc"
|
||||
%>
|
||||
<script>
|
||||
(function(){
|
||||
var vue_prefix="<%=prefix%>";
|
||||
@ -16,6 +19,7 @@
|
||||
window.vue_lang = '<%=lang%>';
|
||||
window.token = "<%=token%>";
|
||||
window.device_id = {arch:"<%=id.arch%>",uid:"<%=id.uid%>",version:"<%=id.version%>"};
|
||||
window.istore_features = <%=jsonc.stringify(features)%>;
|
||||
})();
|
||||
</script>
|
||||
<h2 name="content"><%:iStore%>
|
||||
@ -26,5 +30,32 @@
|
||||
<link rel="stylesheet" href="/luci-static/istore/style.css?v=<%=id.version%>">
|
||||
<div id="app">
|
||||
</div>
|
||||
<%+tasks/embed%>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
let beforeunloadRegistered = false;
|
||||
window.istore_log = function(flush_menu_onclose) {
|
||||
if (flush_menu_onclose && !beforeunloadRegistered) {
|
||||
beforeunloadRegistered = true;
|
||||
window.addEventListener("beforeunload", function(event) {
|
||||
try { window.L.ui.menu.flushCache() } catch (e) { }
|
||||
return true;
|
||||
});
|
||||
}
|
||||
taskd.show_log("istore", true);
|
||||
};
|
||||
})();
|
||||
<%
|
||||
local taskd = require "luci.model.tasks"
|
||||
local status = taskd.status("istore")
|
||||
if status.running then
|
||||
-%>
|
||||
window.istore_log(true);
|
||||
<%
|
||||
end
|
||||
%>
|
||||
</script>
|
||||
|
||||
<script type="module" crossorigin src="/luci-static/istore/index.js?v=<%=id.version%>"></script>
|
||||
<%+footer%>
|
@ -1,14 +1,10 @@
|
||||
#!/bin/sh
|
||||
#set -x
|
||||
#IS_DEBUG=1
|
||||
|
||||
IS_ROOT=/tmp/is-root
|
||||
DL_DIR=${IS_ROOT}/tmp/dl
|
||||
LISTS_DIR_O=/tmp/opkg-lists
|
||||
LISTS_DIR=${IS_ROOT}${LISTS_DIR_O}
|
||||
OPKG_CONF_DIR=${IS_ROOT}/etc/opkg
|
||||
APP_LIST_FILE=/etc/istore/app.list
|
||||
BACKUP_CONFIG_FILE=/etc/config/istore
|
||||
FEEDS_SERVER=https://istore.linkease.com/repo
|
||||
ARCH=`jsonfilter -i /etc/.app_store.id -e '$.arch'`
|
||||
|
||||
@ -126,249 +122,8 @@ new_upgrade() {
|
||||
wrapped_in_update upgrade "$@"
|
||||
}
|
||||
|
||||
opkg_list_installed_packages() {
|
||||
target=$1
|
||||
case $target in
|
||||
"preinstalled")
|
||||
OPKG_INFO_DIR="/rom/usr/lib/opkg/info"
|
||||
;;
|
||||
"userinstalled")
|
||||
OPKG_INFO_DIR="/overlay/upper/usr/lib/opkg/info"
|
||||
;;
|
||||
"allinstalled")
|
||||
OPKG_INFO_DIR="/usr/lib/opkg/info"
|
||||
;;
|
||||
*)
|
||||
echo "invalid target"
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
(cd $OPKG_INFO_DIR && find . -depth -maxdepth 1 -name "*.list" -type f | sed 's#^\./\(.*\)\.list$#\1#g')
|
||||
}
|
||||
|
||||
ipk_build() {
|
||||
PKG_NAME_TEMP=$1
|
||||
IPK_OUTPUT_DIR=$2
|
||||
|
||||
UCI_BAK_DIR="/etc/istore/uci-defaults_bak/"
|
||||
UCI_DEF_DIR="etc/uci-defaults"
|
||||
OPKG_INFO_DIR="/usr/lib/opkg/info/"
|
||||
|
||||
[ -n "${PKG_NAME_TEMP}" ] || exit 1
|
||||
#get real pkg name in opkg
|
||||
PKG_NAME_TEMP=`cat ${IS_ROOT}/all_installed_package.list | sort -u | grep "^${PKG_NAME_TEMP}" | head -n 1`
|
||||
[ -n "${PKG_NAME_TEMP}" ] || exit 1
|
||||
|
||||
PKG_NAME=`cat ${OPKG_INFO_DIR}${PKG_NAME_TEMP}.control | grep "^Package: " | cut -d ' ' -f2`
|
||||
PKG_VER=`cat ${OPKG_INFO_DIR}${PKG_NAME}.control | grep "^Version: " | cut -d ' ' -f2`
|
||||
PKG_ARCH=`cat ${OPKG_INFO_DIR}${PKG_NAME}.control | grep "^Architecture: " | cut -d ' ' -f2`
|
||||
IPK_FILE_NAME="${PKG_NAME}_${PKG_VER}_${PKG_ARCH}"
|
||||
|
||||
rm -rf ${IS_ROOT}/${IPK_FILE_NAME}
|
||||
mkdir -p ${IS_ROOT}/${IPK_FILE_NAME}
|
||||
|
||||
#(1)make CONTROL dir; (2)copy control file to dir
|
||||
cd ${IS_ROOT}/${IPK_FILE_NAME}
|
||||
mkdir -p CONTROL
|
||||
for control_file in `ls ${OPKG_INFO_DIR}${PKG_NAME}.* | grep -v ".list$"`; do
|
||||
file=${control_file##*/}
|
||||
suffix=${file##*.}
|
||||
cp ${control_file} CONTROL/${suffix}
|
||||
done
|
||||
|
||||
#(1)make DATA depend dir; (2)copy uci-defaults_bak file to dir; (3)copy other file to dir
|
||||
for pkgfile in `cat ${OPKG_INFO_DIR}${PKG_NAME}.list | cut -b 2-`; do
|
||||
file=${pkgfile##*/}
|
||||
path=${pkgfile%/*}
|
||||
mkdir -p ${path}
|
||||
if [ `echo "${path}" | grep "^${UCI_DEF_DIR}"` ]; then
|
||||
cp "${UCI_BAK_DIR}${file}" "${pkgfile}"
|
||||
else
|
||||
cp "/${pkgfile}" "${pkgfile}"
|
||||
fi
|
||||
done
|
||||
|
||||
#call ipkg-build script to build ipk
|
||||
ipkg-build ${IS_ROOT}/${IPK_FILE_NAME} ${IPK_OUTPUT_DIR}
|
||||
echo "${IPK_FILE_NAME}.ipk" >> ${IPK_OUTPUT_DIR}/appdepipk.list
|
||||
|
||||
[ -n "${IS_DEBUG}" ] || rm -rf ${IS_ROOT}/${IPK_FILE_NAME}
|
||||
}
|
||||
|
||||
# if arg is NULL, use light backup, otherwise use local backup
|
||||
backup() {
|
||||
[ -n "$1" ] && BACKUP_PATH=$1
|
||||
|
||||
#1.add all istore self data to sysupgrade config file,
|
||||
#sysupgrade will backup/restore it auto when flash new firmware
|
||||
echo "/etc/.app_store.id" > /lib/upgrade/keep.d/luci-app-store
|
||||
cat /usr/lib/opkg/info/luci-app-store.list >> /lib/upgrade/keep.d/luci-app-store
|
||||
echo "/etc/rc.d/S45istore" >> /lib/upgrade/keep.d/luci-app-store
|
||||
echo "/etc/istore/uci-defaults_bak" >> /lib/upgrade/keep.d/luci-app-store
|
||||
echo "${APP_LIST_FILE}" >> /lib/upgrade/keep.d/luci-app-store
|
||||
echo "${BACKUP_CONFIG_FILE}" >> /lib/upgrade/keep.d/luci-app-store
|
||||
|
||||
#write user installed package list to file
|
||||
opkg_list_installed_packages "userinstalled" 2>/dev/null | sort -u > ${IS_ROOT}/user_installed_package.list
|
||||
|
||||
#write installed package list by istore feed to file
|
||||
cat ${IS_ROOT}/user_installed_package.list | \
|
||||
grep '^app-meta-' > ${IS_ROOT}/istore_installed_package.list
|
||||
|
||||
#if no input backup path, only back app.list
|
||||
mkdir -p /etc/istore
|
||||
cp ${IS_ROOT}/istore_installed_package.list ${APP_LIST_FILE}
|
||||
echo "backup installed package list to ${APP_LIST_FILE}"
|
||||
|
||||
if [ ! -n "${BACKUP_PATH}" ]; then
|
||||
echo "backup success"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -d "${BACKUP_PATH}" ] && ! mkdir -p "${BACKUP_PATH}" ; then
|
||||
echo "invalid backup path, can not backup ipk"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#write all installed package list to file
|
||||
opkg_list_installed_packages "allinstalled" 2>/dev/null | sort -u > ${IS_ROOT}/all_installed_package.list
|
||||
|
||||
#write system pre installed package list to file
|
||||
opkg_list_installed_packages "preinstalled" 2>/dev/null | sort -u > ${IS_ROOT}/pre_installed_package.list
|
||||
|
||||
#write installed packages and depends list by istore feed to file by depend sequence
|
||||
appdep_list=""
|
||||
temp_list=`cat ${IS_ROOT}/istore_installed_package.list | sed 's/^/\t/'`
|
||||
while [ -n "${temp_list}" ]
|
||||
do
|
||||
#get real pkg name
|
||||
for PKG_NAME_TEMP in ${temp_list}; do
|
||||
REAL_PKG_NAME=`cat ${IS_ROOT}/all_installed_package.list | sort -u | grep "^${PKG_NAME_TEMP}" | head -n 1`
|
||||
if [ "${REAL_PKG_NAME}" != "${PKG_NAME_TEMP}" ]; then
|
||||
temp_list=`echo "${temp_list}" | sed 's/^\t'"${PKG_NAME_TEMP}"'$/\t'"${REAL_PKG_NAME}"'/'`
|
||||
fi
|
||||
done
|
||||
|
||||
appdep_list=`echo -e "${temp_list}\n${appdep_list}"`
|
||||
[ -n "${IS_DEBUG}" ] && echo -e "temp_list:\n""${temp_list}"
|
||||
[ -n "${IS_DEBUG}" ] && echo -e "appdep_list:\n""${appdep_list}"
|
||||
|
||||
temp_list=`echo "${temp_list}" | xargs opkg depends | grep -v "depends on:" | grep -v " (>= " | grep -v " (= " | sort -u`
|
||||
done
|
||||
|
||||
appdep_list_all=`echo "${appdep_list}" | cut -f2 | grep -v "^$" | awk '!seen[$0]++'`
|
||||
[ -n "${IS_DEBUG}" ] && echo -e "appdep_list_all:\n""${appdep_list_all}"
|
||||
echo "${appdep_list_all}" > ${IS_ROOT}/appdep.list
|
||||
|
||||
#3.rebuild all istore installed package to ipk and backup to userdata partation
|
||||
|
||||
# 4. create dir
|
||||
date=$(date +%Y-%m%d-%H%M)
|
||||
if [ ! -d "$BACKUP_PATH/backup_istore_$date" ];then
|
||||
mkdir $BACKUP_PATH/backup_istore_$date
|
||||
fi
|
||||
cp ${IS_ROOT}/istore_installed_package.list $BACKUP_PATH/backup_istore_$date/app.list
|
||||
cp ${IS_ROOT}/appdep.list $BACKUP_PATH/backup_istore_$date/appdep.list
|
||||
|
||||
#only backup non pre installed ipk
|
||||
cp ${IS_ROOT}/appdep.list ${IS_ROOT}/appdep_strip.list
|
||||
for pre_installed_pkg in `cat ${IS_ROOT}/appdep.list ${IS_ROOT}/pre_installed_package.list | sort -n | uniq -d`; do
|
||||
sed -i '/^'"$pre_installed_pkg"'$/d' ${IS_ROOT}/appdep_strip.list
|
||||
done
|
||||
|
||||
rm -f $BACKUP_PATH/backup_istore_$date/appdepipk.list
|
||||
echo "build ipk"
|
||||
for pkg_name in `cat ${IS_ROOT}/appdep_strip.list`; do
|
||||
ipk_build ${pkg_name} $BACKUP_PATH/backup_istore_$date
|
||||
done
|
||||
|
||||
# 5. create tar.gz file,and remove fir
|
||||
cd $BACKUP_PATH
|
||||
echo "write backup file to $BACKUP_PATH/backup_istore_$date.backup.tar.gz"
|
||||
tar -czf $BACKUP_PATH/backup_istore_$date.backup.tar.gz backup_istore_$date
|
||||
rm -rf $BACKUP_PATH/backup_istore_$date
|
||||
echo "backup success"
|
||||
}
|
||||
|
||||
# if arg is NULL, use light backup, otherwise use local backup
|
||||
restore() {
|
||||
if [ -n "$1" ]; then
|
||||
BACKUP_PATH_FILE=$1
|
||||
else
|
||||
echo "install package by ${APP_LIST_FILE}"
|
||||
update
|
||||
for app in `cat ${APP_LIST_FILE}`; do
|
||||
#skip resotre istore self
|
||||
[ "A${app}" == "A""luci-app-store" ] && continue
|
||||
opkg_wrap install ${app}
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -f "${BACKUP_PATH_FILE}" ];then
|
||||
echo "invalid backup file, can not restore ipk"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#1. Unzip file to dir
|
||||
BACKUP_PATH_FILE_NAME=${BACKUP_PATH_FILE##*/}
|
||||
BACKUP_PATH=/tmp/${BACKUP_PATH_FILE_NAME%.backup.tar.gz*}
|
||||
if [ -d "$BACKUP_PATH" ];then
|
||||
rm -rf $BACKUP_PATH
|
||||
fi
|
||||
mkdir -p $BACKUP_PATH
|
||||
echo "unpack input file..."
|
||||
# fix tar path error
|
||||
tar -zxf ${BACKUP_PATH_FILE} -C /tmp/
|
||||
|
||||
echo "check file"
|
||||
if [ ! -f "${BACKUP_PATH}/appdep.list" ];then
|
||||
echo "no available appdep.list, can not restore ipk"
|
||||
exit 1
|
||||
fi
|
||||
echo "check success"
|
||||
|
||||
#2. install ipk by backup path
|
||||
echo "restore begin"
|
||||
for app in `cat ${BACKUP_PATH}/appdepipk.list`; do
|
||||
opkg_wrap install ${BACKUP_PATH}/${app}
|
||||
done
|
||||
|
||||
#3. rm dir
|
||||
rm -rf ${BACKUP_PATH}
|
||||
echo "restore success"
|
||||
}
|
||||
|
||||
get_support_backup_features() {
|
||||
echo "light_backup"
|
||||
#istore custom img mean support local_backup
|
||||
if [ -f /etc/istore_img_flag ];then
|
||||
echo "local_backup"
|
||||
fi
|
||||
}
|
||||
|
||||
get_backup_app_list_file_path() {
|
||||
echo "${APP_LIST_FILE}"
|
||||
}
|
||||
|
||||
get_backup_app_list() {
|
||||
if [ ! -f "${APP_LIST_FILE}" ];then
|
||||
echo "no app.list, can not get backup app list"
|
||||
exit 1
|
||||
fi
|
||||
cat ${APP_LIST_FILE}
|
||||
}
|
||||
|
||||
get_available_backup_file_list() {
|
||||
if [ -n "$1" ]; then
|
||||
for backup_file in `ls $1/*.backup.tar.gz`; do
|
||||
filename=${backup_file##*/}
|
||||
echo "${filename}"
|
||||
done
|
||||
else
|
||||
echo "input backup path is null"
|
||||
exit 1
|
||||
fi
|
||||
remove() {
|
||||
opkg_wrap --autoremove --force-removal-of-dependent-packages remove "$@"
|
||||
}
|
||||
|
||||
usage() {
|
||||
@ -382,12 +137,6 @@ usage() {
|
||||
echo " list-upgradable List installed and upgradable packages"
|
||||
echo " check_self_upgrade Check iStore upgrade"
|
||||
echo " do_self_upgrade Upgrade iStore"
|
||||
echo " backup [dir] Backup all installed package(s) to [directory]"
|
||||
echo " restore [dir] Restore package(s) by [directory]"
|
||||
echo " get_support_backup_features get device support backup features"
|
||||
echo " get_backup_app_list_file_path get light backup app list file path"
|
||||
echo " get_backup_app_list get light backup app list"
|
||||
echo " get_available_backup_file_list get local available backup file list"
|
||||
echo " opkg sys opkg wrap"
|
||||
}
|
||||
|
||||
@ -404,7 +153,7 @@ case $action in
|
||||
new_upgrade "$@"
|
||||
;;
|
||||
"remove")
|
||||
opkg_wrap --autoremove --force-removal-of-dependent-packages remove "$@"
|
||||
remove "$@" || remove "$@"
|
||||
;;
|
||||
"info")
|
||||
opkg_wrap info "$@"
|
||||
@ -418,24 +167,6 @@ case $action in
|
||||
"do_self_upgrade")
|
||||
do_self_upgrade
|
||||
;;
|
||||
"get_support_backup_features")
|
||||
get_support_backup_features
|
||||
;;
|
||||
"backup")
|
||||
backup "$@"
|
||||
;;
|
||||
"restore")
|
||||
restore "$@"
|
||||
;;
|
||||
"get_backup_app_list_file_path")
|
||||
get_backup_app_list_file_path
|
||||
;;
|
||||
"get_backup_app_list")
|
||||
get_backup_app_list
|
||||
;;
|
||||
"get_available_backup_file_list")
|
||||
get_available_backup_file_list "$@"
|
||||
;;
|
||||
"opkg")
|
||||
opkg_wrap "$@"
|
||||
;;
|
||||
|
295
luci-app-store/root/usr/libexec/istore/backup
Executable file
295
luci-app-store/root/usr/libexec/istore/backup
Executable file
@ -0,0 +1,295 @@
|
||||
#!/bin/sh
|
||||
#set -x
|
||||
#IS_DEBUG=1
|
||||
|
||||
IS_ROOT=/tmp/is-backup
|
||||
APP_LIST_FILE=/etc/istore/app.list
|
||||
BACKUP_CONFIG_FILE=/etc/config/istore
|
||||
|
||||
action=${1}
|
||||
shift
|
||||
|
||||
|
||||
is_init() {
|
||||
mkdir -p ${IS_ROOT}
|
||||
}
|
||||
|
||||
opkg_list_installed_packages() {
|
||||
target=$1
|
||||
case $target in
|
||||
"preinstalled")
|
||||
OPKG_INFO_DIR="/rom/usr/lib/opkg/info"
|
||||
;;
|
||||
"userinstalled")
|
||||
OPKG_INFO_DIR="/overlay/upper/usr/lib/opkg/info"
|
||||
;;
|
||||
"allinstalled")
|
||||
OPKG_INFO_DIR="/usr/lib/opkg/info"
|
||||
;;
|
||||
*)
|
||||
echo "invalid target"
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
(cd $OPKG_INFO_DIR && find . -depth -maxdepth 1 -name "*.list" -type f | sed 's#^\./\(.*\)\.list$#\1#g')
|
||||
}
|
||||
|
||||
ipk_build() {
|
||||
PKG_NAME_TEMP=$1
|
||||
IPK_OUTPUT_DIR=$2
|
||||
|
||||
UCI_BAK_DIR="/etc/istore/uci-defaults_bak/"
|
||||
UCI_DEF_DIR="etc/uci-defaults"
|
||||
OPKG_INFO_DIR="/usr/lib/opkg/info/"
|
||||
|
||||
[ -n "${PKG_NAME_TEMP}" ] || exit 1
|
||||
#get real pkg name in opkg
|
||||
PKG_NAME_TEMP=`cat ${IS_ROOT}/all_installed_package.list | sort -u | grep "^${PKG_NAME_TEMP}" | head -n 1`
|
||||
[ -n "${PKG_NAME_TEMP}" ] || exit 1
|
||||
|
||||
PKG_NAME=`cat ${OPKG_INFO_DIR}${PKG_NAME_TEMP}.control | grep "^Package: " | cut -d ' ' -f2`
|
||||
PKG_VER=`cat ${OPKG_INFO_DIR}${PKG_NAME}.control | grep "^Version: " | cut -d ' ' -f2`
|
||||
PKG_ARCH=`cat ${OPKG_INFO_DIR}${PKG_NAME}.control | grep "^Architecture: " | cut -d ' ' -f2`
|
||||
IPK_FILE_NAME="${PKG_NAME}_${PKG_VER}_${PKG_ARCH}"
|
||||
|
||||
rm -rf ${IS_ROOT}/${IPK_FILE_NAME}
|
||||
mkdir -p ${IS_ROOT}/${IPK_FILE_NAME}
|
||||
|
||||
#(1)make CONTROL dir; (2)copy control file to dir
|
||||
cd ${IS_ROOT}/${IPK_FILE_NAME}
|
||||
mkdir -p CONTROL
|
||||
for control_file in `ls ${OPKG_INFO_DIR}${PKG_NAME}.* | grep -v ".list$"`; do
|
||||
file=${control_file##*/}
|
||||
suffix=${file##*.}
|
||||
cp ${control_file} CONTROL/${suffix}
|
||||
done
|
||||
|
||||
#(1)make DATA depend dir; (2)copy uci-defaults_bak file to dir; (3)copy other file to dir
|
||||
for pkgfile in `cat ${OPKG_INFO_DIR}${PKG_NAME}.list | cut -b 2-`; do
|
||||
file=${pkgfile##*/}
|
||||
path=${pkgfile%/*}
|
||||
mkdir -p ${path}
|
||||
if [ `echo "${path}" | grep "^${UCI_DEF_DIR}"` ]; then
|
||||
cp "${UCI_BAK_DIR}${file}" "${pkgfile}"
|
||||
else
|
||||
cp "/${pkgfile}" "${pkgfile}"
|
||||
fi
|
||||
done
|
||||
|
||||
#call ipkg-build script to build ipk
|
||||
/usr/libexec/istore/ipkg-build ${IS_ROOT}/${IPK_FILE_NAME} ${IPK_OUTPUT_DIR}
|
||||
echo "${IPK_FILE_NAME}.ipk" >> ${IPK_OUTPUT_DIR}/appdepipk.list
|
||||
|
||||
[ -n "${IS_DEBUG}" ] || rm -rf ${IS_ROOT}/${IPK_FILE_NAME}
|
||||
}
|
||||
|
||||
# if arg is NULL, use light backup, otherwise use local backup
|
||||
backup() {
|
||||
[ -n "$1" ] && BACKUP_PATH=$1
|
||||
|
||||
#1.add all istore self data to sysupgrade config file,
|
||||
#sysupgrade will backup/restore it auto when flash new firmware
|
||||
echo "/etc/.app_store.id" > /lib/upgrade/keep.d/luci-app-store
|
||||
cat /usr/lib/opkg/info/luci-app-store.list >> /lib/upgrade/keep.d/luci-app-store
|
||||
echo "/etc/rc.d/S45istore" >> /lib/upgrade/keep.d/luci-app-store
|
||||
echo "/etc/istore/uci-defaults_bak" >> /lib/upgrade/keep.d/luci-app-store
|
||||
echo "${APP_LIST_FILE}" >> /lib/upgrade/keep.d/luci-app-store
|
||||
echo "${BACKUP_CONFIG_FILE}" >> /lib/upgrade/keep.d/luci-app-store
|
||||
|
||||
#write user installed package list to file
|
||||
opkg_list_installed_packages "userinstalled" 2>/dev/null | sort -u > ${IS_ROOT}/user_installed_package.list
|
||||
|
||||
#write installed package list by istore feed to file
|
||||
cat ${IS_ROOT}/user_installed_package.list | \
|
||||
grep '^app-meta-' > ${IS_ROOT}/istore_installed_package.list
|
||||
|
||||
#if no input backup path, only back app.list
|
||||
mkdir -p /etc/istore
|
||||
cp ${IS_ROOT}/istore_installed_package.list ${APP_LIST_FILE}
|
||||
echo "backup installed package list to ${APP_LIST_FILE}"
|
||||
|
||||
if [ ! -n "${BACKUP_PATH}" ]; then
|
||||
echo "backup success"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -d "${BACKUP_PATH}" ] && ! mkdir -p "${BACKUP_PATH}" ; then
|
||||
echo "invalid backup path, can not backup ipk"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#write all installed package list to file
|
||||
opkg_list_installed_packages "allinstalled" 2>/dev/null | sort -u > ${IS_ROOT}/all_installed_package.list
|
||||
|
||||
#write system pre installed package list to file
|
||||
opkg_list_installed_packages "preinstalled" 2>/dev/null | sort -u > ${IS_ROOT}/pre_installed_package.list
|
||||
|
||||
#write installed packages and depends list by istore feed to file by depend sequence
|
||||
appdep_list=""
|
||||
temp_list=`cat ${IS_ROOT}/istore_installed_package.list | sed 's/^/\t/'`
|
||||
while [ -n "${temp_list}" ]
|
||||
do
|
||||
#get real pkg name
|
||||
for PKG_NAME_TEMP in ${temp_list}; do
|
||||
REAL_PKG_NAME=`cat ${IS_ROOT}/all_installed_package.list | sort -u | grep "^${PKG_NAME_TEMP}" | head -n 1`
|
||||
if [ "${REAL_PKG_NAME}" != "${PKG_NAME_TEMP}" ]; then
|
||||
temp_list=`echo "${temp_list}" | sed 's/^\t'"${PKG_NAME_TEMP}"'$/\t'"${REAL_PKG_NAME}"'/'`
|
||||
fi
|
||||
done
|
||||
|
||||
appdep_list=`echo -e "${temp_list}\n${appdep_list}"`
|
||||
[ -n "${IS_DEBUG}" ] && echo -e "temp_list:\n""${temp_list}"
|
||||
[ -n "${IS_DEBUG}" ] && echo -e "appdep_list:\n""${appdep_list}"
|
||||
|
||||
temp_list=`echo "${temp_list}" | xargs opkg depends | grep -v "depends on:" | grep -v " (>= " | grep -v " (= " | sort -u`
|
||||
done
|
||||
|
||||
appdep_list_all=`echo "${appdep_list}" | cut -f2 | grep -v "^$" | awk '!seen[$0]++'`
|
||||
[ -n "${IS_DEBUG}" ] && echo -e "appdep_list_all:\n""${appdep_list_all}"
|
||||
echo "${appdep_list_all}" > ${IS_ROOT}/appdep.list
|
||||
|
||||
#3.rebuild all istore installed package to ipk and backup to userdata partation
|
||||
|
||||
# 4. create dir
|
||||
date=$(date +%Y-%m%d-%H%M)
|
||||
if [ ! -d "$BACKUP_PATH/backup_istore_$date" ];then
|
||||
mkdir $BACKUP_PATH/backup_istore_$date
|
||||
fi
|
||||
cp ${IS_ROOT}/istore_installed_package.list $BACKUP_PATH/backup_istore_$date/app.list
|
||||
cp ${IS_ROOT}/appdep.list $BACKUP_PATH/backup_istore_$date/appdep.list
|
||||
|
||||
#only backup non pre installed ipk
|
||||
cp ${IS_ROOT}/appdep.list ${IS_ROOT}/appdep_strip.list
|
||||
for pre_installed_pkg in `cat ${IS_ROOT}/appdep.list ${IS_ROOT}/pre_installed_package.list | sort -n | uniq -d`; do
|
||||
sed -i '/^'"$pre_installed_pkg"'$/d' ${IS_ROOT}/appdep_strip.list
|
||||
done
|
||||
|
||||
rm -f $BACKUP_PATH/backup_istore_$date/appdepipk.list
|
||||
echo "build ipk"
|
||||
for pkg_name in `cat ${IS_ROOT}/appdep_strip.list`; do
|
||||
ipk_build ${pkg_name} $BACKUP_PATH/backup_istore_$date
|
||||
done
|
||||
|
||||
# 5. create tar.gz file,and remove fir
|
||||
cd $BACKUP_PATH
|
||||
echo "write backup file to $BACKUP_PATH/backup_istore_$date.backup.tar.gz"
|
||||
tar -czf $BACKUP_PATH/backup_istore_$date.backup.tar.gz backup_istore_$date
|
||||
rm -rf $BACKUP_PATH/backup_istore_$date
|
||||
echo "backup success"
|
||||
}
|
||||
|
||||
# if arg is NULL, use light backup, otherwise use local backup
|
||||
restore() {
|
||||
if [ -n "$1" ]; then
|
||||
BACKUP_PATH_FILE=$1
|
||||
else
|
||||
echo "install package by ${APP_LIST_FILE}"
|
||||
is-opkg update
|
||||
for app in `cat ${APP_LIST_FILE}`; do
|
||||
#skip resotre istore self
|
||||
[ "A${app}" == "A""luci-app-store" ] && continue
|
||||
is-opkg install ${app}
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -f "${BACKUP_PATH_FILE}" ];then
|
||||
echo "invalid backup file, can not restore ipk"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#1. Unzip file to dir
|
||||
BACKUP_PATH_FILE_NAME=${BACKUP_PATH_FILE##*/}
|
||||
BACKUP_PATH=/tmp/${BACKUP_PATH_FILE_NAME%.backup.tar.gz*}
|
||||
if [ -d "$BACKUP_PATH" ];then
|
||||
rm -rf $BACKUP_PATH
|
||||
fi
|
||||
mkdir -p $BACKUP_PATH
|
||||
echo "unpack input file..."
|
||||
# fix tar path error
|
||||
tar -zxf ${BACKUP_PATH_FILE} -C /tmp/
|
||||
|
||||
echo "check file"
|
||||
if [ ! -f "${BACKUP_PATH}/appdep.list" ];then
|
||||
echo "no available appdep.list, can not restore ipk"
|
||||
exit 1
|
||||
fi
|
||||
echo "check success"
|
||||
|
||||
#2. install ipk by backup path
|
||||
echo "restore begin"
|
||||
( cd ${BACKUP_PATH}; opkg install `cat ${BACKUP_PATH}/appdepipk.list` )
|
||||
|
||||
#3. rm dir
|
||||
rm -rf ${BACKUP_PATH}
|
||||
echo "restore success"
|
||||
}
|
||||
|
||||
get_support_backup_features() {
|
||||
echo "light_backup"
|
||||
#istore custom img mean support local_backup
|
||||
if [ -f /etc/istore_img_flag ];then
|
||||
echo "local_backup"
|
||||
fi
|
||||
}
|
||||
|
||||
get_backup_app_list_file_path() {
|
||||
echo "${APP_LIST_FILE}"
|
||||
}
|
||||
|
||||
get_backup_app_list() {
|
||||
if [ ! -f "${APP_LIST_FILE}" ];then
|
||||
echo "no app.list, can not get backup app list"
|
||||
exit 1
|
||||
fi
|
||||
cat ${APP_LIST_FILE}
|
||||
}
|
||||
|
||||
get_available_backup_file_list() {
|
||||
if [ -n "$1" ]; then
|
||||
for backup_file in `ls $1/*.backup.tar.gz`; do
|
||||
filename=${backup_file##*/}
|
||||
echo "${filename}"
|
||||
done
|
||||
else
|
||||
echo "input backup path is null"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "usage: backup sub-command [arguments...]"
|
||||
echo "where sub-command is one of:"
|
||||
echo " backup [dir] Backup all installed package(s) to [directory]"
|
||||
echo " restore [dir] Restore package(s) by [directory]"
|
||||
echo " get_support_backup_features get device support backup features"
|
||||
echo " get_backup_app_list_file_path get light backup app list file path"
|
||||
echo " get_backup_app_list get light backup app list"
|
||||
echo " get_available_backup_file_list get local available backup file list"
|
||||
}
|
||||
|
||||
is_init >/dev/null 2>&1
|
||||
|
||||
case $action in
|
||||
"get_support_backup_features")
|
||||
get_support_backup_features
|
||||
;;
|
||||
"backup")
|
||||
backup "$@"
|
||||
;;
|
||||
"restore")
|
||||
restore "$@"
|
||||
;;
|
||||
"get_backup_app_list_file_path")
|
||||
get_backup_app_list_file_path
|
||||
;;
|
||||
"get_backup_app_list")
|
||||
get_backup_app_list
|
||||
;;
|
||||
"get_available_backup_file_list")
|
||||
get_available_backup_file_list "$@"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
19
luci-lib-taskd/Makefile
Normal file
19
luci-lib-taskd/Makefile
Normal file
@ -0,0 +1,19 @@
|
||||
#
|
||||
# Copyright (C) 2022 jjm2473 <jjm2473@gmail.com>
|
||||
#
|
||||
# This is free software, licensed under the MIT License.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=Task library
|
||||
LUCI_DEPENDS:=+luci-lib-xterm +taskd
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
PKG_VERSION:=1.0.13
|
||||
PKG_RELEASE:=
|
||||
PKG_MAINTAINER:=jjm2473 <jjm2473@gmail.com>
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
107
luci-lib-taskd/htdocs/luci-static/resources/tasks/tasks.css
Normal file
107
luci-lib-taskd/htdocs/luci-static/resources/tasks/tasks.css
Normal file
@ -0,0 +1,107 @@
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#tasks_detail_container {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #0008;
|
||||
}
|
||||
#tasks_dialog {
|
||||
position: absolute;
|
||||
width: 770px;
|
||||
max-height: 100%;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: #000;
|
||||
|
||||
border-radius: 10px;
|
||||
box-shadow: 2px 2px 6px #000a;
|
||||
padding: 20px;
|
||||
|
||||
color: white;
|
||||
}
|
||||
.dialog-title-bar {
|
||||
margin-top: -10px;
|
||||
margin-right: -10px;
|
||||
margin-bottom: 5px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
.dialog-title-bar .dialog-title {
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
.dialog-content {
|
||||
max-height: 500px;
|
||||
overflow-y: scroll;
|
||||
margin-right: -10px;
|
||||
}
|
||||
.dialog-icons {
|
||||
align-self: center;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.dialog-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: white;
|
||||
color: black;
|
||||
border-radius: 50%;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
user-select: none;
|
||||
margin-left: 10px;
|
||||
line-height: 1;
|
||||
font-family: sans-serif;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.dialog-icon.dialog-icon-min {
|
||||
background-color: darkorange;
|
||||
}
|
||||
.dialog-icon.dialog-icon-close {
|
||||
background-color: #ff5f56;
|
||||
}
|
||||
.dialog-icons:hover .dialog-icon.dialog-icon-min:before {
|
||||
content: "_";
|
||||
}
|
||||
.dialog-icons:hover .dialog-icon.dialog-icon-close:before {
|
||||
content: "X";
|
||||
}
|
||||
|
||||
.tasks_stopped .dialog-icon.dialog-icon-close {
|
||||
background-color: #27c840;
|
||||
}
|
||||
.tasks_stopped #tasks_dialog {
|
||||
padding: 19px;
|
||||
border: 1px #27c840 solid;
|
||||
|
||||
animation: border-blink 1s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.tasks_failed #tasks_dialog {
|
||||
border-color: #ff0000;
|
||||
}
|
||||
|
||||
.tasks_failed .dialog-icon.dialog-icon-close {
|
||||
background-color: #ff0000;
|
||||
}
|
||||
|
||||
@keyframes border-blink { 50% { border-color:#fff ; } }
|
199
luci-lib-taskd/htdocs/luci-static/resources/tasks/tasks.js
Normal file
199
luci-lib-taskd/htdocs/luci-static/resources/tasks/tasks.js
Normal file
@ -0,0 +1,199 @@
|
||||
|
||||
(function(){
|
||||
const taskd={};
|
||||
const $gettext = function(str) {
|
||||
return taskd.i18n[str] || str;
|
||||
};
|
||||
const request = function(url, method, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var oReq = new XMLHttpRequest();
|
||||
oReq.open(method || 'GET', url, true);
|
||||
|
||||
oReq.onload = function (oEvent) {
|
||||
resolve(oReq.responseText);
|
||||
};
|
||||
if (method=='POST') {
|
||||
oReq.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
}
|
||||
oReq.send(data || method=='POST'?"token="+taskd.csrfToken:null);
|
||||
});
|
||||
};
|
||||
const getBin = function(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var oReq = new XMLHttpRequest();
|
||||
oReq.open("GET", url, true);
|
||||
oReq.responseType = "arraybuffer";
|
||||
|
||||
oReq.onload = function (oEvent) {
|
||||
resolve({status: oReq.status, buffer: new Uint8Array(oReq.response)});
|
||||
};
|
||||
|
||||
oReq.send(null);
|
||||
});
|
||||
};
|
||||
const getTaskDetail = function(task_id) {
|
||||
return request("/cgi-bin/luci/admin/system/tasks/status?task_id="+task_id).then(data=>JSON.parse(data));
|
||||
};
|
||||
const create_dialog = function(cfg) {
|
||||
const container = document.createElement('div');
|
||||
container.id = "tasks_detail_container";
|
||||
container.innerHTML = taskd.dialog_template;
|
||||
|
||||
document.body.appendChild(container);
|
||||
const title_view = container.querySelector(".dialog-title-bar .dialog-title");
|
||||
title_view.innerText = cfg.title;
|
||||
|
||||
const term = new Terminal({convertEol: cfg.convertEol||false});
|
||||
if (cfg.nohide) {
|
||||
container.querySelector(".dialog-icon-min").hidden = true;
|
||||
} else {
|
||||
container.querySelector(".dialog-icon-min").onclick = function(){
|
||||
container.hidden=true;
|
||||
term.dispose();
|
||||
document.body.removeChild(container);
|
||||
cfg.onhide && cfg.onhide();
|
||||
return false;
|
||||
};
|
||||
}
|
||||
term.open(document.getElementById("tasks_xterm_log"));
|
||||
|
||||
return {term,container};
|
||||
};
|
||||
const show_log_txt = function(title, content, onclose) {
|
||||
const dialog = create_dialog({title, convertEol:true, onhine:onclose});
|
||||
const container = dialog.container;
|
||||
const term = dialog.term;
|
||||
container.querySelector(".dialog-icon-close").hidden = true;
|
||||
term.write(content);
|
||||
};
|
||||
const show_log = function(task_id, nohide) {
|
||||
let showing = true;
|
||||
let running = true;
|
||||
const dialog = create_dialog({title:task_id, nohide, onhide:function(){showing=false;}});
|
||||
const container = dialog.container;
|
||||
const term = dialog.term;
|
||||
|
||||
const title_view = container.querySelector(".dialog-title-bar .dialog-title");
|
||||
container.querySelector(".dialog-icon-close").onclick = function(){
|
||||
if (!running || confirm($gettext("Stop running task?"))) {
|
||||
running=false;
|
||||
showing=false;
|
||||
del_task(task_id).then(()=>{
|
||||
location.href = location.href;
|
||||
});
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const checkTask = function() {
|
||||
return getTaskDetail(task_id).then(data=>{
|
||||
if (!running) {
|
||||
return false;
|
||||
}
|
||||
running = data.running;
|
||||
let title = task_id;
|
||||
if (!data.running && data.stop) {
|
||||
title += " (" + (data.exit_code?$gettext("Failed at:"):$gettext("Finished at:")) + " " + new Date(data.stop * 1000).toLocaleString() + ")";
|
||||
}
|
||||
title += " > " + (data.command || '');
|
||||
title_view.title = title;
|
||||
title_view.innerText = title;
|
||||
if (!data.running) {
|
||||
container.classList.add('tasks_stopped')
|
||||
if (data.exit_code) {
|
||||
container.classList.add('tasks_failed')
|
||||
}
|
||||
}
|
||||
// last pull
|
||||
return showing;
|
||||
});
|
||||
};
|
||||
let logoffset = 0;
|
||||
const pulllog = function() {
|
||||
getBin("/cgi-bin/luci/admin/system/tasks/log?task_id="+task_id+"&offset="+logoffset).then(function(res){
|
||||
if (!showing) {
|
||||
return false;
|
||||
}
|
||||
switch(res.status){
|
||||
case 205:
|
||||
term.reset();
|
||||
logoffset = 0;
|
||||
return running;
|
||||
break;
|
||||
case 204:
|
||||
return running && checkTask();
|
||||
break;
|
||||
case 200:
|
||||
logoffset += res.buffer.byteLength;
|
||||
term.write(res.buffer);
|
||||
return running;
|
||||
break;
|
||||
}
|
||||
}).then(again => {
|
||||
if (again) {
|
||||
setTimeout(pulllog, 0);
|
||||
}
|
||||
});
|
||||
};
|
||||
checkTask().then(pulllog);
|
||||
};
|
||||
const del_task = function(task_id) {
|
||||
return request("/cgi-bin/luci/admin/system/tasks/stop?task_id="+task_id, "POST");
|
||||
};
|
||||
taskd.show_log = show_log;
|
||||
taskd.remove = del_task;
|
||||
taskd.show_log_txt = show_log_txt;
|
||||
window.taskd=taskd;
|
||||
})();
|
||||
|
||||
(function(){
|
||||
// compat
|
||||
if (typeof(window.findParent) !== 'function') {
|
||||
const elem = function(e) {
|
||||
return (e != null && typeof(e) == 'object' && 'nodeType' in e);
|
||||
};
|
||||
const matches = function(node, selector) {
|
||||
var m = elem(node) ? node.matches || node.msMatchesSelector : null;
|
||||
return m ? m.call(node, selector) : false;
|
||||
};
|
||||
window.findParent = function (node, selector) {
|
||||
if (elem(node) && node.closest)
|
||||
return node.closest(selector);
|
||||
|
||||
while (elem(node))
|
||||
if (matches(node, selector))
|
||||
return node;
|
||||
else
|
||||
node = node.parentNode;
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
if (typeof(window.cbi_submit) !== 'function') {
|
||||
const makeHidden = function(name) {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = name;
|
||||
return input;
|
||||
};
|
||||
window.cbi_submit = function(elem, name, value, action) {
|
||||
var form = elem.form || findParent(elem, 'form');
|
||||
|
||||
if (!form)
|
||||
return false;
|
||||
|
||||
if (action)
|
||||
form.action = action;
|
||||
|
||||
if (name) {
|
||||
var hidden = form.querySelector('input[type="hidden"][name="%s"]'.format(name)) ||
|
||||
makeHidden(name);
|
||||
|
||||
hidden.value = value || '1';
|
||||
form.appendChild(hidden);
|
||||
}
|
||||
|
||||
form.submit();
|
||||
return true;
|
||||
};
|
||||
}
|
||||
})();
|
92
luci-lib-taskd/luasrc/controller/tasks-lib.lua
Executable file
92
luci-lib-taskd/luasrc/controller/tasks-lib.lua
Executable file
@ -0,0 +1,92 @@
|
||||
|
||||
module("luci.controller.tasks-lib", package.seeall)
|
||||
|
||||
|
||||
function index()
|
||||
entry({"admin", "system", "tasks", "status"}, call("tasks_status")).dependent=false
|
||||
entry({"admin", "system", "tasks", "log"}, call("tasks_log")).dependent=false
|
||||
entry({"admin", "system", "tasks", "stop"}, post("tasks_stop")).dependent=false
|
||||
end
|
||||
|
||||
local util = require "luci.util"
|
||||
local jsonc = require "luci.jsonc"
|
||||
local ltn12 = require "luci.ltn12"
|
||||
|
||||
local taskd = require "luci.model.tasks"
|
||||
|
||||
function tasks_status()
|
||||
local data = taskd.status(luci.http.formvalue("task_id"))
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(data)
|
||||
end
|
||||
|
||||
function tasks_log()
|
||||
local wait = 107
|
||||
local task_id = luci.http.formvalue("task_id")
|
||||
local offset = luci.http.formvalue("offset")
|
||||
offset = offset and tonumber(offset) or 0
|
||||
local logpath = "/var/log/tasks/"..task_id..".log"
|
||||
local i
|
||||
local logfd = io.open(logpath, "rb")
|
||||
if logfd == nil then
|
||||
luci.http.status(404)
|
||||
luci.http.write("log not found")
|
||||
return
|
||||
end
|
||||
|
||||
local size = logfd:seek("end")
|
||||
|
||||
if size < offset then
|
||||
luci.http.status(205, "Reset Content")
|
||||
luci.http.write("reset offset")
|
||||
return
|
||||
end
|
||||
|
||||
i = 0
|
||||
while (i < wait)
|
||||
do
|
||||
if size > offset then
|
||||
break
|
||||
end
|
||||
nixio.nanosleep(0, 10000000) -- sleep 10ms
|
||||
size = logfd:seek("end")
|
||||
i = i+1
|
||||
end
|
||||
if i == wait then
|
||||
logfd:close()
|
||||
luci.http.status(204)
|
||||
luci.http.prepare_content("application/octet-stream")
|
||||
return
|
||||
end
|
||||
logfd:seek("set", offset)
|
||||
|
||||
local write_log = function()
|
||||
local buffer = logfd:read(4096)
|
||||
if buffer and #buffer > 0 then
|
||||
return buffer
|
||||
else
|
||||
logfd:close()
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
luci.http.prepare_content("application/octet-stream")
|
||||
|
||||
if logfd then
|
||||
ltn12.pump.all(write_log, luci.http.write)
|
||||
end
|
||||
end
|
||||
|
||||
function tasks_stop()
|
||||
local sys = require("luci.sys")
|
||||
local task_id = luci.http.formvalue("task_id") or ""
|
||||
if task_id == "" then
|
||||
luci.http.status(400)
|
||||
luci.http.write("task_id is empty")
|
||||
return
|
||||
end
|
||||
if sys.call("/etc/init.d/tasks task_del "..task_id.." >/dev/null 2>&1") ~= 0 then
|
||||
nixio.nanosleep(2, 10000000)
|
||||
end
|
||||
luci.http.status(204)
|
||||
end
|
100
luci-lib-taskd/luasrc/model/tasks.lua
Normal file
100
luci-lib-taskd/luasrc/model/tasks.lua
Normal file
@ -0,0 +1,100 @@
|
||||
local util = require "luci.util"
|
||||
local jsonc = require "luci.jsonc"
|
||||
|
||||
local taskd = {}
|
||||
|
||||
local function output(data)
|
||||
local ret={}
|
||||
ret.running=data.running
|
||||
if not data.running then
|
||||
ret.exit_code=data.exit_code
|
||||
if nil == ret.exit_code then
|
||||
if data["data"] and data["data"]["exit_code"] and data["data"]["exit_code"] ~= "" then
|
||||
ret.exit_code=tonumber(data["data"]["exit_code"])
|
||||
else
|
||||
ret.exit_code=143
|
||||
end
|
||||
end
|
||||
end
|
||||
ret.command=data["command"] and data["command"][4] or '#'
|
||||
if data["data"] then
|
||||
ret.start=tonumber(data["data"]["start"])
|
||||
if not data.running and data["data"]["stop"] then
|
||||
ret.stop=tonumber(data["data"]["stop"])
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
taskd.status = function (task_id)
|
||||
task_id = task_id or ""
|
||||
local data = util.trim(util.exec("/etc/init.d/tasks task_status "..task_id.." 2>/dev/null")) or ""
|
||||
if data ~= "" then
|
||||
data = jsonc.parse(data)
|
||||
else
|
||||
if task_id == "" then
|
||||
data = {}
|
||||
else
|
||||
data = {running=false, exit_code=255}
|
||||
end
|
||||
end
|
||||
if task_id ~= "" then
|
||||
return output(data)
|
||||
end
|
||||
local ary={}
|
||||
for k, v in pairs(data) do
|
||||
ary[k] = output(v)
|
||||
end
|
||||
return ary
|
||||
end
|
||||
|
||||
taskd.docker_map = function(config, task_id, script_path, title, desc)
|
||||
require("luci.cbi")
|
||||
require("luci.http")
|
||||
require("luci.sys")
|
||||
local translate = require("luci.i18n").translate
|
||||
local m
|
||||
m = luci.cbi.Map(config, title, desc)
|
||||
m.template = "tasks/docker"
|
||||
-- hide default buttons
|
||||
m.pageaction = false
|
||||
-- we want hook 'on_after_apply' works, 'apply_on_parse' can be true (rollback) or false (no rollback),
|
||||
-- but 'apply_on_parse' must be true for luci 17.01 and below
|
||||
m.apply_on_parse = true
|
||||
m.script_path = script_path
|
||||
m.task_id = task_id
|
||||
m.auto_show_task = true
|
||||
m.on_before_apply = function(self)
|
||||
if self.uci.rollback then
|
||||
-- luci 18.06+ has 'rollback' function
|
||||
-- rollback dialog will show because 'apply_on_parse' is true,
|
||||
-- hide rollback dialog by hook 'apply' function
|
||||
local apply = self.uci.apply
|
||||
self.uci.apply = function(uci, rollback)
|
||||
apply(uci, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
m.on_after_apply = function(self)
|
||||
local cmd
|
||||
local action = luci.http.formvalue("cbi.apply") or "null"
|
||||
if "upgrade" == action or "install" == action
|
||||
or "start" == action or "stop" == action or "restart" == action or "rm" == action then
|
||||
cmd = string.format("\"%s\" %s", script_path, action)
|
||||
end
|
||||
if cmd then
|
||||
if luci.sys.call("/etc/init.d/tasks task_add " .. task_id .. " " .. luci.util.shellquote(cmd) .. " >/dev/null 2>&1") ~= 0 then
|
||||
self.task_start_failed = true
|
||||
self.message = translate("Config saved, but apply failed")
|
||||
end
|
||||
else
|
||||
self.message = translate("Unknown command: ") .. action
|
||||
end
|
||||
if self.message then
|
||||
self.auto_show_task = false
|
||||
end
|
||||
end
|
||||
return m
|
||||
end
|
||||
|
||||
return taskd
|
56
luci-lib-taskd/luasrc/view/tasks/docker.htm
Normal file
56
luci-lib-taskd/luasrc/view/tasks/docker.htm
Normal file
@ -0,0 +1,56 @@
|
||||
|
||||
<% if self.task_start_failed then %>
|
||||
<div class="alert-message warning"><%:Another task running, try again later.%> <a href="javascript:void(taskd.show_log('<%=self.task_id%>'))"><%:Click here to check running task%></a></div>
|
||||
<% end %>
|
||||
|
||||
<%+cbi/map%>
|
||||
<%
|
||||
local task_running = false
|
||||
local taskd = require "luci.model.tasks"
|
||||
local status = taskd.status(self.task_id)
|
||||
task_running = status.running
|
||||
-%>
|
||||
<div class="cbi-page-actions control-group">
|
||||
<%
|
||||
if not task_running then
|
||||
%>
|
||||
<%
|
||||
local util = require "luci.util"
|
||||
local container_status = util.trim(util.exec(self.script_path.." status"))
|
||||
local container_install = (string.len(container_status) > 0)
|
||||
local container_running = container_status == "running"
|
||||
if container_install then
|
||||
-%>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Upgrade%>/<%:Apply%>" onclick="cbi_submit(this, 'cbi.apply', 'upgrade')" />
|
||||
<%
|
||||
if container_running then
|
||||
-%>
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" value="<%:Stop%>" onclick="cbi_submit(this, 'cbi.apply', 'stop')" />
|
||||
|
||||
<input class="btn cbi-button cbi-button-reload" type="button" value="<%:Restart%>" onclick="cbi_submit(this, 'cbi.apply', 'restart')" />
|
||||
<% else %>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Start%>" onclick="cbi_submit(this, 'cbi.apply', 'start')" />
|
||||
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" value="<%:Remove%>" onclick="cbi_submit(this, 'cbi.apply', 'rm')" />
|
||||
<% end
|
||||
else %>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Install%>" onclick="cbi_submit(this, 'cbi.apply', 'install')" />
|
||||
<% end
|
||||
else
|
||||
%>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Task Running%>…" onclick="taskd.show_log('<%=self.task_id%>')" />
|
||||
<%
|
||||
end
|
||||
%>
|
||||
</div>
|
||||
|
||||
<%+tasks/embed%>
|
||||
<%
|
||||
if self.auto_show_task and task_running then
|
||||
-%>
|
||||
<script>
|
||||
taskd.show_log("<%=self.task_id%>");
|
||||
</script>
|
||||
<%
|
||||
end
|
||||
%>
|
31
luci-lib-taskd/luasrc/view/tasks/embed.htm
Normal file
31
luci-lib-taskd/luasrc/view/tasks/embed.htm
Normal file
@ -0,0 +1,31 @@
|
||||
<%+xterm/embed%>
|
||||
<link rel="stylesheet" href="<%=resource%>/tasks/tasks.css<%# ?v=PKG_VERSION %>">
|
||||
<script src="<%=resource%>/tasks/tasks.js<%# ?v=PKG_VERSION %>"></script>
|
||||
<%
|
||||
local i18n = {}
|
||||
local function tr(str)
|
||||
i18n[str]=translate(str)
|
||||
end
|
||||
tr("Stop running task?")
|
||||
tr("Stopped at:")
|
||||
tr("Finished at:")
|
||||
tr("Failed at:")
|
||||
-%>
|
||||
<script>
|
||||
window.taskd.csrfToken="<%=token%>";
|
||||
window.taskd.i18n=<% luci.http.write_json(i18n) %>;
|
||||
window.taskd.dialog_template=`
|
||||
<div id="tasks_dialog">
|
||||
<div class="dialog-title-bar">
|
||||
<span class="dialog-title" id="tasks_id"></span>
|
||||
<span class="dialog-icons">
|
||||
<span class="dialog-icon dialog-icon-close" title="<%:Stop and Remove%>"></span>
|
||||
<span class="dialog-icon dialog-icon-min" title="<%:Hide%>"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="dialog-content">
|
||||
<div id="tasks_xterm_log"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
</script>
|
15
luci-lib-taskd/src/Makefile
Normal file
15
luci-lib-taskd/src/Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
clean:
|
||||
compile:
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_NAME:=luci-lib-dummy
|
||||
INCLUDE_DIR:=./dummy
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
install:
|
||||
mkdir -p "$(DESTDIR)$(LUCI_LIBRARYDIR)/i18n"
|
||||
$(foreach lang,$(LUCI_LANGUAGES),$(foreach po,$(wildcard ${CURDIR}/po/$(lang)/*.po), \
|
||||
po2lmo $(po) \
|
||||
$(DESTDIR)$(LUCI_LIBRARYDIR)/i18n/$(basename $(notdir $(po))).$(lang).lmo;))
|
2
luci-lib-taskd/src/dummy/package.mk
Normal file
2
luci-lib-taskd/src/dummy/package.mk
Normal file
@ -0,0 +1,2 @@
|
||||
define BuildPackage
|
||||
endef
|
32
luci-lib-taskd/src/po/zh-cn/lib-tasks.po
Normal file
32
luci-lib-taskd/src/po/zh-cn/lib-tasks.po
Normal file
@ -0,0 +1,32 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
msgid "Stop running task?"
|
||||
msgstr "删除运行中的任务?"
|
||||
|
||||
msgid "Finished at:"
|
||||
msgstr "完成于:"
|
||||
|
||||
msgid "Failed at:"
|
||||
msgstr "失败于:"
|
||||
|
||||
msgid "Stop and Remove"
|
||||
msgstr "停止并删除"
|
||||
|
||||
msgid "Hide"
|
||||
msgstr "隐藏"
|
||||
|
||||
msgid "Config saved, but apply failed"
|
||||
msgstr "配置已保存,但应用失败"
|
||||
|
||||
msgid "Unknown command: "
|
||||
msgstr "未知命令:"
|
||||
|
||||
msgid "Another task running, try again later."
|
||||
msgstr "已有后台任务运行中,请稍后重试。"
|
||||
|
||||
msgid "Click here to check running task"
|
||||
msgstr "点此查看运行中的任务"
|
||||
|
||||
msgid "Task Running"
|
||||
msgstr "任务执行中"
|
21
luci-lib-xterm/Makefile
Normal file
21
luci-lib-xterm/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
#
|
||||
# Copyright (c) 2017-2019, The xterm.js authors (MIT License)
|
||||
# Copyright (c) 2014-2017, SourceLair, Private Company (www.sourcelair.com) (MIT License)
|
||||
# Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
||||
#
|
||||
# This is free software, licensed under the MIT License.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=Xterm.js library
|
||||
LUCI_DEPENDS:=
|
||||
|
||||
PKG_LICENSE:=MIT
|
||||
PKG_VERSION:=4.18.0
|
||||
PKG_RELEASE:=
|
||||
PKG_MAINTAINER:=jjm2473 <jjm2473@gmail.com>
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
180
luci-lib-xterm/htdocs/luci-static/resources/xterm/xterm.css
Normal file
180
luci-lib-xterm/htdocs/luci-static/resources/xterm/xterm.css
Normal file
@ -0,0 +1,180 @@
|
||||
/**
|
||||
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
|
||||
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
||||
* https://github.com/chjj/term.js
|
||||
* @license MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Originally forked from (with the author's permission):
|
||||
* Fabrice Bellard's javascript vt100 for jslinux:
|
||||
* http://bellard.org/jslinux/
|
||||
* Copyright (c) 2011 Fabrice Bellard
|
||||
* The original design remains. The terminal itself
|
||||
* has been extended to include xterm CSI codes, among
|
||||
* other features.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default styles for xterm.js
|
||||
*/
|
||||
|
||||
.xterm {
|
||||
position: relative;
|
||||
user-select: none;
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.xterm.focus,
|
||||
.xterm:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.xterm .xterm-helpers {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
/**
|
||||
* The z-index of the helpers must be higher than the canvases in order for
|
||||
* IMEs to appear on top.
|
||||
*/
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.xterm .xterm-helper-textarea {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
/* Move textarea out of the screen to the far left, so that the cursor is not visible */
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
left: -9999em;
|
||||
top: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
z-index: -5;
|
||||
/** Prevent wrapping so the IME appears against the textarea at the correct position */
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.xterm .composition-view {
|
||||
/* TODO: Composition position got messed up somewhere */
|
||||
background: #000;
|
||||
color: #FFF;
|
||||
display: none;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.xterm .composition-view.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.xterm .xterm-viewport {
|
||||
/* On OS X this is required in order for the scroll bar to appear fully opaque */
|
||||
background-color: #000;
|
||||
overflow-y: scroll;
|
||||
cursor: default;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.xterm .xterm-screen {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.xterm .xterm-screen canvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.xterm .xterm-scroll-area {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.xterm-char-measure-element {
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -9999em;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.xterm {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.xterm.enable-mouse-events {
|
||||
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.xterm.xterm-cursor-pointer,
|
||||
.xterm .xterm-cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.xterm.column-select.focus {
|
||||
/* Column selection mode */
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.xterm .xterm-accessibility,
|
||||
.xterm .xterm-message {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.xterm .live-region {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.xterm-dim {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.xterm-underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.xterm-strikethrough {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.xterm-screen .xterm-decoration-container .xterm-decoration {
|
||||
z-index: 6;
|
||||
position: absolute;
|
||||
}
|
File diff suppressed because one or more lines are too long
3
luci-lib-xterm/luasrc/view/xterm/embed.htm
Normal file
3
luci-lib-xterm/luasrc/view/xterm/embed.htm
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
<link rel="stylesheet" href="<%=resource%>/xterm/xterm.css<%# ?v=PKG_VERSION %>">
|
||||
<script src="<%=resource%>/xterm/xterm.js<%# ?v=PKG_VERSION %>"></script>
|
41
taskd/Makefile
Normal file
41
taskd/Makefile
Normal file
@ -0,0 +1,41 @@
|
||||
#
|
||||
# Copyright (C) 2022 jjm2473 <jjm2473@gmail.com>
|
||||
#
|
||||
# This is free software, licensed under the MIT License.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
|
||||
PKG_NAME:=taskd
|
||||
PKG_VERSION:=1.0.3
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=jjm2473 <jjm2473@gmail.com>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
TITLE:=Simple Task Manager
|
||||
DEPENDS:=+procd +script-utils +coreutils-stty
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
Simple Task Manager based on procd
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/etc/init.d $(1)/usr/libexec
|
||||
$(INSTALL_BIN) ./files/tasks.init $(1)/etc/init.d/tasks
|
||||
$(INSTALL_BIN) ./files/taskd.sh $(1)/usr/libexec/taskd
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
16
taskd/files/taskd.sh
Executable file
16
taskd/files/taskd.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
TASK_ID="$1"
|
||||
TASK_CMD="$2"
|
||||
|
||||
exec </dev/null >>"/var/log/tasks/$TASK_ID.log" 2>&1
|
||||
|
||||
export HOME=/root
|
||||
export TERM=xterm-256color
|
||||
|
||||
exec script -efqc 'onexit() {
|
||||
/etc/init.d/tasks _task_onstop "'"$TASK_ID"'" "$?"
|
||||
}
|
||||
trap onexit EXIT;
|
||||
stty cols 80 rows 24;
|
||||
'"$TASK_CMD" /dev/null
|
157
taskd/files/tasks.init
Executable file
157
taskd/files/tasks.init
Executable file
@ -0,0 +1,157 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (C) 2022 jjm2473@gmail.com
|
||||
|
||||
USE_PROCD=1
|
||||
START=49
|
||||
|
||||
extra_command "task_add" "<task_id> <task_cmd> [<time_wait>] Add and run a task, time_wait is wait time before auto delete stopped task, in seconds, -1 means forever"
|
||||
extra_command "task_del" "<task_id> Stop and delete task"
|
||||
extra_command "task_status" "[<task_id>] Dump task status, dump all tasks if no task_id specified"
|
||||
extra_command "task_gc" "Auto delete exipred (stopped and after timw_wait) tasks"
|
||||
extra_command "_task_onstop" "<task_id> Update stop time, for internal usage"
|
||||
|
||||
_task_add() {
|
||||
local task_id="${1}"
|
||||
local task_cmd="${2}"
|
||||
local time_wait="${3}"
|
||||
> "/var/log/tasks/$task_id.log"
|
||||
procd_open_instance "$task_id"
|
||||
procd_set_param data start=`date +'%s'` time_wait="$time_wait"
|
||||
procd_set_param command sh -c "exec /usr/libexec/taskd '$task_id' \"\$0\"" "$task_cmd"
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
task_add() {
|
||||
local task_id="${1}"
|
||||
local task_cmd="${2}"
|
||||
local time_wait="${3}"
|
||||
[ -z "$task_id" -o -z "$task_cmd" ] && return 127
|
||||
|
||||
if service_running "$task_id"; then
|
||||
echo "already running" >&2
|
||||
return 1
|
||||
fi
|
||||
if ! mkdir -p /var/log/tasks; then
|
||||
echo "create /var/log/tasks failed!" >&2
|
||||
return 1
|
||||
fi
|
||||
rc_procd _task_add "$task_id" "$task_cmd" "$time_wait"
|
||||
return 0
|
||||
}
|
||||
|
||||
_task_del() {
|
||||
local service="${1}"
|
||||
local task_id="${2}"
|
||||
procd_kill "$service" "$task_id"
|
||||
> "/var/log/tasks/$task_id.log"
|
||||
rm -f "/var/log/tasks/$task_id.log"
|
||||
}
|
||||
|
||||
task_del() {
|
||||
local task_id="${1}"
|
||||
[ -z "$task_id" ] && return 127
|
||||
procd_lock
|
||||
_task_del "$(basename ${basescript:-$initscript})" "$task_id"
|
||||
if [ "$(_task_status "$task_id" | jsonfilter -e '$.running' 2>/dev/null)" = "true" ]; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
_task_status() {
|
||||
local service="$(basename ${basescript:-$initscript})"
|
||||
local instance="$1"
|
||||
local data
|
||||
|
||||
json_init
|
||||
json_add_string name "$service"
|
||||
|
||||
data=$(_procd_ubus_call list | jsonfilter -e '@["'"$service"'"]')
|
||||
[ -z "$data" ] && return 1
|
||||
|
||||
data=$(echo "$data" | jsonfilter -e '$.instances')
|
||||
if [ -z "$data" ]; then
|
||||
if [ -z "$instance" ]; then
|
||||
echo "{}"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$instance" ]; then
|
||||
echo "$data"
|
||||
else
|
||||
instance="\"$instance\""
|
||||
echo "$data" | jsonfilter -e '$['"$instance"']'
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
task_status() {
|
||||
local task_id="${1}"
|
||||
_task_status "$task_id"
|
||||
}
|
||||
|
||||
task_gc() {
|
||||
local service="$(basename ${basescript:-$initscript})"
|
||||
local task_id instance time_wait
|
||||
local data
|
||||
|
||||
json_init
|
||||
[ -n "$service" ] && json_add_string name "$service"
|
||||
|
||||
data=$(_procd_ubus_call list | jsonfilter -e '@["'"$service"'"]')
|
||||
[ -z "$data" ] && return 1
|
||||
|
||||
data=$(echo "$data" | jsonfilter -e '$.instances')
|
||||
[ -z "$data" ] && return 1
|
||||
|
||||
procd_lock
|
||||
|
||||
ls /var/log/tasks/ | sed 's/.log$//g' | while read task_id; do
|
||||
instance=$(echo "$data" | jsonfilter -e '$["'"$task_id"'"]')
|
||||
[ "$(echo "$instance" | jsonfilter -e '$.running')" = "false" ] || continue
|
||||
time_wait=$(echo "$instance" | jsonfilter -e '$.data.time_wait')
|
||||
[ "$time_wait" = "-1" ] && continue
|
||||
[ $(($(date +'%s' -r "/var/log/tasks/$task_id.log") + ${time_wait:-0})) -lt `date +'%s'` ] && _task_del "$service" "$task_id"
|
||||
done
|
||||
}
|
||||
|
||||
_insert_exit() {
|
||||
local exit_code="$2"
|
||||
eval "`jshn -r "$1" | grep -v json_init`"
|
||||
json_select data || {
|
||||
_procd_set_param data stop=`date +'%s'` exit_code="$exit_code"
|
||||
return
|
||||
}
|
||||
json_add_string stop `date +'%s'`
|
||||
json_add_string exit_code "$exit_code"
|
||||
json_select ..
|
||||
}
|
||||
|
||||
_task_exit() {
|
||||
local task_id="$1"
|
||||
local exit_code="$2"
|
||||
local inst_json="$3"
|
||||
|
||||
_procd_call json_add_object "$task_id"
|
||||
_procd_call _insert_exit "$inst_json" "$exit_code"
|
||||
_procd_call json_close_object
|
||||
}
|
||||
|
||||
_task_onstop() {
|
||||
local task_id="${1}"
|
||||
local exit_code="${2}"
|
||||
[ -z "$task_id" ] && return 127
|
||||
local service="$(basename ${basescript:-$initscript})"
|
||||
|
||||
json_init
|
||||
json_add_string name "$service"
|
||||
data=$(_procd_ubus_call list | jsonfilter -e '@["'"$service"'"].instances["'"$task_id"'"]')
|
||||
[ -z "$data" ] && return 1
|
||||
json_cleanup
|
||||
|
||||
rc_procd _task_exit "$task_id" "$exit_code" "$data"
|
||||
}
|
Loading…
Reference in New Issue
Block a user